import Stripe from 'stripe'
import type { RowDataPacket, ResultSetHeader } from 'mysql2'
import { db, DB } from './db'

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string)

/**
 * Reembolsa un pago de Stripe, entero o en parte.
 *
 * `amountEur` omitido = todo. Stripe cobra en la unidad mínima, así que el
 * importe va en céntimos. El reembolso se pide sobre el PaymentIntent de la
 * sesión de checkout, que es lo que guardamos en `stripelog`.
 */
export async function refundStripeSession(sessionId: string, amountEur?: number): Promise<boolean> {
  if (!sessionId) return false
  try {
    const s = await stripe.checkout.sessions.retrieve(sessionId)
    const pi = typeof s.payment_intent === 'string' ? s.payment_intent : s.payment_intent?.id
    if (!pi) return false
    const cents = amountEur === undefined ? undefined : Math.round(amountEur * 100)
    if (cents !== undefined && cents <= 0) return false
    await stripe.refunds.create({ payment_intent: pi, ...(cents === undefined ? {} : { amount: cents }) })
    return true
  } catch (e) {
    console.error(`[stripe] no se pudo reembolsar ${sessionId}:`, e)
    return false
  }
}

export async function isSessionPaid(sessionId: string): Promise<boolean> {
  if (!sessionId) return false
  try {
    const s = await stripe.checkout.sessions.retrieve(sessionId)
    return s.payment_status === 'paid'
  } catch {
    return false
  }
}

export interface CheckoutParams {
  accountId: number
  username: string
  email: string
  acoreIp: string
  amount: number
  characterName: string
  productName: string
  successUrl: string
  cancelUrl: string
  currency?: string
  metadata?: Record<string, string>
}

/** Crea una sesión de pago Stripe y registra el StripeLog. Devuelve la URL de checkout. */
export async function createCheckoutSession(
  p: CheckoutParams,
): Promise<{ success: boolean; url?: string; error?: string }> {
  try {
    const sep = p.successUrl.includes('?') ? '&' : '?'
    const successUrl = `${p.successUrl}${sep}session_id={CHECKOUT_SESSION_ID}`
    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      line_items: [
        {
          price_data: {
            currency: p.currency || 'eur',
            product_data: { name: p.productName },
            unit_amount: Math.round(p.amount * 100),
          },
          quantity: 1,
        },
      ],
      mode: 'payment',
      success_url: successUrl,
      cancel_url: p.cancelUrl,
      metadata: p.metadata,
    })
    const mode = (process.env.STRIPE_SECRET_KEY || '').startsWith('sk_live') ? 'live' : 'test'
    await db(DB.default).query(
      'INSERT INTO stripelog (account_id, username, email, acore_ip, stripe_ip, product_name, amount, session_id, mode, character_name, timestamp, fulfilled) ' +
        'VALUES (?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, NOW(), 0)',
      [p.accountId, p.username, p.email, p.acoreIp, p.productName, p.amount, session.id, mode, p.characterName],
    )
    return { success: true, url: session.url ?? undefined }
  } catch (e) {
    return { success: false, error: String(e) }
  }
}

/**
 * Verifica que la sesión está pagada y aún no entregada; la marca como entregada
 * (evita re-entregas). Devuelve el character_name del log o null. (Port de
 * require_paid_stripe.)
 */
export async function claimPaidCheckout(
  sessionId: string,
): Promise<{ characterName: string; metadata: Record<string, string> } | null> {
  if (!sessionId) return null
  let metadata: Record<string, string> = {}
  try {
    const s = await stripe.checkout.sessions.retrieve(sessionId)
    if (s.payment_status !== 'paid') return null
    metadata = (s.metadata as Record<string, string>) ?? {}
  } catch {
    return null
  }
  const [rows] = await db(DB.default).query<RowDataPacket[]>(
    'SELECT id, character_name FROM stripelog WHERE session_id = ?',
    [sessionId],
  )
  const log = rows[0]
  if (!log) return null
  // Reclamo ATÓMICO: solo gana quien pasa fulfilled 0->1. Evita doble entrega si
  // el webhook y la página de éxito corren a la vez.
  const [res] = await db(DB.default).query<ResultSetHeader>(
    'UPDATE stripelog SET fulfilled = 1 WHERE id = ? AND fulfilled = 0',
    [log.id],
  )
  if (res.affectedRows === 0) return null
  return { characterName: log.character_name, metadata }
}

/** Registra la IP de Stripe y actualiza el timestamp del log (como el webhook de Django). */
export async function recordStripeIp(sessionId: string, ip: string): Promise<void> {
  if (!sessionId) return
  await db(DB.default).query('UPDATE stripelog SET stripe_ip = ?, timestamp = NOW() WHERE session_id = ?', [
    ip.slice(0, 45),
    sessionId,
  ])
}
