#!/usr/bin/env node

import process from 'node:process'

import { runtimeConfig } from '../src/config/runtime.js'
import { closeDb, query } from '../src/db/client.js'
import { runDatabaseMigrations } from '../src/db/migrate.js'
import { updateClaimToken } from '../src/repositories/claim-token-repo.js'
import { getFulfillmentProfileByKey, upsertSkuFulfillmentBinding } from '../src/repositories/fulfillment-profile-repo.js'
import { createInventoryItems, releaseReservedInventoryItem } from '../src/repositories/inventory-repo.js'
import { getTaskById, updateTask } from '../src/repositories/task-repo.js'
import { ensureFulfillmentCatalogBootstrapped } from '../src/services/bootstrap/fulfillment-bootstrap-service.js'
import { completeAdminTaskManualDispatch } from '../src/services/admin/admin-service.js'
import { upsertOrderFromWebhook } from '../src/services/order/order-service.js'
import { addHours, nowIso } from '../src/utils/time.js'

const RESET_TABLES = [
  'message_deliveries',
  'tencent_browser_contexts',
  'task_events',
  'claim_tokens',
  'task_inventory_bindings',
  'fulfillment_tasks',
  'order_items',
  'orders',
  'webhook_events',
  'product_match_rules',
  'inventory_items',
]

const SEED_PROVIDER = 'seed'
const SEED_PLATFORM = 'sandbox'
const SEED_SHOP_ID = 'seed-shop'
const SEED_SHOP_NAME = 'Seed Sandbox Shop'

const options = parseArgs(process.argv.slice(2))

if (options.help) {
  printHelp()
  process.exit(0)
}

try {
  printPreview()

  if (!options.apply) {
    console.log('\n当前为预览模式。确认后追加 `--apply`，需要清库时再加 `--reset`。')
    process.exit(0)
  }

  await runDatabaseMigrations()
  await ensureFulfillmentCatalogBootstrapped()

  if (options.reset) {
    await query(`TRUNCATE TABLE ${RESET_TABLES.join(', ')} RESTART IDENTITY CASCADE`)
    console.log('\n已先清空测试业务数据。')
  }

  await ensureSeedBindings()
  await createSeedInventory()

  const manualPending = await seedScenario({
    scenarioKey: 'manual_pending',
    orderLabel: 'SEED-MANUAL-PENDING',
    skuCode: 'seed-manual-pending',
    skuName: '开发种子-人工履约待处理',
  })

  const manualCompleted = await seedScenario({
    scenarioKey: 'manual_completed',
    orderLabel: 'SEED-MANUAL-COMPLETED',
    skuCode: 'seed-manual-completed',
    skuName: '开发种子-人工履约已完成',
    afterCreate: async (result) => {
      const task = result.tasks[0]
      if (!task) {
        return null
      }

      await completeAdminTaskManualDispatch(task.id, {
        outcome: 'delivered',
        resultMessage: '开发种子数据：人工履约已完成',
        deliveryReference: 'SEED-MANUAL-REF-001',
        deliveredCredential: '开发种子数据：已通过人工渠道发放',
      })

      return getTaskById(task.id)
    },
  })

  const claimReady = await seedScenario({
    scenarioKey: 'claim_ready',
    orderLabel: 'SEED-CLAIM-READY',
    skuCode: 'seed-claim-ready',
    skuName: '开发种子-领取兑换可用',
  })

  const claimClaimed = await seedScenario({
    scenarioKey: 'claim_claimed',
    orderLabel: 'SEED-CLAIM-CLAIMED',
    skuCode: 'seed-claim-claimed',
    skuName: '开发种子-已打开领取链接',
    afterCreate: async (result) => applyClaimedState(result.tasks[0]),
  })

  const claimRoleConfirmed = await seedScenario({
    scenarioKey: 'claim_role_confirmed',
    orderLabel: 'SEED-CLAIM-ROLE',
    skuCode: 'seed-claim-role-confirmed',
    skuName: '开发种子-已确认角色',
    afterCreate: async (result) => applyRoleConfirmedState(result.tasks[0]),
  })

  const claimRetryPending = await seedScenario({
    scenarioKey: 'claim_retry_pending',
    orderLabel: 'SEED-CLAIM-RETRY',
    skuCode: 'seed-claim-retry',
    skuName: '开发种子-等待重试',
    afterCreate: async (result) => applyRetryPendingState(result.tasks[0]),
  })

  const claimExpired = await seedScenario({
    scenarioKey: 'claim_expired',
    orderLabel: 'SEED-CLAIM-EXPIRED',
    skuCode: 'seed-claim-expired',
    skuName: '开发种子-领取链接已过期',
    afterCreate: async (result) => applyExpiredState(result.tasks[0]),
  })

  const claimWaitingInventory = await seedScenario({
    scenarioKey: 'claim_waiting_inventory',
    orderLabel: 'SEED-CLAIM-WAIT',
    skuCode: 'seed-claim-wait',
    skuName: '开发种子-等待库存',
  })

  printResultSummary([
    manualPending,
    manualCompleted,
    claimReady,
    claimClaimed,
    claimRoleConfirmed,
    claimRetryPending,
    claimExpired,
    claimWaitingInventory,
  ])
} catch (error) {
  console.error('\n写入开发种子数据失败：', error instanceof Error ? error.message : String(error || '未知错误'))
  process.exitCode = 1
} finally {
  await closeDb()
}

async function ensureSeedBindings() {
  const timestamp = nowIso()
  const manualProfile = await getRequiredProfile('manual_review')
  const claimProfile = await getRequiredProfile('tencent_claim_redeem')

  const bindings = [
    { skuCode: 'seed-manual-pending', profileId: manualProfile.id, priority: 10 },
    { skuCode: 'seed-manual-completed', profileId: manualProfile.id, priority: 20 },
    { skuCode: 'seed-claim-ready', profileId: claimProfile.id, priority: 30 },
    { skuCode: 'seed-claim-claimed', profileId: claimProfile.id, priority: 40 },
    { skuCode: 'seed-claim-role-confirmed', profileId: claimProfile.id, priority: 50 },
    { skuCode: 'seed-claim-retry', profileId: claimProfile.id, priority: 60 },
    { skuCode: 'seed-claim-expired', profileId: claimProfile.id, priority: 70 },
    { skuCode: 'seed-claim-wait', profileId: claimProfile.id, priority: 80 },
  ]

  for (const binding of bindings) {
    await upsertSkuFulfillmentBinding({
      skuCode: binding.skuCode,
      provider: SEED_PROVIDER,
      platform: SEED_PLATFORM,
      shopId: SEED_SHOP_ID,
      profileId: binding.profileId,
      enabled: true,
      priority: binding.priority,
      configJson: JSON.stringify({
        seededBy: 'scripts/seed-dev-data.ts',
        scenario: binding.skuCode,
      }),
      createdAt: timestamp,
      updatedAt: timestamp,
    })
  }
}

async function createSeedInventory() {
  const timestamp = nowIso()

  await createInventoryItems([
    {
      skuCode: 'seed-claim-ready',
      displayValue: 'SEED-CLAIM-CODE-READY-001',
      batchNo: 'seed-20260409',
      credentialType: 'tencent_code',
      payload: {
        code: 'SEED-CLAIM-CODE-READY-001',
        scenario: 'claim_ready',
      },
      createdAt: timestamp,
      updatedAt: timestamp,
    },
    {
      skuCode: 'seed-claim-claimed',
      displayValue: 'SEED-CLAIM-CODE-CLAIMED-001',
      batchNo: 'seed-20260409',
      credentialType: 'tencent_code',
      payload: {
        code: 'SEED-CLAIM-CODE-CLAIMED-001',
        scenario: 'claim_claimed',
      },
      createdAt: timestamp,
      updatedAt: timestamp,
    },
    {
      skuCode: 'seed-claim-role-confirmed',
      displayValue: 'SEED-CLAIM-CODE-ROLE-001',
      batchNo: 'seed-20260409',
      credentialType: 'tencent_code',
      payload: {
        code: 'SEED-CLAIM-CODE-ROLE-001',
        scenario: 'claim_role_confirmed',
      },
      createdAt: timestamp,
      updatedAt: timestamp,
    },
    {
      skuCode: 'seed-claim-retry',
      displayValue: 'SEED-CLAIM-CODE-RETRY-001',
      batchNo: 'seed-20260409',
      credentialType: 'tencent_code',
      payload: {
        code: 'SEED-CLAIM-CODE-RETRY-001',
        scenario: 'claim_retry_pending',
      },
      createdAt: timestamp,
      updatedAt: timestamp,
    },
    {
      skuCode: 'seed-claim-expired',
      displayValue: 'SEED-CLAIM-CODE-EXPIRED-001',
      batchNo: 'seed-20260409',
      credentialType: 'tencent_code',
      payload: {
        code: 'SEED-CLAIM-CODE-EXPIRED-001',
        scenario: 'claim_expired',
      },
      createdAt: timestamp,
      updatedAt: timestamp,
    },
    {
      skuCode: 'seed-spare-available',
      displayValue: 'SEED-SPARE-CODE-001',
      batchNo: 'seed-20260409',
      credentialType: 'tencent_code',
      payload: {
        code: 'SEED-SPARE-CODE-001',
        scenario: 'spare_available',
      },
      createdAt: timestamp,
      updatedAt: timestamp,
    },
  ])
}

async function seedScenario({ scenarioKey, orderLabel, skuCode, skuName, afterCreate = null }) {
  const result = await upsertOrderFromWebhook(buildSeedOrderEvent({
    scenarioKey,
    orderLabel,
    skuCode,
    skuName,
  }))

  const task = typeof afterCreate === 'function'
    ? await afterCreate(result)
    : result.tasks[0] || null

  return {
    scenarioKey,
    orderId: result.order.id,
    platformOrderId: result.order.platform_order_id,
    taskId: task?.id || null,
    taskNo: task?.task_no || '',
    taskStatus: task?.task_status || '',
    deliveryStatus: task?.delivery_status || '',
    inventoryStatus: task?.inventory_status || '',
    inventoryItemId: Number(task?.primary_inventory_item_id || 0) || null,
    claimTokenId: Number(task?.primary_claim_token_id || 0) || null,
    executorKey: String(task?.executor_key || '').trim(),
  }
}

async function applyClaimedState(task) {
  if (!task) {
    return null
  }

  const now = nowIso()
  await updateTask(task.id, {
    task_status: 'claimed',
    user_action_status: 'claimed',
    browser_session_id: `seed-browser-${task.id}`,
    login_type: 'qq',
    claimed_at: now,
    updated_at: now,
    last_error: '',
  })

  return getTaskById(task.id)
}

async function applyRoleConfirmedState(task) {
  if (!task) {
    return null
  }

  const claimedAt = addHours(nowIso(), -1)
  const roleConfirmedAt = nowIso()
  await updateTask(task.id, {
    task_status: 'role_confirmed',
    user_action_status: 'role_confirmed',
    browser_session_id: `seed-browser-${task.id}`,
    login_type: 'qq',
    nickname: 'SeedHero',
    role_id: 'seed-role-1001',
    role_name: '开发角色-已确认',
    area: 'seed-area',
    partition_name: 'seed-partition',
    claimed_at: claimedAt,
    role_confirmed_at: roleConfirmedAt,
    updated_at: roleConfirmedAt,
    last_error: '',
  })

  return getTaskById(task.id)
}

async function applyRetryPendingState(task) {
  if (!task) {
    return null
  }

  const claimedAt = addHours(nowIso(), -2)
  const roleConfirmedAt = addHours(nowIso(), -1)
  const updatedAt = nowIso()
  await updateTask(task.id, {
    task_status: 'retry_pending',
    delivery_status: 'pending',
    inventory_status: 'reserved',
    user_action_status: 'role_confirmed',
    browser_session_id: `seed-browser-${task.id}`,
    login_type: 'qq',
    nickname: 'SeedRetryUser',
    role_id: 'seed-role-retry',
    role_name: '开发角色-待重试',
    area: 'seed-area',
    partition_name: 'seed-partition',
    claimed_at: claimedAt,
    role_confirmed_at: roleConfirmedAt,
    attempt_count: 2,
    last_error: '开发种子数据：模拟兑换失败，等待重试',
    updated_at: updatedAt,
  })

  return getTaskById(task.id)
}

async function applyExpiredState(task) {
  if (!task) {
    return null
  }

  const now = nowIso()
  if (task.primary_claim_token_id) {
    await updateClaimToken(task.primary_claim_token_id, {
      status: 'expired',
      expired_at: addHours(now, -2),
      updated_at: now,
    })
  }

  if (task.primary_inventory_item_id) {
    await releaseReservedInventoryItem(task.primary_inventory_item_id, now)
  }

  await updateTask(task.id, {
    task_status: 'expired',
    inventory_status: 'pending',
    user_action_status: 'expired',
    last_error: '领取链接已过期，预占库存项已释放',
    updated_at: now,
  })

  return getTaskById(task.id)
}

function buildSeedOrderEvent({ scenarioKey, orderLabel, skuCode, skuName }) {
  const timestamp = nowIso()

  return {
    provider: SEED_PROVIDER,
    platform: SEED_PLATFORM,
    shopId: SEED_SHOP_ID,
    shopName: SEED_SHOP_NAME,
    platformOrderId: orderLabel,
    orderStatus: 'paid',
    payStatus: 'paid',
    buyerId: `${scenarioKey}-buyer`,
    buyerName: `开发种子-${scenarioKey}`,
    receiverContact: `seed+${scenarioKey}@example.com`,
    totalAmount: 100,
    currency: 'CNY',
    paidAt: timestamp,
    rawPayload: {
      source: 'seed-dev-data',
      scenarioKey,
      platformOrderId: orderLabel,
    },
    items: [
      {
        skuCode,
        skuName,
        quantity: 1,
        spec: {
          title: skuName,
          scenarioKey,
        },
        snapshot: {
          title: skuName,
          scenarioKey,
          seeded: true,
        },
      },
    ],
  }
}

async function getRequiredProfile(profileKey) {
  const profile = await getFulfillmentProfileByKey(profileKey)

  if (!profile) {
    throw new Error(`缺少履约档案: ${profileKey}`)
  }

  return profile
}

function parseArgs(argv) {
  return {
    apply: argv.includes('--apply'),
    reset: argv.includes('--reset'),
    help: argv.includes('-h') || argv.includes('--help'),
  }
}

function printHelp() {
  console.log(`
用法:
  npm run seed:dev-data -- [--apply] [--reset]

说明:
  基于当前 PostgreSQL fulfillment schema 生成一组可重复验证的开发种子数据。

场景:
  - manual_pending: 人工履约待处理
  - manual_completed: 人工履约已完成
  - claim_ready: 已预占库存并生成领取链接
  - claim_claimed: 用户已打开领取链接
  - claim_role_confirmed: 用户已确认角色
  - claim_retry_pending: 自动履约失败等待重试
  - claim_expired: 领取链接已过期
  - claim_waiting_inventory: 等待库存

参数:
  --apply   实际写入数据
  --reset   写入前先清空测试业务数据

数据库连接:
  ${String(runtimeConfig.database?.url || '').trim() || '(未配置 DATABASE_URL)'}
`.trim())
}

function printPreview() {
  console.log('准备生成开发种子数据：')
  console.log('- manual_pending -> 人工履约待处理')
  console.log('- manual_completed -> 人工履约已完成')
  console.log('- claim_ready -> 领取兑换已就绪')
  console.log('- claim_claimed -> 用户已打开领取链接')
  console.log('- claim_role_confirmed -> 用户已确认角色')
  console.log('- claim_retry_pending -> 自动履约失败等待重试')
  console.log('- claim_expired -> 领取链接已过期')
  console.log('- claim_waiting_inventory -> 等待库存')
  console.log(`- database: ${String(runtimeConfig.database?.url || '').trim() || '(未配置 DATABASE_URL)'}`)
}

function printResultSummary(rows) {
  console.log('\n开发种子数据已写入：')

  for (const row of rows) {
    console.log(`- ${row.scenarioKey}`)
    console.log(`  orderId=${row.orderId} platformOrderId=${row.platformOrderId}`)
    console.log(`  taskId=${row.taskId || '-'} taskNo=${row.taskNo || '-'} executor=${row.executorKey || '-'}`)
    console.log(`  taskStatus=${row.taskStatus || '-'} deliveryStatus=${row.deliveryStatus || '-'} inventoryStatus=${row.inventoryStatus || '-'}`)
    console.log(`  inventoryItemId=${row.inventoryItemId || '-'} claimTokenId=${row.claimTokenId || '-'}`)
  }

  console.log('\n建议验证入口：')
  console.log('- `/api/v1/admin/orders?platformOrderId=SEED-`')
  console.log('- `/api/v1/admin/tasks?taskNo=DT`')
  console.log('- `/api/v1/admin/inventory`')
}
