-- TDD-04 数据库表结构初始化
-- 适用 PostgreSQL 16 + pg_partman（分区扩展可选）
-- 命名约定：小写下划线，主键 uuid，金额 numeric(20,4)，JSONB 存半结构化数据

CREATE EXTENSION IF NOT EXISTS pgcrypto;

--------------------------------------------------------------------------------
-- 0. 开发参考表（种族、境界、货币静态配置）
--------------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS race_templates (
    id              VARCHAR(32) PRIMARY KEY,
    name            VARCHAR(64) NOT NULL,
    category        VARCHAR(16) NOT NULL DEFAULT 'common', -- common / rare
    can_create      BOOLEAN NOT NULL DEFAULT true,
    description     TEXT,
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

--------------------------------------------------------------------------------
-- 1. 账号与角色
--------------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS players (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    nakama_user_id  UUID UNIQUE,
    platform        VARCHAR(32),
    device_id_hash  VARCHAR(128) UNIQUE,
    status          VARCHAR(16) NOT NULL DEFAULT 'active', -- active / banned / deleted
    created_at      TIMESTAMPTZ DEFAULT NOW(),
    updated_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS characters (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    player_id           UUID NOT NULL REFERENCES players(id) ON DELETE CASCADE,
    name                VARCHAR(64) UNIQUE NOT NULL,
    race_id             VARCHAR(32) NOT NULL,
    birth_race_id       VARCHAR(32) NOT NULL,
    birth_world_tier    SMALLINT NOT NULL DEFAULT 1,
    world_tier          SMALLINT NOT NULL DEFAULT 1,
    realm_tier          SMALLINT NOT NULL DEFAULT 1,
    minor_realm         SMALLINT NOT NULL DEFAULT 1,
    realm_status        VARCHAR(16) NOT NULL DEFAULT 'normal', -- normal / tribulation_pending / breakthrough_ready
    level               INT NOT NULL DEFAULT 1,
    exp                 BIGINT NOT NULL DEFAULT 0,
    power               BIGINT NOT NULL DEFAULT 0,
    status              VARCHAR(16) NOT NULL DEFAULT 'active', -- active / dead / sealed / deleted
    base_stats          JSONB NOT NULL DEFAULT '{}',
    battle_stats        JSONB NOT NULL DEFAULT '{}',
    san_current         SMALLINT NOT NULL DEFAULT 100,
    san_max             SMALLINT NOT NULL DEFAULT 100,
    crime_score         INT NOT NULL DEFAULT 0,
    heavenly_value      INT NOT NULL DEFAULT 0,
    karma_value         INT NOT NULL DEFAULT 0,
    reputation_score    INT NOT NULL DEFAULT 0,
    mercenary_score     INT NOT NULL DEFAULT 0,
    hunter_prestige     INT NOT NULL DEFAULT 0,
    last_online_at      TIMESTAMPTZ,
    daily_reset_at      TIMESTAMPTZ,
    created_at          TIMESTAMPTZ DEFAULT NOW(),
    updated_at          TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_characters_player ON characters(player_id);
CREATE INDEX idx_characters_realm_rank ON characters(world_tier, realm_tier, power DESC);
CREATE INDEX idx_characters_realm_minor ON characters(realm_tier, minor_realm, power DESC);
CREATE INDEX idx_characters_last_online ON characters(last_online_at);
CREATE INDEX idx_characters_status ON characters(status);

CREATE TABLE IF NOT EXISTS character_race_states (
    character_id            UUID PRIMARY KEY REFERENCES characters(id) ON DELETE CASCADE,
    main_race_id            VARCHAR(32) NOT NULL,
    sub_branch_id           VARCHAR(32),
    bloodline_data          JSONB NOT NULL DEFAULT '{}',
    is_rare_race            BOOLEAN NOT NULL DEFAULT false,
    rebirth_count           SMALLINT NOT NULL DEFAULT 0,
    conversion_cooldown_until TIMESTAMPTZ,
    race_talents            JSONB NOT NULL DEFAULT '{}',
    hidden_talents          JSONB NOT NULL DEFAULT '{}',
    updated_at              TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS race_currency_wallets (
    character_id        UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    race_currency_code  VARCHAR(32) NOT NULL,
    amount              NUMERIC(20,4) NOT NULL DEFAULT 0,
    total_earned        NUMERIC(20,4) NOT NULL DEFAULT 0,
    updated_at          TIMESTAMPTZ DEFAULT NOW(),
    PRIMARY KEY (character_id, race_currency_code)
);

--------------------------------------------------------------------------------
-- 2. 境界
--------------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS realms (
    tier                    SMALLINT PRIMARY KEY,
    minor_realm_max         SMALLINT NOT NULL DEFAULT 3,
    name                    VARCHAR(64) NOT NULL,
    world_tier              SMALLINT NOT NULL,
    main_currency_code      VARCHAR(32) NOT NULL,
    is_tribulation_required BOOLEAN NOT NULL DEFAULT true,
    base_success_rate       NUMERIC(5,4) NOT NULL DEFAULT 0.5,
    attr_growth_template    JSONB NOT NULL DEFAULT '{}'
);

CREATE TABLE IF NOT EXISTS character_realms (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    character_id        UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    realm_tier          SMALLINT NOT NULL,
    max_minor_reached   SMALLINT NOT NULL DEFAULT 1,
    exp_in_tier         BIGINT NOT NULL DEFAULT 0,
    stats_snapshot      JSONB NOT NULL DEFAULT '{}',
    is_current          BOOLEAN NOT NULL DEFAULT false,
    updated_at          TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_character_realms_character ON character_realms(character_id);
CREATE INDEX idx_character_realms_current ON character_realms(character_id, is_current);

CREATE TABLE IF NOT EXISTS realm_breakthrough_records (
    id                      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    character_id            UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    from_realm_tier         SMALLINT NOT NULL,
    to_realm_tier           SMALLINT NOT NULL,
    from_minor_realm        SMALLINT NOT NULL,
    to_minor_realm          SMALLINT NOT NULL,
    is_success              BOOLEAN NOT NULL,
    is_break_world_barrier  BOOLEAN NOT NULL DEFAULT false,
    source_world_tier       SMALLINT NOT NULL,
    target_world_tier       SMALLINT NOT NULL,
    created_at              TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_realm_breakthrough_character ON realm_breakthrough_records(character_id);
CREATE INDEX idx_realm_breakthrough_created ON realm_breakthrough_records(created_at);

--------------------------------------------------------------------------------
-- 3. 功法与技能
--------------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS manuals (
    id                      VARCHAR(64) PRIMARY KEY,
    name                    VARCHAR(128) NOT NULL,
    category                VARCHAR(32) NOT NULL, -- universal / profession / racial / unique
    domain                  VARCHAR(32) NOT NULL,
    element                 VARCHAR(16) NOT NULL DEFAULT 'none',
    alignment               VARCHAR(16) NOT NULL DEFAULT 'neutral',
    max_layers              SMALLINT NOT NULL DEFAULT 1,
    required_race           VARCHAR(32)[],
    required_profession     VARCHAR(32)[],
    base_attr               JSONB NOT NULL DEFAULT '{}',
    skill_unlock_layers     JSONB NOT NULL DEFAULT '[]',
    version                 INT NOT NULL DEFAULT 1
);

CREATE TABLE IF NOT EXISTS character_manuals (
    id                      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    character_id            UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    manual_id               VARCHAR(64) NOT NULL REFERENCES manuals(id),
    instance_data           JSONB NOT NULL DEFAULT '{}',
    is_buffing              BOOLEAN NOT NULL DEFAULT false,
    buff_data               JSONB NOT NULL DEFAULT '{}',
    source_tag              VARCHAR(32) NOT NULL DEFAULT 'ORIGINAL',
    can_copy_to_jade_slip   BOOLEAN NOT NULL DEFAULT false,
    copy_count_left         SMALLINT NOT NULL DEFAULT 0,
    created_at              TIMESTAMPTZ DEFAULT NOW(),
    updated_at              TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_character_manuals_character ON character_manuals(character_id);
CREATE INDEX idx_character_manuals_buffing ON character_manuals(character_id, is_buffing);
CREATE INDEX idx_character_manuals_manual ON character_manuals(manual_id);
CREATE INDEX idx_character_manuals_instance_gin ON character_manuals USING GIN (instance_data jsonb_path_ops);

CREATE TABLE IF NOT EXISTS skills (
    id                  VARCHAR(64) PRIMARY KEY,
    name                VARCHAR(128) NOT NULL,
    category            VARCHAR(32) NOT NULL,
    domain              VARCHAR(32) NOT NULL,
    archetype           VARCHAR(32) NOT NULL,
    element             VARCHAR(16) NOT NULL DEFAULT 'none',
    alignment           VARCHAR(16) NOT NULL DEFAULT 'neutral',
    damage_type         VARCHAR(16) NOT NULL DEFAULT 'physical',
    scaling_attr        VARCHAR(8),
    is_eldritch         BOOLEAN NOT NULL DEFAULT false,
    san_cost            SMALLINT NOT NULL DEFAULT 0,
    san_gate            JSONB NOT NULL DEFAULT '{}',
    san_scaling         NUMERIC(6,4) NOT NULL DEFAULT 0,
    requirements        JSONB NOT NULL DEFAULT '{}',
    continuous_cost     JSONB NOT NULL DEFAULT '{}',
    flow                VARCHAR(32) NOT NULL DEFAULT 'transcript_jade_ok',
    base_template       JSONB NOT NULL DEFAULT '{}',
    version             INT NOT NULL DEFAULT 1
);

CREATE TABLE IF NOT EXISTS character_skills (
    id                      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    character_id            UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    skill_id                VARCHAR(64) NOT NULL REFERENCES skills(id),
    custom_name             VARCHAR(128),
    instance_data           JSONB NOT NULL DEFAULT '{}',
    source_tag              VARCHAR(32) NOT NULL DEFAULT 'ORIGINAL',
    proficiency             INT NOT NULL DEFAULT 0,
    is_unique               BOOLEAN NOT NULL DEFAULT false,
    unique_holder_until     TIMESTAMPTZ,
    lineage                 JSONB NOT NULL DEFAULT '{}',
    created_at              TIMESTAMPTZ DEFAULT NOW(),
    updated_at              TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_character_skills_character ON character_skills(character_id);
CREATE INDEX idx_character_skills_unique ON character_skills(character_id, is_unique);
CREATE INDEX idx_character_skills_skill ON character_skills(skill_id);
CREATE INDEX idx_character_skills_instance_gin ON character_skills USING GIN (instance_data jsonb_path_ops);

--------------------------------------------------------------------------------
-- 4. 物品、装备与经济
--------------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS items (
    id                  VARCHAR(64) PRIMARY KEY,
    name                VARCHAR(128) NOT NULL,
    category            VARCHAR(32) NOT NULL,
    sub_category        VARCHAR(32),
    stackable           BOOLEAN NOT NULL DEFAULT false,
    max_stack           INT NOT NULL DEFAULT 1,
    bind_type           VARCHAR(16) NOT NULL DEFAULT 'none',
    world_tier          SMALLINT NOT NULL DEFAULT 1,
    base_attr_template  JSONB NOT NULL DEFAULT '{}',
    random_affix_pool   JSONB NOT NULL DEFAULT '{}',
    script_id           VARCHAR(64),
    version             INT NOT NULL DEFAULT 1
);

CREATE TABLE IF NOT EXISTS inventories (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    character_id        UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    item_id             VARCHAR(64) NOT NULL REFERENCES items(id),
    slot_type           VARCHAR(16) NOT NULL DEFAULT 'bag', -- bag / warehouse / mail / auction_escrow
    quantity            INT NOT NULL DEFAULT 1,
    instance_data       JSONB NOT NULL DEFAULT '{}',
    is_stolen           BOOLEAN NOT NULL DEFAULT false,
    blood_debt_mark     BOOLEAN NOT NULL DEFAULT false,
    can_trade           BOOLEAN NOT NULL DEFAULT true,
    acquired_at         TIMESTAMPTZ DEFAULT NOW(),
    created_at          TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_inventories_character_slot ON inventories(character_id, slot_type);
CREATE INDEX idx_inventories_item ON inventories(item_id);
CREATE INDEX idx_inventories_instance_gin ON inventories USING GIN (instance_data jsonb_path_ops);

CREATE TABLE IF NOT EXISTS equipments (
    character_id    UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    slot_name       VARCHAR(32) NOT NULL, -- weapon / head / body / hand / foot / accessory1 / accessory2 / life_item
    inventory_id    UUID REFERENCES inventories(id) ON DELETE SET NULL,
    updated_at      TIMESTAMPTZ DEFAULT NOW(),
    PRIMARY KEY (character_id, slot_name)
);

CREATE INDEX idx_equipments_inventory ON equipments(inventory_id);

CREATE TABLE IF NOT EXISTS currencies (
    code            VARCHAR(32) PRIMARY KEY,
    name            VARCHAR(64) NOT NULL,
    world_tier      SMALLINT NOT NULL DEFAULT 1,
    is_premium      BOOLEAN NOT NULL DEFAULT false,
    exchange_rules  JSONB NOT NULL DEFAULT '{}'
);

CREATE TABLE IF NOT EXISTS currency_balances (
    character_id    UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    currency_code   VARCHAR(32) NOT NULL REFERENCES currencies(code),
    amount          NUMERIC(20,4) NOT NULL DEFAULT 0,
    total_earned    NUMERIC(20,4) NOT NULL DEFAULT 0,
    total_spent     NUMERIC(20,4) NOT NULL DEFAULT 0,
    updated_at      TIMESTAMPTZ DEFAULT NOW(),
    PRIMARY KEY (character_id, currency_code)
);

-- 经济审计日志（建议按 created_at 月分区，保留 12 个月）
CREATE TABLE IF NOT EXISTS economy_audit_logs (
    id              BIGSERIAL PRIMARY KEY,
    character_id    UUID REFERENCES characters(id) ON DELETE SET NULL,
    entity_type     VARCHAR(16) NOT NULL, -- character / guild / system
    entity_id       UUID NOT NULL,
    currency_code   VARCHAR(32) NOT NULL,
    flow_type       VARCHAR(16) NOT NULL, -- faucet / sink / transfer
    reason_code     VARCHAR(64) NOT NULL,
    amount          NUMERIC(20,4) NOT NULL,
    balance_after   NUMERIC(20,4),
    related_id      UUID,
    world_tier      SMALLINT NOT NULL,
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_economy_audit_character_reason ON economy_audit_logs(character_id, reason_code, created_at);
CREATE INDEX idx_economy_audit_entity ON economy_audit_logs(entity_id, created_at);
CREATE INDEX idx_economy_audit_currency ON economy_audit_logs(currency_code, created_at);
CREATE INDEX idx_economy_audit_flow ON economy_audit_logs(flow_type, created_at);
CREATE INDEX idx_economy_audit_world_tier ON economy_audit_logs(world_tier, created_at);
CREATE INDEX idx_economy_audit_created ON economy_audit_logs(created_at);
COMMENT ON TABLE economy_audit_logs IS '大表建议：按 created_at 月分区，保留 12 个月（可用 pg_partman）';

--------------------------------------------------------------------------------
-- 5. 组织（门派/帮派/家族/自建宗门）
--------------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS guilds (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name                VARCHAR(128) UNIQUE NOT NULL,
    org_type            VARCHAR(16) NOT NULL, -- system_sect / player_sect / guild / family
    world_tier          SMALLINT NOT NULL DEFAULT 1,
    region_id           UUID,
    zone_id             UUID,
    leader_id           UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    level               SMALLINT NOT NULL DEFAULT 1,
    reputation          INT NOT NULL DEFAULT 0,
    member_limit        INT NOT NULL DEFAULT 50,
    tax_rate            NUMERIC(5,4) NOT NULL DEFAULT 0.20,
    diplomacy_policy    JSONB NOT NULL DEFAULT '{}',
    building_levels     JSONB NOT NULL DEFAULT '{}',
    funds               JSONB NOT NULL DEFAULT '{}',
    status              VARCHAR(16) NOT NULL DEFAULT 'active', -- active / dissolved / blacklisted
    created_at          TIMESTAMPTZ DEFAULT NOW(),
    dissolved_at        TIMESTAMPTZ
);

CREATE INDEX idx_guilds_type_status ON guilds(org_type, status);
CREATE INDEX idx_guilds_world_tier ON guilds(world_tier, status);
CREATE INDEX idx_guilds_leader ON guilds(leader_id);

CREATE TABLE IF NOT EXISTS guild_members (
    guild_id        UUID NOT NULL REFERENCES guilds(id) ON DELETE CASCADE,
    character_id    UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    role            VARCHAR(32) NOT NULL DEFAULT 'member', -- leader / vice / elder / elder_council / member / disciple_manager
    joined_at       TIMESTAMPTZ DEFAULT NOW(),
    contribution    JSONB NOT NULL DEFAULT '{}',
    daily_quota     JSONB NOT NULL DEFAULT '{}',
    updated_at      TIMESTAMPTZ DEFAULT NOW(),
    PRIMARY KEY (guild_id, character_id)
);

CREATE INDEX idx_guild_members_character ON guild_members(character_id);

CREATE TABLE IF NOT EXISTS guild_territories (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    guild_id            UUID NOT NULL REFERENCES guilds(id) ON DELETE CASCADE,
    territory_type      VARCHAR(32) NOT NULL,
    level               SMALLINT NOT NULL DEFAULT 1,
    upgrade_progress    JSONB NOT NULL DEFAULT '{}',
    params              JSONB NOT NULL DEFAULT '{}',
    updated_at          TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_guild_territories_guild ON guild_territories(guild_id, territory_type);

CREATE TABLE IF NOT EXISTS guild_diplomacy (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    guild_a_id  UUID NOT NULL REFERENCES guilds(id) ON DELETE CASCADE,
    guild_b_id  UUID NOT NULL REFERENCES guilds(id) ON DELETE CASCADE,
    relation    VARCHAR(16) NOT NULL, -- allied / at_war / trade / betrayed_cooldown
    started_at  TIMESTAMPTZ DEFAULT NOW(),
    ended_at    TIMESTAMPTZ
);

CREATE INDEX idx_guild_diplomacy_a ON guild_diplomacy(guild_a_id, relation);
CREATE INDEX idx_guild_diplomacy_b ON guild_diplomacy(guild_b_id, relation);

CREATE TABLE IF NOT EXISTS guild_warehouses (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    guild_id        UUID NOT NULL REFERENCES guilds(id) ON DELETE CASCADE,
    inventory_id    UUID NOT NULL REFERENCES inventories(id) ON DELETE CASCADE,
    deposited_by    UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    deposit_type    VARCHAR(16) NOT NULL DEFAULT 'common', -- common / tribute / black_eat
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_guild_warehouses_guild ON guild_warehouses(guild_id);

--------------------------------------------------------------------------------
-- 6. 市场、拍卖与情报
--------------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS market_orders (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    seller_id           UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    seller_guild_id     UUID REFERENCES guilds(id) ON DELETE SET NULL,
    item_id             VARCHAR(64) NOT NULL REFERENCES items(id),
    inventory_id        UUID NOT NULL REFERENCES inventories(id) ON DELETE CASCADE,
    currency_code       VARCHAR(32) NOT NULL REFERENCES currencies(code),
    unit_price          NUMERIC(20,4) NOT NULL,
    quantity            INT NOT NULL,
    total_price         NUMERIC(20,4) NOT NULL,
    tax_rate            NUMERIC(5,4) NOT NULL DEFAULT 0.05,
    status              VARCHAR(16) NOT NULL DEFAULT 'active', -- active / sold / cancelled / expired
    world_tier          SMALLINT NOT NULL DEFAULT 1,
    listed_at           TIMESTAMPTZ DEFAULT NOW(),
    expired_at          TIMESTAMPTZ,
    sold_at             TIMESTAMPTZ,
    buyer_id            UUID REFERENCES characters(id) ON DELETE SET NULL
);

CREATE INDEX idx_market_orders_search ON market_orders(item_id, status, world_tier);
CREATE INDEX idx_market_orders_seller ON market_orders(seller_id, status);
CREATE INDEX idx_market_orders_listed ON market_orders(listed_at);
CREATE INDEX idx_market_orders_status ON market_orders(status, expired_at);
COMMENT ON TABLE market_orders IS '大表建议：按 listed_at 月分区，保留 6 个月';

CREATE TABLE IF NOT EXISTS auctions (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    seller_id           UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    guild_id            UUID REFERENCES guilds(id) ON DELETE SET NULL,
    auction_type        VARCHAR(16) NOT NULL, -- official / organization
    item_id             VARCHAR(64) NOT NULL REFERENCES items(id),
    inventory_id        UUID NOT NULL REFERENCES inventories(id) ON DELETE CASCADE,
    category            VARCHAR(32) NOT NULL, -- rare_bloodline / rare_manual / jade_slip / secret_material / artifact / material
    currency_code       VARCHAR(32) NOT NULL REFERENCES currencies(code),
    start_price         NUMERIC(20,4) NOT NULL,
    reserve_price       NUMERIC(20,4),
    min_increment_rate  NUMERIC(5,4) NOT NULL DEFAULT 0.05,
    deposit_rate        NUMERIC(5,4) NOT NULL DEFAULT 0.05,
    tax_rate            NUMERIC(5,4) NOT NULL DEFAULT 0.05,
    status              VARCHAR(16) NOT NULL DEFAULT 'preparing', -- preparing / active / extended / sold / expired / robbed
    started_at          TIMESTAMPTZ,
    ended_at            TIMESTAMPTZ,
    is_anonymous        BOOLEAN NOT NULL DEFAULT false,
    access_type         VARCHAR(16) NOT NULL DEFAULT 'public',
    risk_type           VARCHAR(16) NOT NULL DEFAULT 'none', -- rare_robbable / none
    created_at          TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_auctions_list ON auctions(status, auction_type, ended_at);
CREATE INDEX idx_auctions_seller ON auctions(seller_id);
CREATE INDEX idx_auctions_started ON auctions(started_at);
CREATE INDEX idx_auctions_guild ON auctions(guild_id, status);

CREATE TABLE IF NOT EXISTS auction_bids (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    auction_id          UUID NOT NULL REFERENCES auctions(id) ON DELETE CASCADE,
    bidder_id           UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    amount              NUMERIC(20,4) NOT NULL,
    deposit_paid        NUMERIC(20,4) NOT NULL DEFAULT 0,
    is_auto_bid         BOOLEAN NOT NULL DEFAULT false,
    auto_max_amount     NUMERIC(20,4),
    bid_at              TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_auction_bids_auction ON auction_bids(auction_id, bid_at);
CREATE INDEX idx_auction_bids_bidder ON auction_bids(bidder_id, bid_at);
COMMENT ON TABLE auction_bids IS '大表建议：与 auctions 对齐，按 bid_at 月分区，保留 6 个月';

CREATE TABLE IF NOT EXISTS auction_blacklist_records (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    guild_id            UUID NOT NULL REFERENCES guilds(id) ON DELETE CASCADE,
    auction_id          UUID NOT NULL REFERENCES auctions(id) ON DELETE CASCADE,
    is_exposed          BOOLEAN NOT NULL DEFAULT false,
    exposed_at          TIMESTAMPTZ,
    exposure_type       VARCHAR(16), -- report / arbitration / heaven_sense
    punishment_level    SMALLINT,
    reputation_delta    INT,
    created_at          TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_auction_blacklist_guild ON auction_blacklist_records(guild_id);

CREATE TABLE IF NOT EXISTS intelligence_orders (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    seller_id           UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    buyer_id            UUID REFERENCES characters(id) ON DELETE SET NULL,
    intel_type          VARCHAR(32) NOT NULL, -- resource / event / location / bounty_clue / instance_entry / unique_skill_clue
    target_id           UUID,
    target_world_tier   SMALLINT NOT NULL DEFAULT 1,
    content_summary     VARCHAR(256),
    detail_data         JSONB NOT NULL DEFAULT '{}',
    currency_code       VARCHAR(32) NOT NULL REFERENCES currencies(code),
    price               NUMERIC(20,4) NOT NULL,
    purchase_count      SMALLINT NOT NULL DEFAULT 0,
    max_purchase        SMALLINT NOT NULL DEFAULT 1,
    status              VARCHAR(16) NOT NULL DEFAULT 'active', -- active / sold_out / expired
    cooldown_until      TIMESTAMPTZ,
    created_at          TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_intelligence_type ON intelligence_orders(intel_type, status);
CREATE INDEX idx_intelligence_target_tier ON intelligence_orders(target_world_tier, status);
CREATE INDEX idx_intelligence_seller ON intelligence_orders(seller_id);
CREATE INDEX idx_intelligence_created ON intelligence_orders(created_at);

--------------------------------------------------------------------------------
-- 7. 弟子系统
--------------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS disciples (
    id                      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    owner_id                UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    guild_id                UUID REFERENCES guilds(id) ON DELETE SET NULL,
    name                    VARCHAR(64) NOT NULL,
    race_id                 VARCHAR(32) NOT NULL,
    quality                 VARCHAR(16) NOT NULL DEFAULT 'common', -- common / fine / excellent / perfect / immortal
    profession              VARCHAR(32),
    stats                   JSONB NOT NULL DEFAULT '{}',
    skills                  JSONB NOT NULL DEFAULT '{}',
    equipment               JSONB NOT NULL DEFAULT '{}',
    daily_task_quota_min    INT NOT NULL DEFAULT 120,
    death_rate_modifier     NUMERIC(5,4) NOT NULL DEFAULT 0,
    status                  VARCHAR(16) NOT NULL DEFAULT 'active', -- active / dead / dispatched / insurance_pending
    tombstone_data          JSONB NOT NULL DEFAULT '{}',
    created_at              TIMESTAMPTZ DEFAULT NOW(),
    died_at                 TIMESTAMPTZ
);

CREATE INDEX idx_disciples_owner ON disciples(owner_id, status);
CREATE INDEX idx_disciples_guild ON disciples(guild_id);

CREATE TABLE IF NOT EXISTS disciple_missions (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    disciple_id         UUID NOT NULL REFERENCES disciples(id) ON DELETE CASCADE,
    owner_id            UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    contract_id         UUID, -- FK deferred to contracts creation below
    mission_type        VARCHAR(16) NOT NULL, -- mercenary / gathering / sect_proxy
    difficulty          SMALLINT NOT NULL DEFAULT 1,
    success_rate        NUMERIC(5,4) NOT NULL DEFAULT 0,
    death_rate          NUMERIC(5,4) NOT NULL DEFAULT 0,
    insurance_item_id   UUID REFERENCES inventories(id) ON DELETE SET NULL,
    result              VARCHAR(16) NOT NULL DEFAULT 'pending', -- pending / success / fail / death
    rewards             JSONB NOT NULL DEFAULT '{}',
    started_at          TIMESTAMPTZ DEFAULT NOW(),
    ended_at            TIMESTAMPTZ
);

CREATE INDEX idx_disciple_missions_disciple ON disciple_missions(disciple_id, result);
CREATE INDEX idx_disciple_missions_owner ON disciple_missions(owner_id, started_at);

--------------------------------------------------------------------------------
-- 8. 悬赏、追杀令与佣兵委托
--------------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS contracts (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    contract_type       VARCHAR(32) NOT NULL, -- mercenary / limited_time / bounty
    publisher_id        UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    publisher_guild_id  UUID REFERENCES guilds(id) ON DELETE SET NULL,
    difficulty          SMALLINT NOT NULL DEFAULT 1,
    currency_code       VARCHAR(32) NOT NULL REFERENCES currencies(code),
    base_reward         NUMERIC(20,4) NOT NULL,
    max_participants    SMALLINT NOT NULL DEFAULT 1,
    status              VARCHAR(16) NOT NULL DEFAULT 'active', -- active / accepted / completed / failed / expired / cancelled
    valid_until         TIMESTAMPTZ NOT NULL,
    created_at          TIMESTAMPTZ DEFAULT NOW(),
    completed_at        TIMESTAMPTZ
);

CREATE INDEX idx_contracts_publisher ON contracts(publisher_id, contract_type, status);
CREATE INDEX idx_contracts_status_until ON contracts(status, valid_until);

CREATE TABLE IF NOT EXISTS bounties (
    contract_id         UUID PRIMARY KEY REFERENCES contracts(id) ON DELETE CASCADE,
    bounty_type         VARCHAR(16) NOT NULL, -- official_bounty / private_bounty / manhunt
    target_id           UUID REFERENCES characters(id) ON DELETE CASCADE,
    target_world_tier   SMALLINT NOT NULL DEFAULT 1,
    reward_amount       NUMERIC(20,4) NOT NULL,
    fee_amount          NUMERIC(20,4) NOT NULL DEFAULT 0,
    is_anonymous        BOOLEAN NOT NULL DEFAULT false,
    report_count_today  SMALLINT NOT NULL DEFAULT 0,
    trigger_probability NUMERIC(5,4) NOT NULL DEFAULT 0,
    clues               JSONB NOT NULL DEFAULT '{}',
    item_inventory_id   UUID REFERENCES inventories(id) ON DELETE SET NULL
);

CREATE INDEX idx_bounties_target ON bounties(target_id, bounty_type, target_world_tier);
CREATE INDEX idx_bounties_type ON bounties(bounty_type, target_world_tier);

CREATE TABLE IF NOT EXISTS contract_participants (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    contract_id         UUID NOT NULL REFERENCES contracts(id) ON DELETE CASCADE,
    character_id        UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    participant_type    VARCHAR(16) NOT NULL, -- hunter / disciple_proxy / party_member
    disciple_id         UUID REFERENCES disciples(id) ON DELETE SET NULL,
    status              VARCHAR(16) NOT NULL DEFAULT 'accepted', -- accepted / active / completed / failed
    accepted_at         TIMESTAMPTZ DEFAULT NOW(),
    completed_at        TIMESTAMPTZ
);

CREATE INDEX idx_contract_participants_contract ON contract_participants(contract_id, status);
CREATE INDEX idx_contract_participants_character ON contract_participants(character_id, status);

CREATE TABLE IF NOT EXISTS manhunt_records (
    bounty_id           UUID PRIMARY KEY REFERENCES bounties(contract_id) ON DELETE CASCADE,
    original_owner_id   UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    current_holder_id   UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    window_end_at       TIMESTAMPTZ NOT NULL,
    outcome             VARCHAR(16) NOT NULL DEFAULT 'pending', -- pending / returned / black_ate / failed
    hunter_choice       VARCHAR(16),
    compensation_paid   NUMERIC(20,4) DEFAULT 0,
    chase_right_until   TIMESTAMPTZ,
    created_at          TIMESTAMPTZ DEFAULT NOW()
);

--------------------------------------------------------------------------------
-- 9. 世界地图、区域、副本与遗迹
--------------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS world_layers (
    tier                SMALLINT PRIMARY KEY,
    name                VARCHAR(64) NOT NULL,
    display_layer       SMALLINT NOT NULL DEFAULT 0,
    realm_tier          SMALLINT NOT NULL DEFAULT 1,
    is_pvp_enabled      BOOLEAN NOT NULL DEFAULT false,
    cross_tier_rules    JSONB NOT NULL DEFAULT '{}'
);

CREATE TABLE IF NOT EXISTS maps (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    world_tier      SMALLINT NOT NULL REFERENCES world_layers(tier),
    name            VARCHAR(128) NOT NULL,
    map_type        VARCHAR(16) NOT NULL DEFAULT 'world', -- world / event / instance / ruin
    seed            BIGINT,
    layout_data     JSONB NOT NULL DEFAULT '{}',
    is_active       BOOLEAN NOT NULL DEFAULT true,
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_maps_world_tier ON maps(world_tier, map_type, is_active);

CREATE TABLE IF NOT EXISTS regions (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    map_id          UUID NOT NULL REFERENCES maps(id) ON DELETE CASCADE,
    world_tier      SMALLINT NOT NULL DEFAULT 1,
    name            VARCHAR(128) NOT NULL,
    theme           VARCHAR(32),
    is_safe_zone    BOOLEAN NOT NULL DEFAULT false,
    pvp_rules       JSONB NOT NULL DEFAULT '{}'
);

CREATE INDEX idx_regions_map ON regions(map_id, is_safe_zone);
CREATE INDEX idx_regions_world_tier ON regions(world_tier);

CREATE TABLE IF NOT EXISTS zones (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    region_id           UUID NOT NULL REFERENCES regions(id) ON DELETE CASCADE,
    zone_type           VARCHAR(16) NOT NULL, -- stronghold / wild / secret / dungeon / ruins
    name                VARCHAR(128) NOT NULL,
    terrain             VARCHAR(32),
    weather             VARCHAR(32),
    resource_density    SMALLINT NOT NULL DEFAULT 1,
    danger_level        SMALLINT NOT NULL DEFAULT 1,
    rarity              VARCHAR(16) NOT NULL DEFAULT 'normal',
    seed                BIGINT,
    generated_data      JSONB NOT NULL DEFAULT '{}',
    refreshed_at        TIMESTAMPTZ DEFAULT NOW(),
    expires_at          TIMESTAMPTZ
);

CREATE INDEX idx_zones_region ON zones(region_id, zone_type);
CREATE INDEX idx_zones_expires ON zones(expires_at);

CREATE TABLE IF NOT EXISTS zone_explorations (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    zone_id             UUID NOT NULL REFERENCES zones(id) ON DELETE CASCADE,
    character_id        UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    fog_state           JSONB NOT NULL DEFAULT '{}',
    exploration_count   INT NOT NULL DEFAULT 0,
    last_entered_at     TIMESTAMPTZ,
    UNIQUE (zone_id, character_id)
);

CREATE INDEX idx_zone_explorations_zone ON zone_explorations(zone_id);
CREATE INDEX idx_zone_explorations_character ON zone_explorations(character_id);

--------------------------------------------------------------------------------
-- 10. 战斗与战报
--------------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS battles (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    battle_type     VARCHAR(16) NOT NULL, -- expedition_pve / dungeon_pve / pvp / gvg / bounty / manhunt
    world_tier      SMALLINT NOT NULL DEFAULT 1,
    realm_tier      SMALLINT NOT NULL DEFAULT 1,
    game_timestamp  TIMESTAMPTZ DEFAULT NOW(),
    attacker_id     UUID REFERENCES characters(id) ON DELETE SET NULL,
    defender_id     UUID REFERENCES characters(id) ON DELETE SET NULL,
    party_a         UUID[] NOT NULL DEFAULT '{}',
    party_b         UUID[] NOT NULL DEFAULT '{}',
    status          VARCHAR(16) NOT NULL DEFAULT 'in_progress', -- in_progress / completed
    result_summary  JSONB NOT NULL DEFAULT '{}',
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_battles_attacker ON battles(attacker_id, created_at);
CREATE INDEX idx_battles_defender ON battles(defender_id, created_at);
CREATE INDEX idx_battles_type_status ON battles(battle_type, status);
CREATE INDEX idx_battles_realm ON battles(world_tier, realm_tier, created_at);

CREATE TABLE IF NOT EXISTS battle_logs (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    battle_id       UUID UNIQUE NOT NULL REFERENCES battles(id) ON DELETE CASCADE,
    report          JSONB NOT NULL DEFAULT '{}',
    special_events  JSONB NOT NULL DEFAULT '[]',
    drops           JSONB NOT NULL DEFAULT '{}',
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_battle_logs_created ON battle_logs(created_at);
CREATE INDEX idx_battle_logs_report_gin ON battle_logs USING GIN (report jsonb_path_ops);
COMMENT ON TABLE battle_logs IS '大表建议：按 created_at 周分区，保留 8 周（可用 pg_partman）';

CREATE TABLE IF NOT EXISTS player_kills (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    killer_id       UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    victim_id       UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    battle_id       UUID REFERENCES battles(id) ON DELETE SET NULL,
    world_tier      SMALLINT NOT NULL DEFAULT 1,
    is_malice       BOOLEAN NOT NULL DEFAULT false,
    karma_delta     INT NOT NULL DEFAULT 0,
    crime_delta     INT NOT NULL DEFAULT 0,
    loot_taken      JSONB NOT NULL DEFAULT '{}',
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_player_kills_killer ON player_kills(killer_id);
CREATE INDEX idx_player_kills_victim ON player_kills(victim_id);
CREATE INDEX idx_player_kills_world ON player_kills(world_tier, created_at);

--------------------------------------------------------------------------------
-- 11. 副本、遗迹（依赖 battles）
--------------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS instances (
    id                      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    zone_id                 UUID NOT NULL REFERENCES zones(id) ON DELETE CASCADE,
    instance_type           VARCHAR(16) NOT NULL, -- permanent / random / ruin / event
    theme                   VARCHAR(32),
    world_tier              SMALLINT NOT NULL DEFAULT 1,
    recommended_realm_tier  SMALLINT NOT NULL DEFAULT 1,
    generated_layout        JSONB NOT NULL DEFAULT '{}',
    affixes                 JSONB NOT NULL DEFAULT '[]',
    max_parties             SMALLINT NOT NULL DEFAULT 1,
    expires_at              TIMESTAMPTZ,
    created_at              TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_instances_world_tier ON instances(world_tier, instance_type, expires_at);
CREATE INDEX idx_instances_zone ON instances(zone_id);

CREATE TABLE IF NOT EXISTS instance_runs (
    id                      UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    instance_id             UUID NOT NULL REFERENCES instances(id) ON DELETE CASCADE,
    party_leader_id         UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    party_members           UUID[] NOT NULL DEFAULT '{}',
    difficulty_coefficient  NUMERIC(6,4) NOT NULL DEFAULT 1.0,
    status                  VARCHAR(16) NOT NULL DEFAULT 'in_progress', -- in_progress / completed / failed / abandoned
    battle_id               UUID REFERENCES battles(id) ON DELETE SET NULL,
    consumed_keys           SMALLINT NOT NULL DEFAULT 0,
    started_at              TIMESTAMPTZ DEFAULT NOW(),
    ended_at                TIMESTAMPTZ
);

CREATE INDEX idx_instance_runs_instance ON instance_runs(instance_id, status);
CREATE INDEX idx_instance_runs_leader ON instance_runs(party_leader_id, started_at);

CREATE TABLE IF NOT EXISTS instance_loot (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    run_id              UUID NOT NULL REFERENCES instance_runs(id) ON DELETE CASCADE,
    character_id        UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    item_id             VARCHAR(64) NOT NULL REFERENCES items(id),
    inventory_id        UUID REFERENCES inventories(id) ON DELETE SET NULL,
    currency_code       VARCHAR(32) REFERENCES currencies(code),
    currency_amount     NUMERIC(20,4) DEFAULT 0,
    is_resonance_copy   BOOLEAN NOT NULL DEFAULT false,
    completeness        NUMERIC(5,4) DEFAULT 1.0,
    created_at          TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_instance_loot_run ON instance_loot(run_id);
CREATE INDEX idx_instance_loot_character ON instance_loot(character_id);

CREATE TABLE IF NOT EXISTS world_ruin_records (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    source_character_id UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    source_world_tier   SMALLINT NOT NULL,
    region_id           UUID NOT NULL REFERENCES regions(id) ON DELETE CASCADE,
    zone_id             UUID NOT NULL REFERENCES zones(id) ON DELETE CASCADE,
    instance_id         UUID REFERENCES instances(id) ON DELETE SET NULL,
    richness_score      NUMERIC(8,2) NOT NULL DEFAULT 0,
    snapshot_data       JSONB NOT NULL DEFAULT '{}',
    total_extractable   NUMERIC(6,4) NOT NULL DEFAULT 0,
    extracted_ratio     NUMERIC(6,4) NOT NULL DEFAULT 0,
    status              VARCHAR(16) NOT NULL DEFAULT 'active',
    opened_at           TIMESTAMPTZ DEFAULT NOW(),
    expires_at          TIMESTAMPTZ
);

CREATE INDEX idx_world_ruin_source ON world_ruin_records(source_character_id);
CREATE INDEX idx_world_ruin_region ON world_ruin_records(region_id, status);
CREATE INDEX idx_world_ruin_expires ON world_ruin_records(status, expires_at);

--------------------------------------------------------------------------------
-- 12. 渡劫记录
--------------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS tribulation_records (
    id                          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    character_id                UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    realm_tier                  SMALLINT NOT NULL,
    minor_realm                 SMALLINT NOT NULL,
    tribulation_type            VARCHAR(16) NOT NULL, -- normal / heart_devil / qi_deviation / chaos / heti
    base_success_rate           NUMERIC(5,4) NOT NULL,
    modified_success_rate       NUMERIC(5,4) NOT NULL,
    helper_ids                  UUID[] NOT NULL DEFAULT '{}',
    result                      VARCHAR(16) NOT NULL, -- success / fail / backlash / death
    penalties                   JSONB NOT NULL DEFAULT '{}',
    drops_on_success            JSONB NOT NULL DEFAULT '[]',
    san_snapshot                SMALLINT NOT NULL DEFAULT 100,
    created_at                  TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_tribulation_character ON tribulation_records(character_id, created_at);
CREATE INDEX idx_tribulation_realm ON tribulation_records(realm_tier, created_at);

--------------------------------------------------------------------------------
-- 13. 社交关系
--------------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS social_relations (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    character_a_id  UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    character_b_id  UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    relation_type   VARCHAR(16) NOT NULL, -- friend / companion / master / apprentice / enemy
    intimacy        INT NOT NULL DEFAULT 0,
    status          VARCHAR(16) NOT NULL DEFAULT 'active', -- active / cooldown / dissolved
    cooldown_until  TIMESTAMPTZ,
    created_at      TIMESTAMPTZ DEFAULT NOW(),
    dissolved_at    TIMESTAMPTZ,
    UNIQUE (character_a_id, character_b_id, relation_type)
);

CREATE INDEX idx_social_relations_a ON social_relations(character_a_id, relation_type, status);
CREATE INDEX idx_social_relations_b ON social_relations(character_b_id, relation_type, status);

CREATE TABLE IF NOT EXISTS lovers (
    relation_id             UUID PRIMARY KEY REFERENCES social_relations(id) ON DELETE CASCADE,
    resonance               INT NOT NULL DEFAULT 0,
    shared_residence_id     UUID REFERENCES guild_territories(id) ON DELETE SET NULL,
    combined_skill_id       UUID REFERENCES character_skills(id) ON DELETE SET NULL,
    vow_path                VARCHAR(16),
    teleport_cooldown_until TIMESTAMPTZ,
    mutual_benefits         JSONB NOT NULL DEFAULT '{}',
    updated_at              TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS sworn_brothers (
    relation_id             UUID PRIMARY KEY REFERENCES social_relations(id) ON DELETE CASCADE,
    group_id                UUID UNIQUE NOT NULL DEFAULT gen_random_uuid(),
    member_order            SMALLINT NOT NULL DEFAULT 0,
    shared_mission_slots    JSONB NOT NULL DEFAULT '{}',
    group_buffs             JSONB NOT NULL DEFAULT '{}',
    flag_zone_id            UUID REFERENCES zones(id) ON DELETE SET NULL,
    updated_at              TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS reputation_records (
    id              BIGSERIAL PRIMARY KEY,
    character_id    UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    source_id       UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    source_type     VARCHAR(16) NOT NULL, -- trade / mercenary / master / party / report
    delta           INT NOT NULL DEFAULT 0,
    tag             VARCHAR(32),
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_reputation_character ON reputation_records(character_id, created_at);
CREATE INDEX idx_reputation_source ON reputation_records(source_id, created_at);
COMMENT ON TABLE reputation_records IS '大表建议：按 created_at 月分区，保留 6 个月';

--------------------------------------------------------------------------------
-- 14. 配置与审计
--------------------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS nacos_configs (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    config_key          VARCHAR(128) UNIQUE NOT NULL,
    namespace           VARCHAR(64) NOT NULL,
    value               JSONB NOT NULL DEFAULT '{}',
    version             INT NOT NULL DEFAULT 1,
    is_grayscale        BOOLEAN NOT NULL DEFAULT false,
    grayscale_ratio     NUMERIC(5,4) NOT NULL DEFAULT 0,
    effective_from      TIMESTAMPTZ,
    created_at          TIMESTAMPTZ DEFAULT NOW(),
    updated_at          TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_nacos_configs_namespace ON nacos_configs(namespace, config_key);

CREATE TABLE IF NOT EXISTS daily_reset_tracking (
    character_id            UUID NOT NULL REFERENCES characters(id) ON DELETE CASCADE,
    reset_date              DATE NOT NULL,
    report_count_used       SMALLINT NOT NULL DEFAULT 0,
    instance_count_used     SMALLINT NOT NULL DEFAULT 0,
    ruin_count_used         SMALLINT NOT NULL DEFAULT 0,
    double_cultivation_minutes SMALLINT NOT NULL DEFAULT 0,
    fishing_games           SMALLINT NOT NULL DEFAULT 0,
    updated_at              TIMESTAMPTZ DEFAULT NOW(),
    PRIMARY KEY (character_id, reset_date)
);

--------------------------------------------------------------------------------
-- 14. 延迟外键约束（解决建表顺序依赖）
--------------------------------------------------------------------------------
ALTER TABLE guilds ADD CONSTRAINT fk_guilds_region
    FOREIGN KEY (region_id) REFERENCES regions(id) ON DELETE SET NULL;
ALTER TABLE guilds ADD CONSTRAINT fk_guilds_zone
    FOREIGN KEY (zone_id) REFERENCES zones(id) ON DELETE SET NULL;

ALTER TABLE disciple_missions ADD CONSTRAINT fk_disciple_missions_contract
    FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE SET NULL;

--------------------------------------------------------------------------------
-- 15. 参考数据 seed
--------------------------------------------------------------------------------

INSERT INTO race_templates (id, name, category, can_create, description) VALUES
('human', '人族', 'common', false, '默认不可创建，可通过转化获得'),
('tiger_yao', '虎妖', 'common', true, '妖族分支'),
('wolf_yao', '狼妖', 'common', true, '妖族分支'),
('elf_light', '光精灵', 'common', true, '精灵族分支'),
('elf_dark', '暗精灵', 'common', true, '精灵族分支'),
('demon_blood', '魔血族', 'common', true, '魔族分支'),
('ghost_ethereal', '幽魂族', 'common', true, '鬼族分支'),
('deep_one', '深潜裔', 'rare', true, '稀有血统，多分支进化'),
('dragon_blood', '龙血族', 'rare', true, '稀有种族'),
('chaos_heritage', '混沌裔', 'rare', true, '稀有种族'),
('giant', '巨人族', 'rare', true, '稀有种族')
ON CONFLICT (id) DO NOTHING;

INSERT INTO realms (tier, minor_realm_max, name, world_tier, main_currency_code, is_tribulation_required, base_success_rate, attr_growth_template) VALUES
(1, 3, '炼气期', 1, 'copper', false, 1.0, '{"hp":10,"atk":2,"def":1}'),
(2, 3, '筑基期', 2, 'copper', true, 0.55, '{"hp":20,"atk":4,"def":2}'),
(3, 3, '金丹期', 3, 'spirit_stone_low', true, 0.50, '{"hp":35,"atk":7,"def":4}'),
(4, 3, '元婴期', 4, 'soul_crystal', true, 0.45, '{"hp":55,"atk":11,"def":6}'),
(5, 3, '化神期', 5, 'immortal_crystal', true, 0.40, '{"hp":80,"atk":16,"def":9}'),
(6, 3, '合体期', 6, 'chaos_crystal', true, 0.35, '{"hp":110,"atk":22,"def":12}')
ON CONFLICT (tier) DO NOTHING;

INSERT INTO world_layers (tier, name, display_layer, realm_tier, is_pvp_enabled, cross_tier_rules) VALUES
(1, '种族出生地', 0, 1, false, '{}'),
(2, '洪荒主陆', 1, 2, true, '{}'),
(3, '洪荒腹地', 2, 3, true, '{}'),
(4, '太古秘境', 3, 4, true, '{}'),
(5, '混沌之渊·化神域', 4, 5, true, '{}'),
(6, '混沌之渊·合体域', 5, 6, true, '{}')
ON CONFLICT (tier) DO NOTHING;

INSERT INTO currencies (code, name, world_tier, is_premium, exchange_rules) VALUES
('copper', '铜钱', 1, false, '{}'),
('silver', '银两', 2, false, '{}'),
('spirit_stone_low', '下品灵石', 3, false, '{}'),
('spirit_stone_mid', '中品灵石', 3, false, '{}'),
('soul_crystal', '魂晶', 4, false, '{}'),
('immortal_crystal', '仙晶', 5, false, '{}'),
('chaos_crystal', '混沌灵石', 6, false, '{}'),
('purple_gas', '鸿蒙紫气', 1, true, '{}')
ON CONFLICT (code) DO NOTHING;

--------------------------------------------------------------------------------
-- 自检提示（可选）：
-- psql -U postgres -d honghuang -f database/migrations/001_init_schema.up.sql
--------------------------------------------------------------------------------
