import { getDb } from '../db/client.js'
import { SessionStatus } from '../constants.js'

const sql = getDb()

// Default tiers as fallback (used if pricing_tiers is empty — e.g. fresh dev DB or
// test fixture without backfill). Match the DEFAULT_*_BACKFILL arrays in migrate.js
// so a missing row never breaks pricing in the wild. Stage 3 keeps these as a
// belt-and-suspenders only — `id` here is the legacy bare string, which Stage 3
// preserves for the "no rows" edge case so the customer-facing shape (UUID strings
// once populated, bare strings only in this degraded fallback path) keeps the
// same JSON keys.
const DEFAULT_CHAT_TIERS = [
  { id: '5',   minutes: 5,   price_idr: 5000,  tag: null },
  { id: '12',  minutes: 12,  price_idr: 12000, tag: 'paling pas' },
  { id: '30',  minutes: 30,  price_idr: 25000, tag: 'hemat' },
  { id: '60',  minutes: 60,  price_idr: 45000, tag: null },
  { id: '120', minutes: 120, price_idr: 80000, tag: 'best deal' },
]
const DEFAULT_CALL_TIERS = [
  { id: '10', minutes: 10, price_idr: 9000,  tag: null },
  { id: '20', minutes: 20, price_idr: 17000, tag: 'paling pas' },
  { id: '45', minutes: 45, price_idr: 35000, tag: null },
  { id: '60', minutes: 60, price_idr: 45000, tag: 'hemat' },
]
const DEFAULT_DISCOUNT = {
  enabled: true,
  actual_price_idr: 2000,
  gimmick_price_idr: 12000,
  duration_minutes: 12,
  modes: ['chat'],
}

// Map a pricing_tiers row to the customer-facing tier object. `original_price_idr`
// is deliberately omitted from the public shape (Stage 3 keeps it schema-only).
const toCustomerTier = (row) => ({
  id: row.id,
  tag: row.tag,
  minutes: row.minutes,
  price_idr: row.price_idr,
})

const readChatTiers = async () => {
  const rows = await sql`
    SELECT id, minutes, price_idr, tag, sort_order
    FROM pricing_tiers
    WHERE mode = 'chat' AND is_active = true
    ORDER BY sort_order ASC, minutes ASC
  `
  if (rows.length === 0) return DEFAULT_CHAT_TIERS
  return rows.map(toCustomerTier)
}

const readCallTiers = async () => {
  const rows = await sql`
    SELECT id, minutes, price_idr, tag, sort_order
    FROM pricing_tiers
    WHERE mode = 'call' AND is_active = true
    ORDER BY sort_order ASC, minutes ASC
  `
  if (rows.length === 0) return DEFAULT_CALL_TIERS
  return rows.map(toCustomerTier)
}

/**
 * Read the canonical first-session-discount config from `pricing_promotions`
 * (eligibility = 'first_session'). Falls back to DEFAULT_DISCOUNT if the row
 * is missing (fresh dev DB or partial migration).
 *
 * Exported because the customer-facing payment-session route needs the exact
 * same source-of-truth as `getPricingForCustomer` and the eligibility predicate
 * — duplicating the read in route code would be a foot-gun for operator edits
 * in CC (Stage 5 moved this off `app_config` JSON to the relational table).
 */
export const readFirstSessionDiscountConfig = async () => {
  const [row] = await sql`
    SELECT enabled, actual_price_idr, gimmick_price_idr, duration_minutes, modes
    FROM pricing_promotions
    WHERE eligibility = 'first_session'
  `
  if (!row) return DEFAULT_DISCOUNT
  return {
    enabled: row.enabled,
    actual_price_idr: row.actual_price_idr,
    gimmick_price_idr: row.gimmick_price_idr,
    duration_minutes: row.duration_minutes,
    modes: row.modes,
  }
}

/**
 * Per-customer first-session-discount eligibility.
 *
 * Predicate (Phase 4):
 *   - pricing_promotions.enabled (for eligibility='first_session') == true, AND
 *   - customer is phone-verified (customers.phone IS NOT NULL — phone only gets set
 *     via the OTP-verify path, so non-null is proof of verification), AND
 *   - customer has no completed/closing chat_sessions row (returning users pay full price).
 *
 * Note: deviates from the plan's `users.phone_verified_at` reference — there is no such
 * column. `phone IS NOT NULL` is the equivalent invariant in this schema.
 */
export const isCustomerEligibleForFirstSessionDiscount = async (customerId) => {
  const discount = await readFirstSessionDiscountConfig()
  if (!discount.enabled) return false

  const [customer] = await sql`
    SELECT phone FROM customers WHERE id = ${customerId}
  `
  if (!customer || !customer.phone) return false

  const [prior] = await sql`
    SELECT id FROM chat_sessions
    WHERE customer_id = ${customerId}
      AND status IN (${SessionStatus.COMPLETED}, ${SessionStatus.CLOSING})
    LIMIT 1
  `
  return !prior
}

/**
 * Pricing payload for the client. Returns chat + call tier groups plus a per-customer
 * first-session-discount block.
 *
 * Shape:
 *   { chat: { tiers: [...] },
 *     call: { tiers: [...] },
 *     first_session_discount: {
 *       eligible: boolean,
 *       actual_price_idr, gimmick_price_idr, duration_minutes, modes: string[]
 *     } }
 */
export const getPricingForCustomer = async (customerId) => {
  const [chatTiers, callTiers, discount, eligible] = await Promise.all([
    readChatTiers(),
    readCallTiers(),
    readFirstSessionDiscountConfig(),
    isCustomerEligibleForFirstSessionDiscount(customerId),
  ])
  return {
    chat: { tiers: chatTiers },
    call: { tiers: callTiers },
    first_session_discount: {
      eligible,
      actual_price_idr: discount.actual_price_idr,
      gimmick_price_idr: discount.gimmick_price_idr,
      duration_minutes: discount.duration_minutes,
      modes: discount.modes,
    },
  }
}

/**
 * Validate a (mode, duration_minutes, price_idr) selection against the configured tiers.
 * Used by payment-session creation as a defense-in-depth check.
 */
export const isValidTier = async ({ mode, durationMinutes, priceIdr }) => {
  const [row] = await sql`
    SELECT id FROM pricing_tiers
    WHERE mode = ${mode}
      AND minutes = ${durationMinutes}
      AND price_idr = ${priceIdr}
      AND is_active = true
    LIMIT 1
  `
  if (row) return true
  // Fallback: empty table → use in-memory defaults. Keeps tests with a clean DB green.
  const tiers = mode === 'call' ? DEFAULT_CALL_TIERS : DEFAULT_CHAT_TIERS
  return tiers.some((t) => t.minutes === durationMinutes && t.price_idr === priceIdr)
}

/**
 * Look up the canonical tier for (mode, duration_minutes). Returns null if no match.
 */
export const findTier = async ({ mode, durationMinutes }) => {
  const [row] = await sql`
    SELECT id, minutes, price_idr, tag
    FROM pricing_tiers
    WHERE mode = ${mode}
      AND minutes = ${durationMinutes}
      AND is_active = true
    LIMIT 1
  `
  if (row) return { id: row.id, minutes: row.minutes, price_idr: row.price_idr, tag: row.tag }
  // Fallback: empty table → in-memory defaults.
  const tiers = mode === 'call' ? DEFAULT_CALL_TIERS : DEFAULT_CHAT_TIERS
  return tiers.find((t) => t.minutes === durationMinutes) ?? null
}

/**
 * Extension pricing — same chat tiers, but first-session discount NEVER applies.
 * (Kept for parity with the old pricing.service shape; voice-call extensions are not
 * a current feature, so we return chat tiers only.)
 */
// eslint-disable-next-line no-unused-vars
export const getExtensionPriceTiers = async (customerId) => {
  const tiers = await readChatTiers()
  return {
    tiers,
    first_session_discount: { eligible: false },
  }
}

// ---- Back-compat aliases (will be removed after Phase 4 frontend cutover) ----

/**
 * @deprecated Use isCustomerEligibleForFirstSessionDiscount.
 * Kept so route handlers and migrated services still resolve while we cut over.
 */
export const isCustomerEligibleForFreeTrial = isCustomerEligibleForFirstSessionDiscount

/**
 * @deprecated Tiers now live in `chat`/`call` groups; callers should pick one.
 * Returns chat tiers in the legacy shape (single array, no group wrapper).
 */
export const getPriceTiers = async () => {
  const tiers = await readChatTiers()
  // Legacy callers expected `{duration_minutes, price, label}` keys. Map.
  return tiers.map((t) => ({
    duration_minutes: t.minutes,
    price: t.price_idr,
    label: `${t.minutes} Menit`,
    id: t.id,
    tag: t.tag,
  }))
}
