import { apiRequest } from "@/shared/api/request";
import { API_ENDPOINTS, API_OPERATIONS, apiEndpointPath } from "@/shared/api/generated/endpoints";
import type { ApiPage, EntityId, PageQuery } from "@/shared/api/types";

export interface HostAgencyPolicyLevelDto {
    id?: number;
    policyId?: number;
    level: number;
    // 后端保存的是达到该等级后的累计权益，列表里展示的最高工资也基于累计值。
    requiredDiamonds: number;
    hostSalaryUsd: string;
    hostCoinReward: number;
    agencySalaryUsd: string;
    totalSalaryUsd?: string;
    status: string;
    sortOrder?: number;
    createdAtMs?: number;
    updatedAtMs?: number;
}

export interface HostAgencyPolicyDto {
    id: number;
    appCode?: string;
    name: string;
    regionId: number;
    status: string;
    settlementMode: string;
    settlementTriggerMode: string;
    giftCoinToDiamondRatio: string;
    residualDiamondToUsdRate: string;
    effectiveFromMs?: number;
    effectiveToMs?: number;
    createdByAdminId?: number;
    updatedByAdminId?: number;
    publishStatus?: string;
    publishError?: string;
    publishedAtMs?: number;
    publishedByAdminId?: number;
    createdAtMs?: number;
    updatedAtMs?: number;
    levels: HostAgencyPolicyLevelDto[];
}

export interface HostAgencyPolicyPayload {
    name: string;
    // 第一阶段按单区域配置；同一区域同一生效时间只能启用一条政策。
    region_id: number;
    status: string;
    settlement_mode: string;
    settlement_trigger_mode: string;
    gift_coin_to_diamond_ratio: string;
    residual_diamond_to_usd_rate: string;
    effective_from_ms: number;
    effective_to_ms: number;
    levels: Array<{
        level: number;
        required_diamonds: number;
        host_salary_usd: string;
        host_coin_reward: number;
        agency_salary_usd: string;
        status: string;
        sort_order: number;
    }>;
}

type RawPolicy = HostAgencyPolicyDto & Record<string, unknown>;
type RawLevel = HostAgencyPolicyLevelDto & Record<string, unknown>;

export function listHostAgencyPolicies(query: PageQuery = {}): Promise<ApiPage<HostAgencyPolicyDto>> {
    const endpoint = API_ENDPOINTS.listHostAgencyPolicies;
    // 统一走 OpenAPI 生成的 operation path，避免手写路径和后端契约漂移。
    return apiRequest<ApiPage<RawPolicy>>(apiEndpointPath(API_OPERATIONS.listHostAgencyPolicies), {
        method: endpoint.method,
        query,
    }).then((page) => ({
        ...page,
        items: (page.items || []).map(normalizePolicy),
    }));
}

export function createHostAgencyPolicy(payload: HostAgencyPolicyPayload): Promise<HostAgencyPolicyDto> {
    const endpoint = API_ENDPOINTS.createHostAgencyPolicy;
    return apiRequest<RawPolicy, HostAgencyPolicyPayload>(apiEndpointPath(API_OPERATIONS.createHostAgencyPolicy), {
        body: payload,
        method: endpoint.method,
    }).then(normalizePolicy);
}

export function updateHostAgencyPolicy(
    policyId: EntityId,
    payload: HostAgencyPolicyPayload,
): Promise<HostAgencyPolicyDto> {
    const endpoint = API_ENDPOINTS.updateHostAgencyPolicy;
    return apiRequest<RawPolicy, HostAgencyPolicyPayload>(
        apiEndpointPath(API_OPERATIONS.updateHostAgencyPolicy, { policy_id: policyId }),
        {
            body: payload,
            method: endpoint.method,
        },
    ).then(normalizePolicy);
}

export function deleteHostAgencyPolicy(policyId: EntityId): Promise<unknown> {
    const endpoint = API_ENDPOINTS.deleteHostAgencyPolicy;
    return apiRequest(apiEndpointPath(API_OPERATIONS.deleteHostAgencyPolicy, { policy_id: policyId }), {
        method: endpoint.method,
    });
}

export function publishHostAgencyPolicy(policyId: EntityId): Promise<HostAgencyPolicyDto> {
    const endpoint = API_ENDPOINTS.publishHostAgencyPolicy;
    return apiRequest<RawPolicy>(apiEndpointPath(API_OPERATIONS.publishHostAgencyPolicy, { policy_id: policyId }), {
        method: endpoint.method,
    }).then(normalizePolicy);
}

export interface TeamSalaryPolicyLevelDto {
    id?: number;
    policyId?: number;
    level: number;
    thresholdUsd: string;
    ratePercent: string;
    salaryUsd: string;
    status: string;
    sortOrder?: number;
    createdAtMs?: number;
    updatedAtMs?: number;
}

export interface TeamSalaryPolicyDto {
    id: number;
    appCode?: string;
    policyType: "bd" | "admin";
    name: string;
    regionId: number;
    status: string;
    settlementTriggerMode: string;
    effectiveFromMs?: number;
    effectiveToMs?: number;
    createdByAdminId?: number;
    updatedByAdminId?: number;
    createdAtMs?: number;
    updatedAtMs?: number;
    levels: TeamSalaryPolicyLevelDto[];
}

export interface TeamSalaryPolicyPayload {
    policy_type: "bd" | "admin";
    name: string;
    region_id: number;
    status: string;
    settlement_trigger_mode: string;
    effective_from_ms: number;
    effective_to_ms: number;
    description?: string;
    levels: Array<{
        level: number;
        threshold_usd: string;
        rate_percent: string;
        salary_usd: string;
        status: string;
        sort_order: number;
    }>;
}

type RawTeamPolicy = TeamSalaryPolicyDto & Record<string, unknown>;
type RawTeamLevel = TeamSalaryPolicyLevelDto & Record<string, unknown>;

export function listTeamSalaryPolicies(query: PageQuery = {}): Promise<ApiPage<TeamSalaryPolicyDto>> {
    const endpoint = API_ENDPOINTS.listTeamSalaryPolicies;
    return apiRequest<ApiPage<RawTeamPolicy>>(apiEndpointPath(API_OPERATIONS.listTeamSalaryPolicies), {
        method: endpoint.method,
        query,
    }).then((page) => ({
        ...page,
        items: (page.items || []).map(normalizeTeamPolicy),
    }));
}

export function createTeamSalaryPolicy(payload: TeamSalaryPolicyPayload): Promise<TeamSalaryPolicyDto> {
    const endpoint = API_ENDPOINTS.createTeamSalaryPolicy;
    return apiRequest<RawTeamPolicy, TeamSalaryPolicyPayload>(apiEndpointPath(API_OPERATIONS.createTeamSalaryPolicy), {
        body: payload,
        method: endpoint.method,
    }).then(normalizeTeamPolicy);
}

export function updateTeamSalaryPolicy(
    policyId: EntityId,
    payload: TeamSalaryPolicyPayload,
): Promise<TeamSalaryPolicyDto> {
    const endpoint = API_ENDPOINTS.updateTeamSalaryPolicy;
    return apiRequest<RawTeamPolicy, TeamSalaryPolicyPayload>(
        apiEndpointPath(API_OPERATIONS.updateTeamSalaryPolicy, { policy_id: policyId }),
        {
            body: payload,
            method: endpoint.method,
        },
    ).then(normalizeTeamPolicy);
}

export function deleteTeamSalaryPolicy(policyType: string, policyId: EntityId): Promise<unknown> {
    const endpoint = API_ENDPOINTS.deleteTeamSalaryPolicy;
    return apiRequest(apiEndpointPath(API_OPERATIONS.deleteTeamSalaryPolicy, { policy_id: policyId }), {
        method: endpoint.method,
        query: { policy_type: policyType },
    });
}

export interface HostSalarySettlementUserDto {
    userId: string;
    displayUserId?: string;
    prettyDisplayUserId?: string;
    prettyId?: string;
    username?: string;
    avatar?: string;
}

export interface HostSalarySettlementDto {
    settlementId: string;
    commandId: string;
    transactionId: string;
    settlementType: string;
    triggerMode: string;
    settlementRole: string;
    userId: string;
    user: HostSalarySettlementUserDto;
    agencyOwnerUserId: string;
    agencyOwner: HostSalarySettlementUserDto;
    cycleKey: string;
    policyId: number;
    levelNo: number;
    totalDiamonds: number;
    hostSalaryUsdMinorDelta: number;
    hostSalaryUsdDelta: string;
    hostCoinRewardDelta: number;
    agencySalaryUsdMinorDelta: number;
    agencySalaryUsdDelta: string;
    residualUsdMinorDelta: number;
    residualUsdDelta: string;
    status: string;
    reason: string;
    createdAtMs: number;
}

export interface HostSalarySettlementPendingDto {
    userId: string;
    user: HostSalarySettlementUserDto;
    agencyOwnerUserId: string;
    agencyOwner: HostSalarySettlementUserDto;
    role: string;
    cycleKey: string;
    regionId: number;
    regionName?: string;
    policyId: number;
    policyName: string;
    settlementType: string;
    triggerMode: string;
    levelNo: number;
    requiredDiamonds: number;
    totalDiamonds: number;
    hostSalaryUsdMinorTarget: number;
    hostSalaryUsdTarget: string;
    hostSalaryUsdMinorDelta: number;
    hostSalaryUsdDelta: string;
    hostCoinRewardTarget: number;
    hostCoinRewardDelta: number;
    agencySalaryUsdMinorTarget: number;
    agencySalaryUsdTarget: string;
    agencySalaryUsdMinorDelta: number;
    agencySalaryUsdDelta: string;
    residualUsdMinorDelta: number;
    residualUsdDelta: string;
    totalUsdMinorDelta: number;
    totalUsdDelta: string;
    settledLevelNo: number;
    settledHostSalaryUsdMinor: number;
    settledHostSalaryUsd: string;
    settledHostCoinReward: number;
    settledAgencySalaryUsdMinor: number;
    settledAgencySalaryUsd: string;
    lastSettledTotalDiamonds: number;
    residualUsdMinor: number;
    residualUsd: string;
    monthEndClearedAtMs: number;
    status: string;
}

export interface HostSalarySettlePayload {
    role: "host" | "agency";
    settlement_type: "daily" | "half_month" | "month_end";
    trigger_mode: "manual";
    cycle_key: string;
    region_id?: number;
    country_code?: string;
    user_ids?: string[];
}

export interface HostSalarySettleResult {
    role: string;
    cycleKey: string;
    settlementType: string;
    triggerMode: string;
    requestedCount: number;
    processedCount: number;
    successCount: number;
    skippedCount: number;
    failureCount: number;
    records: HostSalarySettlementDto[];
}

type RawSettlement = HostSalarySettlementDto & Record<string, unknown>;
type RawHostPending = HostSalarySettlementPendingDto & Record<string, unknown>;
type RawHostSettleResult = HostSalarySettleResult & Record<string, unknown>;

export function listHostSalarySettlements(query: PageQuery = {}): Promise<ApiPage<HostSalarySettlementDto>> {
    const endpoint = API_ENDPOINTS.listHostSalarySettlements;
    return apiRequest<ApiPage<RawSettlement>>(apiEndpointPath(API_OPERATIONS.listHostSalarySettlements), {
        method: endpoint.method,
        query,
    }).then((page) => ({
        ...page,
        items: (page.items || []).map(normalizeSettlement),
    }));
}

export function listHostSalarySettlementPending(
    query: PageQuery = {},
): Promise<ApiPage<HostSalarySettlementPendingDto>> {
    return apiRequest<ApiPage<RawHostPending>>("/v1/admin/host-salary-settlements/pending", {
        method: "GET",
        query,
    }).then((page) => ({
        ...page,
        items: (page.items || []).map(normalizeHostPending),
    }));
}

export function settleHostSalary(payload: HostSalarySettlePayload): Promise<HostSalarySettleResult> {
    return apiRequest<RawHostSettleResult, HostSalarySettlePayload>("/v1/admin/host-salary-settlements/settle", {
        body: payload,
        method: "POST",
    }).then(normalizeHostSettleResult);
}

export interface TeamSalarySettlementPendingDto {
    policyType: "bd" | "admin";
    userId: string;
    user: HostSalarySettlementUserDto;
    cycleKey: string;
    regionId: number;
    regionName?: string;
    policyId: number;
    policyName: string;
    levelNo: number;
    incomeUsdMinor: number;
    incomeUsd: string;
    salaryUsdMinorTarget: number;
    salaryUsdTarget: string;
    salaryUsdMinorDelta: number;
    salaryUsdDelta: string;
    settledLevelNo: number;
    settledSalaryUsdMinor: number;
    settledSalaryUsd: string;
    lastSettledIncomeUsdMinor: number;
    lastSettledIncomeUsd: string;
    monthClosedAtMs: number;
}

export interface TeamSalarySettlementRecordDto {
    settlementId: string;
    commandId: string;
    transactionId: string;
    policyType: "bd" | "admin";
    triggerMode: string;
    userId: string;
    user: HostSalarySettlementUserDto;
    cycleKey: string;
    regionId: number;
    policyId: number;
    levelNo: number;
    incomeUsdMinor: number;
    incomeUsd: string;
    salaryUsdMinorDelta: number;
    salaryUsdDelta: string;
    status: string;
    reason: string;
    createdAtMs: number;
}

export interface TeamSalarySettlePayload {
    policy_type: "bd" | "admin";
    trigger_mode: "manual" | "automatic";
    cycle_key: string;
    region_id?: number;
    country_code?: string;
    user_ids?: string[];
}

export interface TeamSalarySettleResult {
    policyType: "bd" | "admin";
    cycleKey: string;
    requestedCount: number;
    processedCount: number;
    successCount: number;
    skippedCount: number;
    failureCount: number;
    records: TeamSalarySettlementRecordDto[];
}

type RawTeamPending = TeamSalarySettlementPendingDto & Record<string, unknown>;
type RawTeamRecord = TeamSalarySettlementRecordDto & Record<string, unknown>;
type RawTeamSettleResult = TeamSalarySettleResult & Record<string, unknown>;

export function listTeamSalarySettlementPending(
    query: PageQuery = {},
): Promise<ApiPage<TeamSalarySettlementPendingDto>> {
    // 新增结算接口尚未进入生成端点，先集中在本 feature API 封装手写路径，避免页面散落裸字符串。
    return apiRequest<ApiPage<RawTeamPending>>("/v1/admin/team-salary-settlements/pending", {
        method: "GET",
        query,
    }).then((page) => ({
        ...page,
        items: (page.items || []).map(normalizeTeamPending),
    }));
}

export function settleTeamSalary(payload: TeamSalarySettlePayload): Promise<TeamSalarySettleResult> {
    // 批量结算是写钱包动作；payload 保持后端 snake_case，减少序列化时的字段转换风险。
    return apiRequest<RawTeamSettleResult, TeamSalarySettlePayload>("/v1/admin/team-salary-settlements/settle", {
        body: payload,
        method: "POST",
    }).then(normalizeTeamSettleResult);
}

export function listTeamSalarySettlementRecords(
    query: PageQuery = {},
): Promise<ApiPage<TeamSalarySettlementRecordDto>> {
    // 记录查询与待结算分开，避免运营查历史记录时触发候选重算。
    return apiRequest<ApiPage<RawTeamRecord>>("/v1/admin/team-salary-settlements/records", {
        method: "GET",
        query,
    }).then((page) => ({
        ...page,
        items: (page.items || []).map(normalizeTeamRecord),
    }));
}

function normalizePolicy(item: RawPolicy): HostAgencyPolicyDto {
    // 响应兼容 snake_case/camelCase，是为了后端 DTO 和前端内部模型之间只有这一处转换。
    return {
        id: numberValue(item.id),
        appCode: stringValue(item.appCode ?? item.app_code),
        name: stringValue(item.name),
        regionId: numberValue(item.regionId ?? item.region_id),
        status: stringValue(item.status) || "disabled",
        settlementMode: stringValue(item.settlementMode ?? item.settlement_mode) || "daily",
        settlementTriggerMode: stringValue(item.settlementTriggerMode ?? item.settlement_trigger_mode) || "automatic",
        giftCoinToDiamondRatio: stringValue(item.giftCoinToDiamondRatio ?? item.gift_coin_to_diamond_ratio) || "1",
        residualDiamondToUsdRate:
            stringValue(item.residualDiamondToUsdRate ?? item.residual_diamond_to_usd_rate) || "0",
        effectiveFromMs: numberValue(item.effectiveFromMs ?? item.effective_from_ms),
        effectiveToMs: numberValue(item.effectiveToMs ?? item.effective_to_ms),
        createdByAdminId: numberValue(item.createdByAdminId ?? item.created_by_admin_id),
        updatedByAdminId: numberValue(item.updatedByAdminId ?? item.updated_by_admin_id),
        publishStatus: stringValue(item.publishStatus ?? item.publish_status) || "draft",
        publishError: stringValue(item.publishError ?? item.publish_error),
        publishedAtMs: numberValue(item.publishedAtMs ?? item.published_at_ms),
        publishedByAdminId: numberValue(item.publishedByAdminId ?? item.published_by_admin_id),
        createdAtMs: numberValue(item.createdAtMs ?? item.created_at_ms),
        updatedAtMs: numberValue(item.updatedAtMs ?? item.updated_at_ms),
        levels: arrayValue(item.levels).map(normalizeLevel),
    };
}

function normalizeSettlement(raw: RawSettlement): HostSalarySettlementDto {
    const user = asRecord(raw.user);
    const agencyOwner = asRecord(raw.agencyOwner ?? raw.agency_owner);
    return {
        settlementId: stringValue(raw.settlementId ?? raw.settlement_id),
        commandId: stringValue(raw.commandId ?? raw.command_id),
        transactionId: stringValue(raw.transactionId ?? raw.transaction_id),
        settlementType: stringValue(raw.settlementType ?? raw.settlement_type),
        triggerMode: stringValue(raw.triggerMode ?? raw.trigger_mode) || "automatic",
        settlementRole: stringValue(raw.settlementRole ?? raw.settlement_role) || "all",
        userId: stringValue(raw.userId ?? raw.user_id),
        user: normalizeSettlementUser(user),
        agencyOwnerUserId: stringValue(raw.agencyOwnerUserId ?? raw.agency_owner_user_id),
        agencyOwner: normalizeSettlementUser(agencyOwner),
        cycleKey: stringValue(raw.cycleKey ?? raw.cycle_key),
        policyId: numberValue(raw.policyId ?? raw.policy_id),
        levelNo: numberValue(raw.levelNo ?? raw.level_no),
        totalDiamonds: numberValue(raw.totalDiamonds ?? raw.total_diamonds),
        hostSalaryUsdMinorDelta: numberValue(raw.hostSalaryUsdMinorDelta ?? raw.host_salary_usd_minor_delta),
        hostSalaryUsdDelta: stringValue(raw.hostSalaryUsdDelta ?? raw.host_salary_usd_delta),
        hostCoinRewardDelta: numberValue(raw.hostCoinRewardDelta ?? raw.host_coin_reward_delta),
        agencySalaryUsdMinorDelta: numberValue(raw.agencySalaryUsdMinorDelta ?? raw.agency_salary_usd_minor_delta),
        agencySalaryUsdDelta: stringValue(raw.agencySalaryUsdDelta ?? raw.agency_salary_usd_delta),
        residualUsdMinorDelta: numberValue(raw.residualUsdMinorDelta ?? raw.residual_usd_minor_delta),
        residualUsdDelta: stringValue(raw.residualUsdDelta ?? raw.residual_usd_delta),
        status: stringValue(raw.status) || "succeeded",
        reason: stringValue(raw.reason),
        createdAtMs: numberValue(raw.createdAtMs ?? raw.created_at_ms),
    };
}

function normalizeHostPending(raw: RawHostPending): HostSalarySettlementPendingDto {
    const user = asRecord(raw.user);
    const agencyOwner = asRecord(raw.agencyOwner ?? raw.agency_owner);
    return {
        userId: stringValue(raw.userId ?? raw.user_id),
        user: normalizeSettlementUser(user),
        agencyOwnerUserId: stringValue(raw.agencyOwnerUserId ?? raw.agency_owner_user_id),
        agencyOwner: normalizeSettlementUser(agencyOwner),
        role: stringValue(raw.role ?? raw.settlementRole ?? raw.settlement_role) || "host",
        cycleKey: stringValue(raw.cycleKey ?? raw.cycle_key),
        regionId: numberValue(raw.regionId ?? raw.region_id),
        regionName: stringValue(raw.regionName ?? raw.region_name),
        policyId: numberValue(raw.policyId ?? raw.policy_id),
        policyName: stringValue(raw.policyName ?? raw.policy_name),
        settlementType: stringValue(raw.settlementType ?? raw.settlement_type) || "half_month",
        triggerMode: stringValue(raw.triggerMode ?? raw.trigger_mode) || "manual",
        levelNo: numberValue(raw.levelNo ?? raw.level_no),
        requiredDiamonds: numberValue(raw.requiredDiamonds ?? raw.required_diamonds),
        totalDiamonds: numberValue(raw.totalDiamonds ?? raw.total_diamonds),
        hostSalaryUsdMinorTarget: numberValue(raw.hostSalaryUsdMinorTarget ?? raw.host_salary_usd_minor_target),
        hostSalaryUsdTarget: stringValue(raw.hostSalaryUsdTarget ?? raw.host_salary_usd_target),
        hostSalaryUsdMinorDelta: numberValue(raw.hostSalaryUsdMinorDelta ?? raw.host_salary_usd_minor_delta),
        hostSalaryUsdDelta: stringValue(raw.hostSalaryUsdDelta ?? raw.host_salary_usd_delta),
        hostCoinRewardTarget: numberValue(raw.hostCoinRewardTarget ?? raw.host_coin_reward_target),
        hostCoinRewardDelta: numberValue(raw.hostCoinRewardDelta ?? raw.host_coin_reward_delta),
        agencySalaryUsdMinorTarget: numberValue(raw.agencySalaryUsdMinorTarget ?? raw.agency_salary_usd_minor_target),
        agencySalaryUsdTarget: stringValue(raw.agencySalaryUsdTarget ?? raw.agency_salary_usd_target),
        agencySalaryUsdMinorDelta: numberValue(raw.agencySalaryUsdMinorDelta ?? raw.agency_salary_usd_minor_delta),
        agencySalaryUsdDelta: stringValue(raw.agencySalaryUsdDelta ?? raw.agency_salary_usd_delta),
        residualUsdMinorDelta: numberValue(raw.residualUsdMinorDelta ?? raw.residual_usd_minor_delta),
        residualUsdDelta: stringValue(raw.residualUsdDelta ?? raw.residual_usd_delta),
        totalUsdMinorDelta: numberValue(raw.totalUsdMinorDelta ?? raw.total_usd_minor_delta),
        totalUsdDelta: stringValue(raw.totalUsdDelta ?? raw.total_usd_delta),
        settledLevelNo: numberValue(raw.settledLevelNo ?? raw.settled_level_no),
        settledHostSalaryUsdMinor: numberValue(raw.settledHostSalaryUsdMinor ?? raw.settled_host_salary_usd_minor),
        settledHostSalaryUsd: stringValue(raw.settledHostSalaryUsd ?? raw.settled_host_salary_usd),
        settledHostCoinReward: numberValue(raw.settledHostCoinReward ?? raw.settled_host_coin_reward),
        settledAgencySalaryUsdMinor: numberValue(
            raw.settledAgencySalaryUsdMinor ?? raw.settled_agency_salary_usd_minor,
        ),
        settledAgencySalaryUsd: stringValue(raw.settledAgencySalaryUsd ?? raw.settled_agency_salary_usd),
        lastSettledTotalDiamonds: numberValue(raw.lastSettledTotalDiamonds ?? raw.last_settled_total_diamonds),
        residualUsdMinor: numberValue(raw.residualUsdMinor ?? raw.residual_usd_minor),
        residualUsd: stringValue(raw.residualUsd ?? raw.residual_usd),
        monthEndClearedAtMs: numberValue(raw.monthEndClearedAtMs ?? raw.month_end_cleared_at_ms),
        status: stringValue(raw.status) || "pending",
    };
}

function normalizeSettlementUser(raw: Record<string, unknown>): HostSalarySettlementUserDto {
    return {
        userId: stringValue(raw.userId ?? raw.user_id),
        displayUserId: stringValue(raw.displayUserId ?? raw.display_user_id),
        prettyDisplayUserId: stringValue(raw.prettyDisplayUserId ?? raw.pretty_display_user_id),
        prettyId: stringValue(raw.prettyId ?? raw.pretty_id),
        username: stringValue(raw.username),
        avatar: stringValue(raw.avatar),
    };
}

function normalizeTeamPending(raw: RawTeamPending): TeamSalarySettlementPendingDto {
    const user = asRecord(raw.user);
    // 待结算 DTO 兼容 snake_case/camelCase，方便后端 Go JSON tag 与前端内部模型独立演进。
    return {
        policyType: (stringValue(raw.policyType ?? raw.policy_type) || "bd") as "bd" | "admin",
        userId: stringValue(raw.userId ?? raw.user_id),
        user: normalizeSettlementUser(user),
        cycleKey: stringValue(raw.cycleKey ?? raw.cycle_key),
        regionId: numberValue(raw.regionId ?? raw.region_id),
        regionName: stringValue(raw.regionName ?? raw.region_name),
        policyId: numberValue(raw.policyId ?? raw.policy_id),
        policyName: stringValue(raw.policyName ?? raw.policy_name),
        levelNo: numberValue(raw.levelNo ?? raw.level_no),
        incomeUsdMinor: numberValue(raw.incomeUsdMinor ?? raw.income_usd_minor),
        incomeUsd: stringValue(raw.incomeUsd ?? raw.income_usd),
        salaryUsdMinorTarget: numberValue(raw.salaryUsdMinorTarget ?? raw.salary_usd_minor_target),
        salaryUsdTarget: stringValue(raw.salaryUsdTarget ?? raw.salary_usd_target),
        salaryUsdMinorDelta: numberValue(raw.salaryUsdMinorDelta ?? raw.salary_usd_minor_delta),
        salaryUsdDelta: stringValue(raw.salaryUsdDelta ?? raw.salary_usd_delta),
        settledLevelNo: numberValue(raw.settledLevelNo ?? raw.settled_level_no),
        settledSalaryUsdMinor: numberValue(raw.settledSalaryUsdMinor ?? raw.settled_salary_usd_minor),
        settledSalaryUsd: stringValue(raw.settledSalaryUsd ?? raw.settled_salary_usd),
        lastSettledIncomeUsdMinor: numberValue(raw.lastSettledIncomeUsdMinor ?? raw.last_settled_income_usd_minor),
        lastSettledIncomeUsd: stringValue(raw.lastSettledIncomeUsd ?? raw.last_settled_income_usd),
        monthClosedAtMs: numberValue(raw.monthClosedAtMs ?? raw.month_closed_at_ms),
    };
}

function normalizeTeamRecord(raw: RawTeamRecord): TeamSalarySettlementRecordDto {
    const user = asRecord(raw.user);
    // 金额字段同时保留 minor 和格式化字符串；表格展示用字符串，后续导出/统计可用 minor。
    return {
        settlementId: stringValue(raw.settlementId ?? raw.settlement_id),
        commandId: stringValue(raw.commandId ?? raw.command_id),
        transactionId: stringValue(raw.transactionId ?? raw.transaction_id),
        policyType: (stringValue(raw.policyType ?? raw.policy_type) || "bd") as "bd" | "admin",
        triggerMode: stringValue(raw.triggerMode ?? raw.trigger_mode),
        userId: stringValue(raw.userId ?? raw.user_id),
        user: normalizeSettlementUser(user),
        cycleKey: stringValue(raw.cycleKey ?? raw.cycle_key),
        regionId: numberValue(raw.regionId ?? raw.region_id),
        policyId: numberValue(raw.policyId ?? raw.policy_id),
        levelNo: numberValue(raw.levelNo ?? raw.level_no),
        incomeUsdMinor: numberValue(raw.incomeUsdMinor ?? raw.income_usd_minor),
        incomeUsd: stringValue(raw.incomeUsd ?? raw.income_usd),
        salaryUsdMinorDelta: numberValue(raw.salaryUsdMinorDelta ?? raw.salary_usd_minor_delta),
        salaryUsdDelta: stringValue(raw.salaryUsdDelta ?? raw.salary_usd_delta),
        status: stringValue(raw.status) || "succeeded",
        reason: stringValue(raw.reason),
        createdAtMs: numberValue(raw.createdAtMs ?? raw.created_at_ms),
    };
}

function normalizeTeamSettleResult(raw: RawTeamSettleResult): TeamSalarySettleResult {
    // 批量结算结果保留 records，结算后需要立即展示或定位 settlement_id 时无需再次查列表。
    return {
        policyType: (stringValue(raw.policyType ?? raw.policy_type) || "bd") as "bd" | "admin",
        cycleKey: stringValue(raw.cycleKey ?? raw.cycle_key),
        requestedCount: numberValue(raw.requestedCount ?? raw.requested_count),
        processedCount: numberValue(raw.processedCount ?? raw.processed_count),
        successCount: numberValue(raw.successCount ?? raw.success_count),
        skippedCount: numberValue(raw.skippedCount ?? raw.skipped_count),
        failureCount: numberValue(raw.failureCount ?? raw.failure_count),
        records: arrayValue(raw.records).map((item) => normalizeTeamRecord(asRecord(item) as RawTeamRecord)),
    };
}

function normalizeHostSettleResult(raw: RawHostSettleResult): HostSalarySettleResult {
    return {
        role: stringValue(raw.role ?? raw.settlementRole ?? raw.settlement_role),
        cycleKey: stringValue(raw.cycleKey ?? raw.cycle_key),
        settlementType: stringValue(raw.settlementType ?? raw.settlement_type),
        triggerMode: stringValue(raw.triggerMode ?? raw.trigger_mode),
        requestedCount: numberValue(raw.requestedCount ?? raw.requested_count),
        processedCount: numberValue(raw.processedCount ?? raw.processed_count),
        successCount: numberValue(raw.successCount ?? raw.success_count),
        skippedCount: numberValue(raw.skippedCount ?? raw.skipped_count),
        failureCount: numberValue(raw.failureCount ?? raw.failure_count),
        records: arrayValue(raw.records).map((item) => normalizeSettlement(asRecord(item) as RawSettlement)),
    };
}

function normalizeTeamPolicy(item: RawTeamPolicy): TeamSalaryPolicyDto {
    return {
        id: numberValue(item.id),
        appCode: stringValue(item.appCode ?? item.app_code),
        policyType: (stringValue(item.policyType ?? item.policy_type) || "bd") as "bd" | "admin",
        name: stringValue(item.name),
        regionId: numberValue(item.regionId ?? item.region_id),
        status: stringValue(item.status) || "disabled",
        settlementTriggerMode: stringValue(item.settlementTriggerMode ?? item.settlement_trigger_mode) || "automatic",
        effectiveFromMs: numberValue(item.effectiveFromMs ?? item.effective_from_ms),
        effectiveToMs: numberValue(item.effectiveToMs ?? item.effective_to_ms),
        createdByAdminId: numberValue(item.createdByAdminId ?? item.created_by_admin_id),
        updatedByAdminId: numberValue(item.updatedByAdminId ?? item.updated_by_admin_id),
        createdAtMs: numberValue(item.createdAtMs ?? item.created_at_ms),
        updatedAtMs: numberValue(item.updatedAtMs ?? item.updated_at_ms),
        levels: arrayValue(item.levels).map(normalizeTeamLevel),
    };
}

function normalizeTeamLevel(raw: unknown): TeamSalaryPolicyLevelDto {
    const item = asRecord(raw) as RawTeamLevel;
    return {
        id: numberValue(item.id),
        policyId: numberValue(item.policyId ?? item.policy_id),
        level: numberValue(item.level),
        thresholdUsd: stringValue(item.thresholdUsd ?? item.threshold_usd),
        ratePercent: stringValue(item.ratePercent ?? item.rate_percent),
        salaryUsd: stringValue(item.salaryUsd ?? item.salary_usd),
        status: stringValue(item.status) || "active",
        sortOrder: numberValue(item.sortOrder ?? item.sort_order),
        createdAtMs: numberValue(item.createdAtMs ?? item.created_at_ms),
        updatedAtMs: numberValue(item.updatedAtMs ?? item.updated_at_ms),
    };
}

function normalizeLevel(raw: unknown): HostAgencyPolicyLevelDto {
    const item = asRecord(raw) as RawLevel;
    // 金额字段保持字符串，避免前端 Number 格式化吞掉小数位或引入精度问题。
    return {
        id: numberValue(item.id),
        policyId: numberValue(item.policyId ?? item.policy_id),
        level: numberValue(item.level),
        requiredDiamonds: numberValue(item.requiredDiamonds ?? item.required_diamonds),
        hostSalaryUsd: stringValue(item.hostSalaryUsd ?? item.host_salary_usd),
        hostCoinReward: numberValue(item.hostCoinReward ?? item.host_coin_reward),
        agencySalaryUsd: stringValue(item.agencySalaryUsd ?? item.agency_salary_usd),
        totalSalaryUsd: stringValue(item.totalSalaryUsd ?? item.total_salary_usd),
        status: stringValue(item.status) || "active",
        sortOrder: numberValue(item.sortOrder ?? item.sort_order),
        createdAtMs: numberValue(item.createdAtMs ?? item.created_at_ms),
        updatedAtMs: numberValue(item.updatedAtMs ?? item.updated_at_ms),
    };
}

function asRecord(value: unknown): Record<string, unknown> {
    return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : {};
}

function arrayValue(value: unknown): unknown[] {
    return Array.isArray(value) ? value : [];
}

function stringValue(value: unknown) {
    return typeof value === "string" ? value : value === undefined || value === null ? "" : String(value);
}

function numberValue(value: unknown) {
    // 列表展示允许缺省值兜底为 0；真正的必填和正数校验在表单 schema 与后端 service 完成。
    const number = Number(value || 0);
    return Number.isFinite(number) ? number : 0;
}
