import { query, withTransaction } from '../../db/client.js'
import { ensureWorkerWalletWithClient, getWorkerWalletWithClient } from './shared.js'
import type { CreateWorkerInput, ListInput, WorkerUserRow } from './types.js'
import type { PoolClient } from 'pg'

const WORKER_USER_SELECT = `
  SELECT
    wu.*,
    wl.level_key,
    wl.name AS level_name,
    wl.permission_json AS level_permission_json,
    inv.username AS inviter_username,
    inv.display_name AS inviter_display_name,
    ww.available_amount,
    ww.frozen_deposit_amount,
    ww.pending_unfreeze_amount,
    ww.total_credited_amount,
    ww.total_settled_amount
  FROM worker_users wu
  LEFT JOIN worker_levels wl ON wl.id = wu.level_id
  LEFT JOIN worker_users inv ON inv.id = wu.inviter_id
  LEFT JOIN worker_wallets ww ON ww.worker_id = wu.id
`

export {
  createWorkerSessionRecord,
  getWorkerSessionBySessionId,
  listWorkerSessions,
  revokeAllWorkerSessions,
  revokeWorkerSession,
  touchWorkerSession,
  type CreateWorkerSessionInput,
  type CreateWorkerSessionResult,
} from './worker-session-repo.js'

export {
  countWorkerLevelUsages,
  deleteWorkerLevel,
  getWorkerLevelById,
  getWorkerLevelByKey,
  listWorkerLevels,
  upsertWorkerLevel,
} from './worker-level-repo.js'

export async function getWorkerUserByUsername(username: string): Promise<WorkerUserRow | null> {
  const result = await query<WorkerUserRow>(
    `${WORKER_USER_SELECT} WHERE wu.username = $1 LIMIT 1`,
    [
      String(username || '')
        .trim()
        .toLowerCase(),
    ],
  )
  return result.rows[0] || null
}

export async function getWorkerUserByPhone(phone: string): Promise<WorkerUserRow | null> {
  const result = await query<WorkerUserRow>(`${WORKER_USER_SELECT} WHERE wu.phone = $1 LIMIT 1`, [
    String(phone || '').trim(),
  ])
  return result.rows[0] || null
}

export async function getWorkerUserByDisplayName(
  displayName: string,
): Promise<WorkerUserRow | null> {
  const result = await query<WorkerUserRow>(
    `${WORKER_USER_SELECT} WHERE wu.display_name = $1 LIMIT 1`,
    [String(displayName || '').trim()],
  )
  return result.rows[0] || null
}

export async function getWorkerUserByInviteCode(inviteCode: string): Promise<WorkerUserRow | null> {
  const result = await query<WorkerUserRow>(
    `${WORKER_USER_SELECT} WHERE wu.invite_code = $1 LIMIT 1`,
    [
      String(inviteCode || '')
        .trim()
        .toUpperCase(),
    ],
  )
  return result.rows[0] || null
}

export async function getWorkerUserById(workerId: number | string): Promise<WorkerUserRow | null> {
  const result = await query<WorkerUserRow>(`${WORKER_USER_SELECT} WHERE wu.id = $1 LIMIT 1`, [
    Number(workerId),
  ])
  return result.rows[0] || null
}

export async function createWorkerUser(input: CreateWorkerInput): Promise<WorkerUserRow | null> {
  return withTransaction(async (client) => {
    const result = await client.query<{ id: number }>(
      `
        INSERT INTO worker_users (
          username, password_hash, display_name, phone, level_id, status,
          worker_type, inviter_id, invite_code,
          created_at, updated_at
        ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
        RETURNING id
      `,
      [
        input.username,
        input.passwordHash,
        input.displayName,
        input.phone,
        input.levelId,
        input.status,
        input.workerType || 'external',
        input.inviterId || null,
        input.inviteCode,
        input.now,
        input.now,
      ],
    )
    const workerId = Number(result.rows[0]?.id || 0)
    await ensureWorkerWalletWithClient(client, workerId, input.now)
    if (input.smsCodeId) {
      const consumed = await client.query(
        `
          UPDATE worker_sms_codes
          SET consumed_at = $2
          WHERE id = $1 AND consumed_at IS NULL
        `,
        [input.smsCodeId, input.now],
      )
      if (consumed.rowCount !== 1) {
        throw new Error('worker_sms_code_consume_conflict')
      }
    }
    return getWorkerUserByIdWithClient(client, workerId)
  })
}

export async function updateWorkerUser(
  workerId: number | string,
  patch: Partial<
    Pick<
      WorkerUserRow,
      | 'status'
      | 'worker_type'
      | 'review_note'
      | 'frozen_reason'
      | 'frozen_at'
      | 'level_id'
      | 'personal_deposit_free_amount'
      | 'session_version'
      | 'updated_at'
      | 'reviewed_at'
    >
  >,
): Promise<WorkerUserRow | null> {
  const current = await getWorkerUserById(workerId)
  if (!current) return null
  const next = { ...current, ...patch }

  await query(
    `
      UPDATE worker_users
      SET
        status = $1,
        worker_type = $2,
        review_note = $3,
        frozen_reason = $4,
        frozen_at = $5,
        level_id = $6,
        personal_deposit_free_amount = $7,
        session_version = $8,
        reviewed_at = $9,
        updated_at = $10
      WHERE id = $11
    `,
    [
      next.status,
      next.worker_type || 'external',
      next.review_note || '',
      next.frozen_reason || '',
      next.frozen_at || null,
      next.level_id || null,
      next.personal_deposit_free_amount ?? null,
      Number(next.session_version || 1),
      next.reviewed_at || null,
      next.updated_at,
      Number(workerId),
    ],
  )
  return getWorkerUserById(workerId)
}

export async function updateWorkerPassword(input: {
  workerId: number
  passwordHash: string
  sessionVersion: number
  now: string
}): Promise<WorkerUserRow | null> {
  const result = await query<{ id: number }>(
    `
      UPDATE worker_users
      SET
        password_hash = $1,
        session_version = $2,
        updated_at = $3
      WHERE id = $4
      RETURNING id
    `,
    [input.passwordHash, input.sessionVersion, input.now, input.workerId],
  )
  if (!result.rows[0]) {
    return null
  }
  return getWorkerUserById(input.workerId)
}

export async function incrementWorkerSessionVersion(
  workerId: number | string,
  now: string,
): Promise<WorkerUserRow | null> {
  const result = await query<{ id: number }>(
    `
      UPDATE worker_users
      SET session_version = session_version + 1, updated_at = $2
      WHERE id = $1
      RETURNING id
    `,
    [Number(workerId), now],
  )
  if (!result.rows[0]) {
    return null
  }
  return getWorkerUserById(workerId)
}

export async function listWorkerUsers({
  page = 1,
  pageSize = 20,
  status = '',
  keyword = '',
  workerType = '',
  workerId = 0,
  levelId = 0,
}: ListInput = {}): Promise<{ items: WorkerUserRow[]; total: number }> {
  const { whereClause, params } = buildWorkerUserWhere({
    status,
    keyword,
    workerType,
    workerId,
    levelId,
  })
  const totalResult = await query<{ total: number }>(
    `SELECT COUNT(*)::int AS total FROM worker_users wu ${whereClause}`,
    params,
  )

  const offset = (page - 1) * pageSize
  params.push(pageSize, offset)
  const itemsResult = await query<WorkerUserRow>(
    `${WORKER_USER_SELECT}
     ${whereClause}
     ORDER BY wu.id DESC
     LIMIT $${params.length - 1} OFFSET $${params.length}`,
    params,
  )
  return { items: itemsResult.rows, total: Number(totalResult.rows[0]?.total || 0) }
}

export {
  addWorkerWalletCredit,
  ensureWorkerWallet,
  getWorkerWallet,
  listWorkerWalletLedgers,
} from './worker-wallet-repo.js'

export {
  countWorkerWithdrawRequestsOnDay,
  createWorkerFinanceRequest,
  createWorkerWithdrawalAccount,
  getWorkerFinanceRequestById,
  getWorkerFinanceRequestSummary,
  getWorkerWithdrawalAccount,
  listWorkerFinanceRequests,
  listWorkerWithdrawalAccounts,
  reviewWorkerFinanceRequest,
  summarizeWorkerFinanceRequests,
  upsertWorkerWithdrawalAccount,
} from './worker-finance-repo.js'

export {
  countWorkerAcceptedOrders,
  listWorkerCompletionLeaderboard,
  maybeUpgradeWorkerLevelWithClient,
  shouldApplyAutomaticWorkerLevel,
} from './worker-ranking-repo.js'

async function getWorkerUserByIdWithClient(
  client: PoolClient,
  workerId: number | string,
): Promise<WorkerUserRow | null> {
  const result = await client.query<WorkerUserRow>(
    `${WORKER_USER_SELECT} WHERE wu.id = $1 LIMIT 1`,
    [Number(workerId)],
  )
  return result.rows[0] || null
}

function buildWorkerUserWhere({
  status = '',
  keyword = '',
  workerType = '',
  workerId = 0,
  levelId = 0,
}: Pick<ListInput, 'status' | 'keyword' | 'workerType' | 'workerId' | 'levelId'>) {
  const filters: string[] = []
  const params: unknown[] = []
  if (status) {
    params.push(status)
    filters.push(`wu.status = $${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})`,
    )
  }
  if (workerType) {
    params.push(workerType)
    filters.push(`wu.worker_type = $${params.length}`)
  }
  if (workerId) {
    params.push(workerId)
    filters.push(`wu.id = $${params.length}`)
  }
  if (levelId) {
    params.push(levelId)
    filters.push(`wu.level_id = $${params.length}`)
  }
  return {
    whereClause: filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '',
    params,
  }
}

/** 消耗一张改名卡并更新昵称；条件更新保证并发下不会超发。 */
export async function consumeWorkerRenameCard(input: {
  workerId: number
  displayName: string
  now: string
}): Promise<WorkerUserRow | null> {
  const result = await query<{ id: number }>(
    `
      UPDATE worker_users
      SET
        display_name = $1,
        rename_cards = rename_cards - 1,
        last_renamed_at = $2,
        updated_at = $2
      WHERE id = $3 AND rename_cards > 0
      RETURNING id
    `,
    [input.displayName, input.now, input.workerId],
  )
  if (!result.rows[0]) {
    return null
  }
  return getWorkerUserById(input.workerId)
}
