import { query, withTransaction } from '../../db/client.js'
import { toPositiveInteger } from './shared.js'
import {
  normalizeWorkOrderAcceptanceMode,
  normalizeWorkOrderGiftCooldownHours,
  normalizeWorkOrderGiftCooldownMinutes,
} from '../../domain/work-order-acceptance-mode.js'
import type {
  ProductRuleListInput,
  WorkOrderRow,
  WorkOrderShareRow,
  WorkProductRuleRow,
} from './types.js'
import { WORK_ORDER_SELECT } from './work-order-repo.js'

const WORK_PRODUCT_RULE_SELECT = `
  SELECT
    wpr.*,
    wc.name AS category_name
  FROM work_product_rules wpr
  LEFT JOIN work_categories wc ON wc.id = wpr.category_id
`

function buildWorkProductRuleWhere({
  enabled = null,
  keyword = '',
  categoryId = 0,
}: ProductRuleListInput) {
  const filters: string[] = []
  const params: unknown[] = []
  if (enabled !== null && enabled !== undefined) {
    params.push(Boolean(enabled))
    filters.push(`wpr.enabled = $${params.length}`)
  }
  if (keyword) {
    params.push(`%${keyword}%`)
    filters.push(`(
      wpr.rule_key ILIKE $${params.length}
      OR wpr.sku_code ILIKE $${params.length}
      OR wpr.product_name ILIKE $${params.length}
    )`)
  }
  if (categoryId) {
    params.push(categoryId)
    filters.push(`wpr.category_id = $${params.length}`)
  }
  return {
    whereClause: filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : '',
    params,
  }
}

export async function listWorkProductRules({
  enabled = null,
  keyword = '',
  categoryId = 0,
}: ProductRuleListInput = {}): Promise<WorkProductRuleRow[]> {
  const { whereClause, params } = buildWorkProductRuleWhere({ enabled, keyword, categoryId })
  const result = await query<WorkProductRuleRow>(
    `${WORK_PRODUCT_RULE_SELECT}
     ${whereClause}
     ORDER BY wpr.sort_order ASC, wpr.id DESC`,
    params,
  )
  return result.rows
}

export async function listWorkProductRulesPage({
  page = 1,
  pageSize = 20,
  enabled = null,
  keyword = '',
  categoryId = 0,
}: ProductRuleListInput): Promise<{ items: WorkProductRuleRow[]; total: number }> {
  const { whereClause, params } = buildWorkProductRuleWhere({ enabled, keyword, categoryId })
  const totalResult = await query<{ total: number }>(
    `
      SELECT COUNT(*)::int AS total
      FROM work_product_rules wpr
      ${whereClause}
    `,
    params,
  )
  const offset = (page - 1) * pageSize
  const listParams = [...params, pageSize, offset]
  const result = await query<WorkProductRuleRow>(
    `${WORK_PRODUCT_RULE_SELECT}
     ${whereClause}
     ORDER BY wpr.sort_order ASC, wpr.id DESC
     LIMIT $${listParams.length - 1} OFFSET $${listParams.length}`,
    listParams,
  )
  return { items: result.rows, total: Number(totalResult.rows[0]?.total || 0) }
}

export async function countWorkProductRulesByCategory(
  input: {
    enabled?: boolean | null
    keyword?: string
  } = {},
): Promise<Array<{ categoryId: number | null; total: number }>> {
  const { whereClause, params } = buildWorkProductRuleWhere(input)
  const result = await query<{ category_id: number | null; total: number }>(
    `
      SELECT wpr.category_id, COUNT(*)::int AS total
      FROM work_product_rules wpr
      ${whereClause}
      GROUP BY wpr.category_id
    `,
    params,
  )
  return result.rows.map((row) => ({
    categoryId: row.category_id ? Number(row.category_id) : null,
    total: Number(row.total || 0),
  }))
}

export async function getWorkProductRuleByKey(ruleKey: string): Promise<WorkProductRuleRow | null> {
  const result = await query<WorkProductRuleRow>(
    `${WORK_PRODUCT_RULE_SELECT} WHERE wpr.rule_key = $1 LIMIT 1`,
    [String(ruleKey || '').trim()],
  )
  return result.rows[0] || null
}

export async function getWorkProductRuleById(
  ruleId: number | string,
): Promise<WorkProductRuleRow | null> {
  const result = await query<WorkProductRuleRow>(
    `${WORK_PRODUCT_RULE_SELECT} WHERE wpr.id = $1 LIMIT 1`,
    [Number(ruleId)],
  )
  return result.rows[0] || null
}

/** 查询仍在接单大厅、且明确来源于指定模板的工单。 */
export async function listOpenWorkOrdersByProductRuleId(
  ruleId: number | string,
): Promise<WorkOrderRow[]> {
  const result = await query<WorkOrderRow>(
    `${WORK_ORDER_SELECT}
     WHERE wo.product_rule_id = $1
       AND wo.status = 'open'
     ORDER BY wo.id ASC`,
    [Number(ruleId)],
  )
  return result.rows
}

export type SyncOpenWorkOrderPriceResult = {
  updated: boolean
  reason: 'updated' | 'price_unchanged' | 'not_open' | 'assigned' | 'sharing_filled'
  workOrderId: number
  sharingPartial: boolean
  workerIds: number[]
}

/** 拼单改价时保留已加入份额的报酬，只按新价计算剩余份额。 */
export function resolveOpenSharingSyncReward(input: {
  totalQuantity: number
  joinedQuantity: number
  lockedShareReward: number
  nextUnitReward: number
}) {
  const totalQuantity = toPositiveInteger(input.totalQuantity, 1)
  const joinedQuantity = Math.min(totalQuantity, Math.max(0, Number(input.joinedQuantity || 0)))
  return (
    Math.max(0, Number(input.lockedShareReward || 0)) +
    (totalQuantity - joinedQuantity) * toPositiveInteger(input.nextUnitReward, 0)
  )
}

/**
 * 同步大厅工单的模板价格。
 * 已加入的拼单份额报酬是合同快照，只更新尚未加入的剩余份额价格。
 */
export async function syncOpenWorkOrderPriceFromRule(input: {
  workOrderId: number
  productRuleId: number
  rewardAmount: number
  sharingUnitReward?: number
  actorName: string
  now: string
}): Promise<SyncOpenWorkOrderPriceResult> {
  return withTransaction(async (client) => {
    const orderResult = await client.query<WorkOrderRow>(
      `SELECT *
       FROM work_orders
       WHERE id = $1 AND product_rule_id = $2
       FOR UPDATE`,
      [input.workOrderId, input.productRuleId],
    )
    const workOrder = orderResult.rows[0] || null
    if (!workOrder || workOrder.status !== 'open') {
      return {
        updated: false,
        reason: 'not_open',
        workOrderId: input.workOrderId,
        sharingPartial: false,
        workerIds: [],
      }
    }
    if (workOrder.assigned_worker_id) {
      return {
        updated: false,
        reason: 'assigned',
        workOrderId: input.workOrderId,
        sharingPartial: false,
        workerIds: [],
      }
    }

    const sharesResult = await client.query<WorkOrderShareRow>(
      `SELECT *
       FROM work_order_shares
       WHERE work_order_id = $1
       FOR UPDATE`,
      [workOrder.id],
    )
    const activeShares = sharesResult.rows.filter((share) => share.status !== 'cancelled')
    const joinedQuantity = activeShares.reduce(
      (sum, share) => sum + Math.max(0, Number(share.quantity || 0)),
      0,
    )
    const workerIds = [
      ...new Set(
        activeShares.map((share) => Number(share.worker_id)).filter((workerId) => workerId > 0),
      ),
    ]
    const sharingPartial = workOrder.sharing_enabled === true && joinedQuantity > 0
    let nextRewardAmount = toPositiveInteger(input.rewardAmount, 0)
    let nextSharingUnitReward = Number(workOrder.sharing_unit_reward || 0)

    if (workOrder.sharing_enabled === true) {
      const totalQuantity = toPositiveInteger(workOrder.sharing_total_quantity, 1)
      if (joinedQuantity >= totalQuantity) {
        return {
          updated: false,
          reason: 'sharing_filled',
          workOrderId: Number(workOrder.id),
          sharingPartial,
          workerIds,
        }
      }
      nextSharingUnitReward = toPositiveInteger(input.sharingUnitReward, 0)
      const lockedShareReward = activeShares.reduce(
        (sum, share) => sum + Math.max(0, Number(share.share_reward || 0)),
        0,
      )
      nextRewardAmount = resolveOpenSharingSyncReward({
        totalQuantity,
        joinedQuantity,
        lockedShareReward,
        nextUnitReward: nextSharingUnitReward,
      })
    }

    const unchanged =
      Number(workOrder.reward_amount || 0) === nextRewardAmount &&
      (!workOrder.sharing_enabled ||
        Number(workOrder.sharing_unit_reward || 0) === nextSharingUnitReward)
    if (unchanged) {
      return {
        updated: false,
        reason: 'price_unchanged',
        workOrderId: Number(workOrder.id),
        sharingPartial,
        workerIds,
      }
    }

    await client.query(
      `UPDATE work_orders
       SET reward_amount = $1,
           sharing_unit_reward = $2,
           updated_at = $3
       WHERE id = $4`,
      [nextRewardAmount, nextSharingUnitReward, input.now, workOrder.id],
    )
    await client.query(
      `INSERT INTO work_order_events (
         work_order_id, actor_type, actor_id, event_type, from_status, to_status, payload_json, created_at
       ) VALUES ($1, 'admin', $2, 'price_synced_from_rule', $3, $3, $4::jsonb, $5)`,
      [
        workOrder.id,
        input.actorName,
        workOrder.status,
        JSON.stringify({
          productRuleId: input.productRuleId,
          previousRewardAmount: Number(workOrder.reward_amount || 0),
          rewardAmount: nextRewardAmount,
          previousSharingUnitReward: Number(workOrder.sharing_unit_reward || 0),
          sharingUnitReward: nextSharingUnitReward,
          joinedQuantity,
          lockedShareReward: activeShares.reduce(
            (sum, share) => sum + Math.max(0, Number(share.share_reward || 0)),
            0,
          ),
        }),
        input.now,
      ],
    )
    return {
      updated: true,
      reason: 'updated',
      workOrderId: Number(workOrder.id),
      sharingPartial,
      workerIds,
    }
  })
}

export async function deleteWorkProductRule(
  ruleId: number | string,
): Promise<{ deleted: boolean }> {
  const result = await query<{ id: number }>(
    'DELETE FROM work_product_rules WHERE id = $1 RETURNING id',
    [Number(ruleId)],
  )
  return { deleted: Boolean(result.rows[0]) }
}

export async function upsertWorkProductRule(input: {
  ruleKey: string
  provider: string
  platform: string
  shopId: string
  skuCode: string
  productName: string
  matchType: string
  categoryId: number | null
  enabled: boolean
  autoCreate: boolean
  rewardAmount: number
  unitPriceFen?: number
  requiredDepositAmount: number
  depositThresholdAmount: number
  sharingEnabled?: boolean
  sharingTotalQuantity?: number
  sharingUnitReward?: number
  sharingAutoFromOrder?: boolean
  timeoutMinutes?: number
  timeoutPolicy?: string
  acceptanceMode?: string
  giftCooldownHours?: number
  giftCooldownMinutes?: number
  requirementJson: string
  matchJson?: string
  sortOrder: number
  now: string
}): Promise<WorkProductRuleRow | null> {
  const result = await query<WorkProductRuleRow>(
    `
      INSERT INTO work_product_rules (
        rule_key, provider, platform, shop_id, sku_code, product_name,
        match_type, category_id, enabled, auto_create, reward_amount,
        unit_price_fen,
        required_deposit_amount, deposit_threshold_amount,
        sharing_enabled, sharing_total_quantity, sharing_unit_reward, sharing_auto_from_order,
        timeout_minutes, timeout_policy,
        acceptance_mode, gift_cooldown_hours, gift_cooldown_minutes,
        requirement_json, match_json,
        sort_order, created_at, updated_at
      ) VALUES (
        $1, $2, $3, $4, $5, $6,
        $7, $8, $9, $10, $11,
        $12,
        $13, $14,
        $15, $16, $17, $18,
        $19, $20,
        $21, $22, $23,
        $24::jsonb, $25::jsonb,
        $26, $27, $28
      )
      ON CONFLICT (rule_key) DO UPDATE
      SET
        provider = EXCLUDED.provider,
        platform = EXCLUDED.platform,
        shop_id = EXCLUDED.shop_id,
        sku_code = EXCLUDED.sku_code,
        product_name = EXCLUDED.product_name,
        match_type = EXCLUDED.match_type,
        category_id = EXCLUDED.category_id,
        enabled = EXCLUDED.enabled,
        auto_create = EXCLUDED.auto_create,
        reward_amount = EXCLUDED.reward_amount,
        unit_price_fen = EXCLUDED.unit_price_fen,
        required_deposit_amount = EXCLUDED.required_deposit_amount,
        deposit_threshold_amount = EXCLUDED.deposit_threshold_amount,
        sharing_enabled = EXCLUDED.sharing_enabled,
        sharing_total_quantity = EXCLUDED.sharing_total_quantity,
        sharing_unit_reward = EXCLUDED.sharing_unit_reward,
        sharing_auto_from_order = EXCLUDED.sharing_auto_from_order,
        timeout_minutes = EXCLUDED.timeout_minutes,
        timeout_policy = EXCLUDED.timeout_policy,
        acceptance_mode = EXCLUDED.acceptance_mode,
        gift_cooldown_hours = EXCLUDED.gift_cooldown_hours,
        gift_cooldown_minutes = EXCLUDED.gift_cooldown_minutes,
        requirement_json = EXCLUDED.requirement_json,
        match_json = EXCLUDED.match_json,
        sort_order = EXCLUDED.sort_order,
        updated_at = EXCLUDED.updated_at
      RETURNING *
    `,
    [
      input.ruleKey,
      input.provider,
      input.platform,
      input.shopId,
      input.skuCode,
      input.productName,
      input.matchType,
      input.categoryId,
      input.enabled,
      input.autoCreate,
      input.rewardAmount,
      Math.max(0, toPositiveInteger(input.unitPriceFen, 0)),
      input.requiredDepositAmount,
      input.depositThresholdAmount,
      input.sharingEnabled === true,
      toPositiveInteger(input.sharingTotalQuantity, 1),
      toPositiveInteger(input.sharingUnitReward, 0),
      input.sharingAutoFromOrder === true,
      toPositiveInteger(input.timeoutMinutes, 0),
      String(input.timeoutPolicy || 'reopen').trim(),
      normalizeWorkOrderAcceptanceMode(input.acceptanceMode),
      normalizeWorkOrderGiftCooldownHours(input.giftCooldownHours),
      normalizeWorkOrderGiftCooldownMinutes(input.giftCooldownMinutes),
      input.requirementJson,
      input.matchJson || '{}',
      input.sortOrder,
      input.now,
      input.now,
    ],
  )
  return getWorkProductRuleByKey(result.rows[0]?.rule_key || input.ruleKey)
}
