import type pg from 'pg';
import type { PosPaymentKind, PosReceipt, PosReceiptPayment } from '../domain/pos-sale.js';
import { AppError } from '../../../shared/errors.js';

type Queryable = Pick<pg.Pool, 'query'> | Pick<pg.PoolClient, 'query'>;

interface ReturnedItemRow {
  id: string;
  quantity: number;
  returned_quantity: number;
  unit_price_cents: number;
  discount_cents: number;
  tax_cents: number;
  vat_rate: 'general' | 'reduced' | 'super-reduced' | null;
  name: string;
  sku: string;
  is_free_item: boolean;
}

interface ReceiptOrderRow {
  id: string;
  receipt_number: string | null;
  subtotal_cents: number;
  discount_cents: number;
  tax_cents: number;
  total_cents: number;
  created_at: Date;
  cash_session_id: string;
  terminal_id: string;
  customer_email: string | null;
  store_name: string;
  address: string | null;
  tax_id: string | null;
  contact_email: string | null;
  contact_phone: string | null;
  receipt_header: string | null;
  receipt_footer: string | null;
  terminal_name: string;
  cashier_email: string;
  return_policy: string | null;
  logo_url: string | null;
}

interface ReceiptItemRow {
  name: string;
  sku: string;
  quantity: number;
  unit_price_cents: number;
  discount_cents: number;
  tax_cents: number;
  vat_rate: 'general' | 'reduced' | 'super-reduced' | null;
  is_free_item: boolean;
}

interface ReceiptPaymentRow {
  amount_cents: number;
  provider: string;
  raw: Record<string, unknown> | null;
}

export async function buildPosReceipt(queryable: Queryable, orderId: string): Promise<PosReceipt> {
  const orderResult = await queryable.query<ReceiptOrderRow>(
    `SELECT o.id, o.receipt_number, o.subtotal_cents, o.discount_cents, o.tax_cents,
            o.total_cents, o.created_at, o.cash_session_id, o.terminal_id,
            customer.email AS customer_email,
            store.name AS store_name, store.address, store.tax_id, store.contact_email,
            store.contact_phone, store.receipt_header, store.receipt_footer, store.logo_url,
            terminal.name AS terminal_name, cashier.email AS cashier_email,
            receipt_settings.return_policy
       FROM orders_orders o
       JOIN pos_stores store ON store.id = o.store_id
       JOIN pos_terminals terminal ON terminal.id = o.terminal_id
       JOIN pos_cash_sessions session ON session.id = o.cash_session_id
       JOIN backoffice_users cashier ON cashier.id = session.user_id
       LEFT JOIN identity_users customer ON customer.id = o.user_id
       LEFT JOIN pos_receipt_settings receipt_settings ON receipt_settings.store_id = o.store_id
      WHERE o.id = $1 AND o.source = 'pos'`,
    [orderId],
  );
  const order = orderResult.rows[0];
  if (!order) throw new AppError(404, 'POS_RECEIPT_NOT_FOUND', 'No se encontró el ticket');

  const [itemResult, paymentResult] = await Promise.all([
    queryable.query<ReceiptItemRow>(
      `SELECT name, sku, quantity, unit_price_cents, discount_cents, tax_cents, vat_rate, is_free_item
         FROM orders_items WHERE order_id = $1 ORDER BY created_at, id`,
      [orderId],
    ),
    queryable.query<ReceiptPaymentRow>(
      `SELECT amount_cents, provider, raw
         FROM payments_transactions WHERE order_id = $1 AND status = 'succeeded'
         ORDER BY created_at, id`,
      [orderId],
    ),
  ]);

  const payments: PosReceiptPayment[] = paymentResult.rows.map((row) => {
    const raw = row.raw ?? {};
    const kind = isPaymentKind(raw.kind) ? raw.kind : providerKind(row.provider);
    const tendered = integerOrNull(raw.tenderedCents);
    const change = integerOrZero(raw.changeCents);
    return {
      methodCode: stringOr(raw.methodCode, row.provider.replace(/^pos_/, '')),
      methodLabel: stringOr(raw.methodLabel, paymentKindLabel(kind)),
      kind,
      amountCents: Number(row.amount_cents),
      tenderedCents: tendered,
      changeCents: change,
    };
  });

  return {
    receiptNumber: order.receipt_number ?? `POS-${order.id.slice(0, 8).toUpperCase()}`,
    orderId: order.id,
    issuedAt: order.created_at,
    company: {
      name: order.store_name,
      address: order.address,
      taxId: order.tax_id,
      email: order.contact_email,
      phone: order.contact_phone,
    },
    logoUrl: order.logo_url,
    terminal: { id: order.terminal_id, name: order.terminal_name },
    cashier: order.cashier_email,
    sessionId: order.cash_session_id,
    customerEmail: order.customer_email,
    items: itemResult.rows.map((item) => {
      const quantity = Number(item.quantity);
      const unitNetCents = Number(item.unit_price_cents);
      const unitTaxCents = Number(item.tax_cents);
      const unitGrossCents = item.is_free_item
        ? unitNetCents
        : grossFromNet(unitNetCents, item.vat_rate);
      const totalCents = (unitNetCents - Number(item.discount_cents) + unitTaxCents) * quantity;
      const subtotalCents = unitGrossCents * quantity;
      const discountCents = Math.max(0, subtotalCents - totalCents);
      const taxCents = unitTaxCents * quantity;
      return {
        name: item.name,
        sku: item.sku,
        quantity,
        unitPriceCents: unitGrossCents,
        subtotalCents,
        discountCents,
        taxCents,
        totalCents,
        freeItem: item.is_free_item,
      };
    }),
    subtotalCents: Number(order.subtotal_cents),
    discountCents: Number(order.discount_cents),
    taxCents: Number(order.tax_cents),
    totalCents: Number(order.total_cents),
    payments,
    changeCents: payments.reduce((sum, payment) => sum + payment.changeCents, 0),
    header: order.receipt_header,
    returnPolicy:
      order.return_policy ?? 'Se admiten devoluciones durante 15 días con el ticket de compra.',
    footer: order.receipt_footer,
  };
}

function grossFromNet(
  netUnitAmountCents: number,
  vatRate: 'general' | 'reduced' | 'super-reduced' | null,
): number {
  if (!vatRate) return netUnitAmountCents;
  const basisPoints = vatRate === 'general' ? 2100 : vatRate === 'reduced' ? 1000 : 400;
  return Math.round(netUnitAmountCents * (1 + basisPoints / 10_000));
}

function isPaymentKind(value: unknown): value is PosPaymentKind {
  return value === 'cash' || value === 'card' || value === 'other';
}

function providerKind(provider: string): PosPaymentKind {
  if (provider === 'pos_cash') return 'cash';
  if (provider === 'pos_card') return 'card';
  return 'other';
}

function paymentKindLabel(kind: PosPaymentKind): string {
  if (kind === 'cash') return 'Efectivo';
  if (kind === 'card') return 'Tarjeta';
  return 'Otro';
}

function stringOr(value: unknown, fallback: string): string {
  return typeof value === 'string' && value.trim() ? value : fallback;
}

function integerOrNull(value: unknown): number | null {
  return typeof value === 'number' && Number.isInteger(value) ? value : null;
}

function integerOrZero(value: unknown): number {
  return typeof value === 'number' && Number.isInteger(value) ? value : 0;
}

/**
 * Build a return/reversal receipt based on the original POS order.
 *
 * The returned payload uses negative line totals and prices `R-<original>` as
 * its receipt number without persisting a separate sequence. The `paidCents`
 * block is replaced with a `refundedCents` flag and the receipt is flagged
 * `isReturn=true` so renderers can style it accordingly.
 */
export async function buildPosReturnReceipt(
  queryable: Queryable,
  orderId: string,
  refundedCents: number,
): Promise<PosReceipt> {
  const original = await buildPosReceipt(queryable, orderId);
  const itemResult = await queryable.query<ReturnedItemRow>(
    `SELECT id, quantity, returned_quantity, unit_price_cents, discount_cents, tax_cents,
            vat_rate, name, sku, is_free_item
       FROM orders_items WHERE order_id = $1 ORDER BY created_at, id`,
    [orderId],
  );
  const items = itemResult.rows
    .filter((row) => row.returned_quantity > 0)
    .map((row) => {
      const returnedQuantity = Number(row.returned_quantity);
      const unitNetCents = Number(row.unit_price_cents);
      const unitTaxCents = Number(row.tax_cents);
      const unitGrossCents = row.is_free_item
        ? unitNetCents
        : grossFromNet(unitNetCents, row.vat_rate);
      const total = (unitNetCents - Number(row.discount_cents) + unitTaxCents) * returnedQuantity;
      const subtotal = unitGrossCents * returnedQuantity;
      const discountCents = Math.max(0, subtotal - total);
      const taxCents = unitTaxCents * returnedQuantity;
      return {
        name: row.name,
        sku: row.sku,
        quantity: returnedQuantity,
        unitPriceCents: unitGrossCents,
        subtotalCents: subtotal,
        discountCents,
        taxCents,
        totalCents: -total,
        freeItem: row.is_free_item,
      };
    });
  const subtotal = items.reduce((sum, item) => sum + item.subtotalCents, 0);
  const discount = items.reduce((sum, item) => sum + item.discountCents, 0);
  const tax = items.reduce((sum, item) => sum + item.taxCents, 0);
  return {
    receiptNumber: original.receiptNumber.startsWith('R-')
      ? original.receiptNumber
      : `R-${original.receiptNumber}`,
    orderId: original.orderId,
    issuedAt: original.issuedAt,
    company: original.company,
    logoUrl: original.logoUrl,
    terminal: original.terminal,
    cashier: original.cashier,
    sessionId: original.sessionId,
    customerEmail: original.customerEmail,
    items,
    subtotalCents: -subtotal,
    discountCents: -discount,
    taxCents: -tax,
    totalCents: -refundedCents,
    payments: [],
    changeCents: 0,
    header: original.header,
    returnPolicy: original.returnPolicy,
    footer: original.footer,
    originalReceiptNumber: original.receiptNumber,
    isReturn: true,
  };
}
