import { query, withTransaction } from '../../db/client.js'
import { ensureWorkerWalletWithClient, getWorkerWalletWithClient } from './shared.js'
import type {
  FinanceRequestListInput,
  WorkerFinanceRequestRow,
  WorkerWithdrawalAccountRow,
} from './types.js'
import type { PoolClient } from 'pg'

const WORKER_FINANCE_REQUEST_SELECT = `
  SELECT
    wfr.*,
    wu.username AS worker_username,
    wu.display_name AS worker_display_name,
    wu.phone AS worker_phone,
    wu.worker_type,
    wl.level_key AS worker_level_key,
    wl.name AS worker_level_name,
    withdraw_stats.withdraw_request_count AS worker_withdraw_request_count,
    withdraw_stats.total_withdraw_amount AS worker_total_withdraw_amount
  FROM worker_finance_requests wfr
  LEFT JOIN worker_users wu ON wu.id = wfr.worker_id
  LEFT JOIN worker_levels wl ON wl.id = wu.level_id
  LEFT JOIN LATERAL (
    SELECT
      COUNT(*)::int AS withdraw_request_count,
      COALESCE(SUM(amount) FILTER (WHERE status = 'approved'), 0)::bigint AS total_withdraw_amount
    FROM worker_finance_requests history_request
    WHERE history_request.worker_id = wfr.worker_id
      AND history_request.request_type = 'withdraw'
  ) withdraw_stats ON TRUE
`

export async function createWorkerFinanceRequest(input: {
  workerId: number
  requestType: string
  amount: number
  accountChannel: string
  accountName: string
  accountNo: string
  note: string
  payloadJson: string
  now: string
}): Promise<WorkerFinanceRequestRow | null> {
  const result = await query<WorkerFinanceRequestRow>(
    `
      INSERT INTO worker_finance_requests (
        worker_id, request_type, status, amount,
        account_channel, account_name, account_no,
        note, reviewed_note, payload_json, created_at, updated_at
      ) VALUES ($1, $2, 'pending', $3, $4, $5, $6, $7, '', $8::jsonb, $9, $10)
      RETURNING *
    `,
    [
      input.workerId,
      input.requestType,
      input.amount,
      input.accountChannel,
      input.accountName,
      input.accountNo,
      input.note,
      input.payloadJson,
      input.now,
      input.now,
    ],
  )
  return result.rows[0] || null
}

/**
 * 创建提现申请并在同一事务内立即冻结资金：
 * available_amount 划入 frozen_withdraw_amount，杜绝"待提现 + 接单押金"双重占用余额。
 */
export async function createWorkerWithdrawRequestWithFreeze(input: {
  workerId: number
  amount: number
  accountChannel: string
  accountName: string
  accountNo: string
  note: string
  payloadJson: string
  now: string
}): Promise<
  | {
      request: WorkerFinanceRequestRow | null
      failureReason: 'withdraw_insufficient'
    }
  | { request: WorkerFinanceRequestRow; failureReason: null }
> {
  return withTransaction(async (client) => {
    await ensureWorkerWalletWithClient(client, Number(input.workerId), input.now)
    const wallet = await getWorkerWalletWithClient(client, Number(input.workerId))
    const availableAmount = Number(wallet?.available_amount || 0)
    const frozenWithdrawAmount = Number(wallet?.frozen_withdraw_amount || 0)

    if (availableAmount < input.amount) {
      return { request: null, failureReason: 'withdraw_insufficient' }
    }

    const nextAvailable = availableAmount - input.amount
    const nextFrozenWithdraw = frozenWithdrawAmount + input.amount
    await client.query(
      `
        UPDATE worker_wallets
        SET available_amount = $1, frozen_withdraw_amount = $2, updated_at = $3
        WHERE worker_id = $4
      `,
      [nextAvailable, nextFrozenWithdraw, input.now, Number(input.workerId)],
    )
    await client.query(
      `
        INSERT INTO worker_wallet_ledgers (
          worker_id, ledger_type, amount, balance_after, frozen_after,
          note, payload_json, created_at
        ) VALUES ($1, 'withdraw_freeze', $2, $3, $4, $5, '{}'::jsonb, $6)
      `,
      [
        Number(input.workerId),
        -input.amount,
        nextAvailable,
        nextFrozenWithdraw,
        '提现申请冻结余额',
        input.now,
      ],
    )

    const createdResult = await client.query<WorkerFinanceRequestRow>(
      `
        INSERT INTO worker_finance_requests (
          worker_id, request_type, status, amount, frozen_amount,
          account_channel, account_name, account_no,
          note, reviewed_note, payload_json, created_at, updated_at
        ) VALUES ($1, 'withdraw', 'pending', $2, $2, $3, $4, $5, $6, '', $7::jsonb, $8, $8)
        RETURNING *
      `,
      [
        Number(input.workerId),
        input.amount,
        input.accountChannel,
        input.accountName,
        input.accountNo,
        input.note,
        input.payloadJson,
        input.now,
      ],
    )
    const request = createdResult.rows[0] || null
    if (!request) {
      throw new Error('提现申请创建失败')
    }
    return { request, failureReason: null }
  })
}

export async function getWorkerWithdrawalAccount(
  workerId: number | string,
  accountChannel: string,
): Promise<WorkerWithdrawalAccountRow | null> {
  const result = await query<WorkerWithdrawalAccountRow>(
    `
      SELECT *
      FROM worker_withdrawal_accounts
      WHERE worker_id = $1 AND account_channel = $2
      LIMIT 1
    `,
    [Number(workerId), accountChannel],
  )
  return result.rows[0] || null
}

export async function listWorkerWithdrawalAccounts(
  workerId: number | string,
): Promise<WorkerWithdrawalAccountRow[]> {
  const result = await query<WorkerWithdrawalAccountRow>(
    `
      SELECT *
      FROM worker_withdrawal_accounts
      WHERE worker_id = $1
      ORDER BY account_channel ASC
    `,
    [Number(workerId)],
  )
  return result.rows
}

export async function createWorkerWithdrawalAccount(input: {
  workerId: number
  accountChannel: string
  accountName: string
  accountNo: string
  alipayQrCodeJson: string
  wechatQrCodeJson: string
  now: string
}): Promise<WorkerWithdrawalAccountRow | null> {
  const result = await query<WorkerWithdrawalAccountRow>(
    `
      INSERT INTO worker_withdrawal_accounts (
        worker_id, account_channel, account_name, account_no,
        alipay_qr_code_json, wechat_qr_code_json, created_at
      ) VALUES ($1, $2, $3, $4, $5::jsonb, $6::jsonb, $7)
      ON CONFLICT (worker_id, account_channel) DO NOTHING
      RETURNING *
    `,
    [
      input.workerId,
      input.accountChannel,
      input.accountName,
      input.accountNo,
      input.alipayQrCodeJson,
      input.wechatQrCodeJson,
      input.now,
    ],
  )
  return result.rows[0] || null
}

export async function upsertWorkerWithdrawalAccount(input: {
  workerId: number
  accountChannel: string
  accountName: string
  accountNo: string
  alipayQrCodeJson: string
  wechatQrCodeJson: string
  now: string
}): Promise<WorkerWithdrawalAccountRow | null> {
  const result = await query<WorkerWithdrawalAccountRow>(
    `
      INSERT INTO worker_withdrawal_accounts (
        worker_id, account_channel, account_name, account_no,
        alipay_qr_code_json, wechat_qr_code_json, created_at
      ) VALUES ($1, $2, $3, $4, $5::jsonb, $6::jsonb, $7)
      ON CONFLICT (worker_id, account_channel) DO UPDATE
      SET
        account_name = EXCLUDED.account_name,
        account_no = EXCLUDED.account_no,
        alipay_qr_code_json = EXCLUDED.alipay_qr_code_json,
        wechat_qr_code_json = EXCLUDED.wechat_qr_code_json
      RETURNING *
    `,
    [
      input.workerId,
      input.accountChannel,
      input.accountName,
      input.accountNo,
      input.alipayQrCodeJson,
      input.wechatQrCodeJson,
      input.now,
    ],
  )
  return result.rows[0] || null
}

export async function getWorkerFinanceRequestById(
  requestId: number | string,
): Promise<WorkerFinanceRequestRow | null> {
  const result = await query<WorkerFinanceRequestRow>(
    `${WORKER_FINANCE_REQUEST_SELECT} WHERE wfr.id = $1 LIMIT 1`,
    [Number(requestId)],
  )
  return result.rows[0] || null
}

export async function countWorkerWithdrawRequestsOnDay(
  workerId: number | string,
  dayStartIso: string,
  dayEndIso: string,
): Promise<number> {
  const result = await query<{ total: number }>(
    `
      SELECT COUNT(*)::int AS total
      FROM worker_finance_requests
      WHERE worker_id = $1
        AND request_type = 'withdraw'
        AND status != 'cancelled'
        AND created_at >= $2
        AND created_at < $3
    `,
    [Number(workerId), dayStartIso, dayEndIso],
  )
  return Number(result.rows[0]?.total || 0)
}

export async function listWorkerFinanceRequests({
  page = 1,
  pageSize = 20,
  requestId = 0,
  workerId = 0,
  status = '',
  requestType = '',
  keyword = '',
  createdFrom = '',
  createdTo = '',
  accountChannel = '',
}: FinanceRequestListInput = {}): Promise<{ items: WorkerFinanceRequestRow[]; total: number }> {
  const { whereClause, params } = buildWorkerFinanceRequestWhere({
    requestId,
    workerId,
    status,
    requestType,
    keyword,
    createdFrom,
    createdTo,
    accountChannel,
  })
  const totalResult = await query<{ total: number }>(
    `SELECT COUNT(*)::int AS total FROM worker_finance_requests wfr
     LEFT JOIN worker_users wu ON wu.id = wfr.worker_id
     ${whereClause}`,
    params,
  )
  const offset = (page - 1) * pageSize
  params.push(pageSize, offset)
  const itemsResult = await query<WorkerFinanceRequestRow>(
    `${WORKER_FINANCE_REQUEST_SELECT}
     ${whereClause}
     ORDER BY wfr.created_at DESC, wfr.id DESC
     LIMIT $${params.length - 1} OFFSET $${params.length}`,
    params,
  )
  return {
    items: itemsResult.rows,
    total: Number(totalResult.rows[0]?.total || 0),
  }
}

/** 按当前列表筛选条件汇总各收款渠道的笔数与金额，供后台合计条展示。 */
export async function summarizeWorkerFinanceRequests(
  input: FinanceRequestListInput = {},
): Promise<Array<{ channel: string; count: number; amount: number }>> {
  const { whereClause, params } = buildWorkerFinanceRequestWhere({
    requestId: input.requestId || 0,
    workerId: input.workerId || 0,
    status: input.status || '',
    requestType: input.requestType || '',
    keyword: input.keyword || '',
    createdFrom: input.createdFrom || '',
    createdTo: input.createdTo || '',
    accountChannel: input.accountChannel || '',
  })
  const result = await query<{ channel: string; total: number; amount: string | number }>(
    `
      SELECT
        COALESCE(NULLIF(wfr.account_channel, ''), 'other') AS channel,
        COUNT(*)::int AS total,
        COALESCE(SUM(wfr.amount), 0)::bigint AS amount
      FROM worker_finance_requests wfr
      LEFT JOIN worker_users wu ON wu.id = wfr.worker_id
      ${whereClause}
      GROUP BY 1
    `,
    params,
  )
  return result.rows.map((row) => ({
    channel: String(row.channel || 'other'),
    count: Number(row.total || 0),
    amount: Number(row.amount || 0),
  }))
}

export async function reviewWorkerFinanceRequest(input: {
  requestId: number
  status: string
  reviewedNote: string
  now: string
  reviewedBy?: string
}): Promise<{
  request: WorkerFinanceRequestRow | null
  failureReason: 'request_not_pending' | 'withdraw_insufficient' | null
}> {
  return withTransaction(async (client) => {
    const currentResult = await client.query<WorkerFinanceRequestRow>(
      `
        SELECT *
        FROM worker_finance_requests
        WHERE id = $1
        LIMIT 1
        FOR UPDATE
      `,
      [input.requestId],
    )
    const current = currentResult.rows[0] || null
    if (!current) return { request: null, failureReason: null }

    if (current.status !== 'pending') {
      return {
        request: await getWorkerFinanceRequestByIdWithClient(client, input.requestId),
        failureReason: 'request_not_pending',
      }
    }

    if (input.status === 'approved') {
      await ensureWorkerWalletWithClient(client, Number(current.worker_id), input.now)
      const wallet = await getWorkerWalletWithClient(client, Number(current.worker_id))
      const availableAmount = Number(wallet?.available_amount || 0)
      const frozenAmount = Number(wallet?.frozen_deposit_amount || 0)
      const amount = Number(current.amount || 0)
      const note =
        input.reviewedNote ||
        (current.request_type === 'withdraw' ? '提现申请审核通过' : '充值申请审核通过')

      if (current.request_type === 'withdraw') {
        const plan = resolveWithdrawApprovalPlan({
          requestFrozenAmount: Number(current.frozen_amount || 0),
          frozenWithdrawAmount: Number(wallet?.frozen_withdraw_amount || 0),
          availableAmount,
          amount,
        })
        if (!plan.ok) {
          return {
            request: await getWorkerFinanceRequestByIdWithClient(client, input.requestId),
            failureReason: plan.reason,
          }
        }
        await client.query(
          `
            UPDATE worker_wallets
            SET available_amount = $1, frozen_withdraw_amount = $2, updated_at = $3
            WHERE worker_id = $4
          `,
          [
            plan.nextAvailableAmount,
            plan.nextFrozenWithdrawAmount,
            input.now,
            Number(current.worker_id),
          ],
        )
        await client.query(
          `
            INSERT INTO worker_wallet_ledgers (
              worker_id, ledger_type, amount, balance_after, frozen_after,
              audit_status, note, payload_json, created_at
            ) VALUES ($1, 'withdraw_paid', $2, $3, $4, 'approved', $5, $6::jsonb, $7)
          `,
          [
            Number(current.worker_id),
            plan.ledgerAmount,
            plan.nextAvailableAmount,
            plan.nextFrozenWithdrawAmount,
            plan.mode === 'frozen' ? `${note}（申请时已冻结，本次确认打款）` : note,
            JSON.stringify({
              requestId: Number(current.id),
              requestType: current.request_type,
              source: 'admin_finance_review',
            }),
            input.now,
          ],
        )
      } else {
        const nextAvailable = availableAmount + amount
        await client.query(
          `
            UPDATE worker_wallets
            SET
              available_amount = $1,
              total_credited_amount = total_credited_amount + $2,
              updated_at = $3
            WHERE worker_id = $4
          `,
          [nextAvailable, amount, input.now, Number(current.worker_id)],
        )
        await client.query(
          `
            INSERT INTO worker_wallet_ledgers (
              worker_id, ledger_type, amount, balance_after, frozen_after,
              audit_status, note, payload_json, created_at
            ) VALUES ($1, 'manual_credit', $2, $3, $4, 'approved', $5, $6::jsonb, $7)
          `,
          [
            Number(current.worker_id),
            amount,
            nextAvailable,
            frozenAmount,
            note,
            JSON.stringify({
              requestId: Number(current.id),
              requestType: current.request_type,
              source: 'admin_finance_review',
            }),
            input.now,
          ],
        )
      }
    } else if (
      input.status === 'rejected' &&
      current.request_type === 'withdraw' &&
      Number(current.frozen_amount || 0) > 0
    ) {
      // 新口径：拒绝提现申请时把申请冻结的资金退回可用余额。
      await ensureWorkerWalletWithClient(client, Number(current.worker_id), input.now)
      const wallet = await getWorkerWalletWithClient(client, Number(current.worker_id))
      const release = resolveWithdrawRejectionPlan({
        requestFrozenAmount: Number(current.frozen_amount || 0),
        frozenWithdrawAmount: Number(wallet?.frozen_withdraw_amount || 0),
        availableAmount: Number(wallet?.available_amount || 0),
      })
      if (!release.ok) {
        return {
          request: await getWorkerFinanceRequestByIdWithClient(client, input.requestId),
          failureReason: release.reason,
        }
      }
      await client.query(
        `
          UPDATE worker_wallets
          SET available_amount = $1, frozen_withdraw_amount = $2, updated_at = $3
          WHERE worker_id = $4
        `,
        [
          release.nextAvailableAmount,
          release.nextFrozenWithdrawAmount,
          input.now,
          Number(current.worker_id),
        ],
      )
      await client.query(
        `
          INSERT INTO worker_wallet_ledgers (
            worker_id, ledger_type, amount, balance_after, frozen_after,
            audit_status, note, payload_json, created_at
          ) VALUES ($1, 'withdraw_release', $2, $3, $4, 'approved', $5, $6::jsonb, $7)
        `,
        [
          Number(current.worker_id),
          release.releaseAmount,
          release.nextAvailableAmount,
          release.nextFrozenWithdrawAmount,
          `${input.reviewedNote || '提现申请审核未通过'}，冻结余额已退回`,
          JSON.stringify({
            requestId: Number(current.id),
            requestType: current.request_type,
            source: 'admin_finance_review',
          }),
          input.now,
        ],
      )
    }

    await client.query(
      `
        UPDATE worker_finance_requests
        SET
          status = $1,
          reviewed_note = $2,
          reviewed_at = $3,
          updated_at = $3,
          payload_json = payload_json || $5::jsonb
        WHERE id = $4
      `,
      [
        input.status,
        input.reviewedNote,
        input.now,
        input.requestId,
        JSON.stringify({
          review: {
            by: String(input.reviewedBy || '').trim(),
            at: input.now,
            status: input.status,
            note: input.reviewedNote,
          },
        }),
      ],
    )

    return {
      request: await getWorkerFinanceRequestByIdWithClient(client, input.requestId),
      failureReason: null,
    }
  })
}

export async function getWorkerFinanceRequestSummary(workerId: number | string) {
  const result = await query<{
    pending_withdraw_amount: number
    approved_withdraw_amount: number
    pending_recharge_amount: number
  }>(
    `
      SELECT
        COALESCE(
          SUM(CASE WHEN request_type = 'withdraw' AND status = 'pending' THEN amount ELSE 0 END),
          0
        )::int AS pending_withdraw_amount,
        COALESCE(
          SUM(CASE WHEN request_type = 'withdraw' AND status = 'approved' THEN amount ELSE 0 END),
          0
        )::int AS approved_withdraw_amount,
        COALESCE(
          SUM(CASE WHEN request_type = 'recharge' AND status = 'pending' THEN amount ELSE 0 END),
          0
        )::int AS pending_recharge_amount
      FROM worker_finance_requests
      WHERE worker_id = $1
    `,
    [Number(workerId)],
  )
  return {
    pendingWithdrawAmount: Number(result.rows[0]?.pending_withdraw_amount || 0),
    approvedWithdrawAmount: Number(result.rows[0]?.approved_withdraw_amount || 0),
    pendingRechargeAmount: Number(result.rows[0]?.pending_recharge_amount || 0),
  }
}

async function getWorkerFinanceRequestByIdWithClient(
  client: PoolClient,
  requestId: number | string,
): Promise<WorkerFinanceRequestRow | null> {
  const result = await client.query<WorkerFinanceRequestRow>(
    `${WORKER_FINANCE_REQUEST_SELECT} WHERE wfr.id = $1 LIMIT 1`,
    [Number(requestId)],
  )
  return result.rows[0] || null
}

function buildWorkerFinanceRequestWhere({
  requestId = 0,
  workerId = 0,
  status = '',
  requestType = '',
  keyword = '',
  createdFrom = '',
  createdTo = '',
  accountChannel = '',
}: FinanceRequestListInput) {
  const filters: string[] = []
  const params: unknown[] = []
  if (accountChannel) {
    params.push(accountChannel)
    filters.push(`wfr.account_channel = $${params.length}`)
  }
  if (createdFrom) {
    params.push(createdFrom)
    // 纯日期字符串 cast 后即当天 00:00:00；带时间则精确到秒
    filters.push(`wfr.created_at >= ($${params.length}::timestamp) AT TIME ZONE 'Asia/Shanghai'`)
  }
  if (createdTo) {
    params.push(createdTo)
    // 带时间精确到秒（含截止时刻）；纯日期保持含截止日全天
    filters.push(
      createdTo.length > 10
        ? `wfr.created_at <= ($${params.length}::timestamp) AT TIME ZONE 'Asia/Shanghai'`
        : `wfr.created_at < (($${params.length}::date + 1)::timestamp) AT TIME ZONE 'Asia/Shanghai'`,
    )
  }
  if (requestId) {
    params.push(requestId)
    filters.push(`wfr.id = $${params.length}`)
  }
  if (workerId) {
    params.push(workerId)
    filters.push(`wfr.worker_id = $${params.length}`)
  }
  if (status) {
    params.push(status)
    filters.push(`wfr.status = $${params.length}`)
  }
  if (requestType) {
    params.push(requestType)
    filters.push(`wfr.request_type = $${params.length}`)
  }
  if (keyword) {
    params.push(`%${keyword}%`)
    filters.push(
      `(wu.username ILIKE $${params.length}
        OR wu.display_name ILIKE $${params.length}
        OR wu.phone ILIKE $${params.length}
        OR wfr.account_name ILIKE $${params.length}
        OR wfr.account_no ILIKE $${params.length})`,
    )
  }
  return {
    whereClause: filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '',
    params,
  }
}

export type WithdrawApprovalPlan =
  | {
      ok: true
      mode: 'frozen' | 'legacy'
      nextAvailableAmount: number
      nextFrozenWithdrawAmount: number
      ledgerAmount: number
    }
  | { ok: false; reason: 'withdraw_insufficient' }

/**
 * 提现审核通过的资金划转计划。
 * - frozen 模式（新数据）：申请时已冻结，打款确认仅解冻，不动可用余额；
 * - legacy 模式（迁移前旧数据 frozen_amount=0）：沿用"确认时从可用余额扣款"口径。
 */
export function resolveWithdrawApprovalPlan(input: {
  requestFrozenAmount: number
  frozenWithdrawAmount: number
  availableAmount: number
  amount: number
}): WithdrawApprovalPlan {
  if (input.requestFrozenAmount > 0) {
    if (input.frozenWithdrawAmount < input.requestFrozenAmount) {
      return { ok: false, reason: 'withdraw_insufficient' }
    }
    return {
      ok: true,
      mode: 'frozen',
      nextAvailableAmount: input.availableAmount,
      nextFrozenWithdrawAmount: input.frozenWithdrawAmount - input.requestFrozenAmount,
      ledgerAmount: 0,
    }
  }
  if (input.availableAmount < input.amount) {
    return { ok: false, reason: 'withdraw_insufficient' }
  }
  return {
    ok: true,
    mode: 'legacy',
    nextAvailableAmount: input.availableAmount - input.amount,
    nextFrozenWithdrawAmount: input.frozenWithdrawAmount,
    ledgerAmount: -input.amount,
  }
}

/** 提现审核拒绝时的资金退回计划。 */
export function resolveWithdrawRejectionPlan(input: {
  requestFrozenAmount: number
  frozenWithdrawAmount: number
  availableAmount: number
}):
  | {
      ok: true
      releaseAmount: number
      nextAvailableAmount: number
      nextFrozenWithdrawAmount: number
    }
  | { ok: false; reason: 'withdraw_insufficient' } {
  if (input.frozenWithdrawAmount < input.requestFrozenAmount) {
    return { ok: false, reason: 'withdraw_insufficient' }
  }
  return {
    ok: true,
    releaseAmount: input.requestFrozenAmount,
    nextAvailableAmount: input.availableAmount + input.requestFrozenAmount,
    nextFrozenWithdrawAmount: input.frozenWithdrawAmount - input.requestFrozenAmount,
  }
}
