import type { QueryResult, QueryResultRow } from 'pg'

import { query } from '../db/client.js'
import type {
  OrderCreateInput,
  OrderListQueryInput,
  OrderUpdateInput,
} from '../types/repository/inputs.js'
import type { OrderListQueryResult, OrderListRow, OrderRow } from '../types/repository/rows.js'

export type OrderQueryExecutor = <T extends QueryResultRow = QueryResultRow>(
  text: string,
  params?: unknown[],
) => Promise<QueryResult<T>>

type OrderPlatformLookupInput = {
  provider?: string
  platform: string
  shopId?: string
  platformOrderId: string
}

export async function findOrderByPlatformOrderId({
  provider = '91kaquan',
  platform,
  shopId = '',
  platformOrderId,
}: OrderPlatformLookupInput): Promise<OrderRow | null> {
  const result = await query<OrderRow>(
    `
      SELECT *
      FROM orders
      WHERE provider = $1 AND platform = $2 AND shop_id = $3 AND platform_order_id = $4
      LIMIT 1
    `,
    [provider, platform, shopId, platformOrderId],
  )

  return result.rows[0] || null
}

export async function findLatestOrderByPlatformOrderId({
  provider = '91kaquan',
  platform,
  platformOrderId,
}: Omit<OrderPlatformLookupInput, 'shopId'>): Promise<OrderRow | null> {
  return findLatestOrderByPlatformOrderIdWithExecutor(query, {
    provider,
    platform,
    platformOrderId,
  })
}

export async function findLatestOrderByPlatformOrderIdWithExecutor(
  executor: OrderQueryExecutor,
  { provider = '91kaquan', platform, platformOrderId }: Omit<OrderPlatformLookupInput, 'shopId'>,
): Promise<OrderRow | null> {
  const result = await executor<OrderRow>(
    `
      SELECT *
      FROM orders
      WHERE provider = $1 AND platform = $2 AND platform_order_id = $3
      ORDER BY id DESC
      LIMIT 1
    `,
    [provider, platform, platformOrderId],
  )

  return result.rows[0] || null
}

export async function findLatestOrderByAnyPlatformOrderId(
  platformOrderId: string,
): Promise<OrderRow | null> {
  const result = await query<OrderRow>(
    `
      SELECT *
      FROM orders
      WHERE platform_order_id = $1
      ORDER BY id DESC
      LIMIT 1
    `,
    [String(platformOrderId || '').trim()],
  )

  return result.rows[0] || null
}

export async function createOrder(input: OrderCreateInput): Promise<OrderRow | null> {
  return createOrderWithExecutor(query, input)
}

export async function createOrderWithExecutor(
  executor: OrderQueryExecutor,
  input: OrderCreateInput,
): Promise<OrderRow | null> {
  const result = await executor<OrderRow>(
    `
      INSERT INTO orders (
        provider,
        platform,
        shop_id,
        shop_name,
        platform_order_id,
        order_status,
        pay_status,
        buyer_id,
        buyer_name,
        receiver_contact,
        total_amount,
        currency,
        raw_payload_json,
        paid_at,
        created_at,
        updated_at
      ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13::jsonb, $14, $15, $16)
      RETURNING *
    `,
    [
      input.provider,
      input.platform,
      input.shopId,
      input.shopName,
      input.platformOrderId,
      input.orderStatus,
      input.payStatus,
      input.buyerId,
      input.buyerName,
      input.receiverContact,
      input.totalAmount,
      input.currency,
      input.rawPayloadJson || '{}',
      input.paidAt || null,
      input.createdAt,
      input.updatedAt,
    ],
  )

  return result.rows[0] || null
}

export async function updateOrder(
  orderId: number | string,
  input: OrderUpdateInput,
): Promise<OrderRow | null> {
  return updateOrderWithExecutor(query, orderId, input)
}

export async function updateOrderWithExecutor(
  executor: OrderQueryExecutor,
  orderId: number | string,
  input: OrderUpdateInput,
): Promise<OrderRow | null> {
  const result = await executor<OrderRow>(
    `
      UPDATE orders
      SET
        provider = $1,
        platform = $2,
        shop_id = $3,
        shop_name = $4,
        order_status = $5,
        pay_status = $6,
        buyer_id = $7,
        buyer_name = $8,
        receiver_contact = $9,
        total_amount = $10,
        currency = $11,
        raw_payload_json = $12::jsonb,
        paid_at = $13,
        updated_at = $14
      WHERE id = $15
      RETURNING *
    `,
    [
      input.provider,
      input.platform,
      input.shopId,
      input.shopName,
      input.orderStatus,
      input.payStatus,
      input.buyerId,
      input.buyerName,
      input.receiverContact,
      input.totalAmount,
      input.currency,
      input.rawPayloadJson || '{}',
      input.paidAt || null,
      input.updatedAt,
      Number(orderId),
    ],
  )

  return result.rows[0] || null
}

export async function getOrderById(orderId: number | string): Promise<OrderRow | null> {
  const result = await query<OrderRow>('SELECT * FROM orders WHERE id = $1 LIMIT 1', [
    Number(orderId),
  ])
  return result.rows[0] || null
}

export async function listOrders({
  ...input
}: OrderListQueryInput = {}): Promise<OrderListQueryResult> {
  return listOrdersWithExecutor(query, input)
}

export async function listOrdersWithExecutor(
  executor: OrderQueryExecutor,
  {
    page = 1,
    pageSize = 20,
    keyword = '',
    provider = '',
    executorKey = '',
    platformOrderId = '',
    payStatus = '',
    taskStatus = '',
    skuCode = '',
    dateFrom = '',
    dateTo = '',
  }: OrderListQueryInput = {},
): Promise<OrderListQueryResult> {
  const offset = (page - 1) * pageSize
  const filters: string[] = []
  const params: unknown[] = []

  if (keyword) {
    params.push(`%${keyword}%`)
    const keywordParam = `$${params.length}`
    filters.push(`(
      (
        o.platform_order_id || ' ' || o.shop_id || ' ' || o.shop_name || ' ' ||
        o.buyer_id || ' ' || o.buyer_name || ' ' || o.receiver_contact
      ) ILIKE ${keywordParam}
      OR EXISTS (
        SELECT 1
        FROM order_items oi
        WHERE oi.order_id = o.id
          AND (
            oi.sku_code || ' ' || oi.sku_name || ' ' ||
            COALESCE(oi.item_snapshot_json->'kuaishouSendCode'->>'itemTitle', '') || ' ' ||
            COALESCE(oi.item_snapshot_json->'kuaishouSendCode'->>'skuNick', '')
          ) ILIKE ${keywordParam}
      )
    )`)
  }

  if (provider) {
    params.push(provider)
    filters.push(`o.provider = $${params.length}`)
  }

  if (platformOrderId) {
    params.push(`%${platformOrderId}%`)
    filters.push(`o.platform_order_id ILIKE $${params.length}`)
  }

  if (payStatus) {
    params.push(payStatus)
    filters.push(`o.pay_status = $${params.length}`)
  }

  if (taskStatus) {
    params.push(taskStatus)
    filters.push(
      `EXISTS (SELECT 1 FROM fulfillment_tasks ft WHERE ft.order_id = o.id AND ft.task_status = $${params.length})`,
    )
  }

  if (executorKey) {
    params.push(executorKey)
    filters.push(
      `EXISTS (SELECT 1 FROM fulfillment_tasks ft WHERE ft.order_id = o.id AND ft.executor_key = $${params.length})`,
    )
  }

  if (skuCode) {
    params.push(skuCode)
    filters.push(
      `EXISTS (SELECT 1 FROM order_items oi WHERE oi.order_id = o.id AND oi.sku_code = $${params.length})`,
    )
  }

  if (dateFrom) {
    params.push(dateFrom)
    filters.push(`o.created_at >= $${params.length}`)
  }

  if (dateTo) {
    params.push(dateTo)
    filters.push(`o.created_at <= $${params.length}`)
  }

  const whereClause = filters.length > 0 ? `WHERE ${filters.join(' AND ')}` : ''
  const totalResult = await executor<{ [column: string]: unknown; total: number }>(
    `SELECT COUNT(*)::int AS total FROM orders o ${whereClause}`,
    params,
  )

  params.push(pageSize)
  params.push(offset)
  const itemsResult = await executor<OrderListRow>(
    `
      SELECT o.*
      FROM orders o
      ${whereClause}
      ORDER BY o.id DESC
      LIMIT $${params.length - 1} OFFSET $${params.length}
    `,
    params,
  )

  return {
    items: itemsResult.rows,
    total: Number(totalResult.rows[0]?.total || 0),
  }
}
