import { v4 as uuidv4 } from 'uuid';

import { config } from '../config';
import { prisma } from '../db/client';
import { getRedis, REDIS_KEYS } from '../redis';
import {
  formatPaymentLabel,
  formatShippingLabel,
  getPaymentMethod,
  getPaymentMethodsForShipping,
  getShippingMethod,
  getShippingMethods,
} from './catalog';
import type { CartItem } from '../intercept/cart';
import {
  orderTotalWithShipping,
  validateOrderSubmit,
} from './orderSubmit';
import {
  createCheckoutDraft,
  getCheckoutDraft,
  saveCheckoutDraft,
  snapshotDraft,
  type CheckoutDraft,
  type CourierAddressDraft,
  type InvoiceDraft,
  type PickupPointDraft,
} from './sessionStore';
import { buildFelopayRedirectUrl, createPaymentToken, randomRef } from '../intercept/payment';

export interface CartSnapshot {
  items: CartItem[];
  totalPrice: number;
  totalItems: number;
  currency: string;
}

export async function loadCartBySessionId(cartSessionId: string): Promise<CartSnapshot> {
  const redis = getRedis();
  const raw = await redis.get(REDIS_KEYS.cart(cartSessionId));
  if (!raw) {
    return { items: [], totalPrice: 0, totalItems: 0, currency: 'PLN' };
  }
  const cart = JSON.parse(raw) as CartSnapshot & { id?: string };
  return {
    items: cart.items ?? [],
    totalPrice: cart.totalPrice ?? 0,
    totalItems: cart.totalItems ?? 0,
    currency: cart.currency ?? 'PLN',
  };
}

export async function saveBotCart(cartSessionId: string, items: CartItem[]): Promise<CartSnapshot> {
  const cart: CartSnapshot & { id: string } = {
    id: cartSessionId,
    items,
    totalPrice: 0,
    totalItems: 0,
    currency: items[0]?.currency ?? 'PLN',
  };
  cart.totalPrice = items.reduce((s, i) => s + i.price * i.quantity, 0);
  cart.totalItems = items.reduce((s, i) => s + i.quantity, 0);
  const redis = getRedis();
  await redis.setex(REDIS_KEYS.cart(cartSessionId), 60 * 60 * 24 * 7, JSON.stringify(cart));
  return cart;
}

export function computeTotals(draft: CheckoutDraft, cart: CartSnapshot) {
  const shipping = draft.shippingMethodId
    ? getShippingMethod(draft.shippingMethodId)
    : undefined;
  const shippingPrice = shipping?.price ?? 0;
  const subtotal = Math.round(cart.totalPrice * 100) / 100;
  const total = orderTotalWithShipping(subtotal, shippingPrice);
  return { subtotal, shipping: shippingPrice, total, currency: cart.currency || 'PLN' };
}

export function draftSnapshotResponse(draft: CheckoutDraft, cart: CartSnapshot) {
  const shipping = draft.shippingMethodId
    ? getShippingMethod(draft.shippingMethodId)
    : undefined;
  return {
    ...snapshotDraft(draft),
    deliveryType: shipping?.type ?? null,
    totals: computeTotals(draft, cart),
    allowedPaymentIds: draft.shippingMethodId
      ? getPaymentMethodsForShipping(draft.shippingMethodId).map((p) => p.id)
      : [],
  };
}

export async function requireDraft(id: string): Promise<CheckoutDraft | null> {
  return getCheckoutDraft(id);
}

export async function createSession(cartSessionId: string) {
  const draft = await createCheckoutDraft(cartSessionId);
  const firstShipping = getShippingMethods()[0];
  if (firstShipping) {
    draft.shippingMethodId = firstShipping.id;
    draft.paymentMethodId = firstShipping.payments[0];
    await saveCheckoutDraft(draft);
  }
  const cart = await loadCartBySessionId(cartSessionId);
  return { checkoutSessionId: draft.id, snapshot: draftSnapshotResponse(draft, cart) };
}

export async function getSessionView(id: string) {
  const draft = await getCheckoutDraft(id);
  if (!draft) return null;
  const cart = await loadCartBySessionId(draft.cartSessionId);
  return draftSnapshotResponse(draft, cart);
}

export async function updateShipping(id: string, shippingMethodId: string) {
  const draft = await getCheckoutDraft(id);
  if (!draft) return { ok: false as const, error: 'Session not found' };
  const shipping = getShippingMethod(shippingMethodId);
  if (!shipping) return { ok: false as const, error: 'Nieprawidłowa metoda dostawy' };

  draft.shippingMethodId = shippingMethodId;
  draft.pickupPoint = undefined;
  draft.courierAddress = undefined;
  if (!draft.paymentMethodId || !shipping.payments.includes(draft.paymentMethodId)) {
    draft.paymentMethodId = shipping.payments[0];
  }
  await saveCheckoutDraft(draft);
  const cart = await loadCartBySessionId(draft.cartSessionId);
  return {
    ok: true as const,
    allowedPaymentIds: getPaymentMethodsForShipping(shippingMethodId).map((p) => p.id),
    totals: computeTotals(draft, cart),
    deliveryType: shipping.type,
  };
}

export async function updatePickup(id: string, point: PickupPointDraft) {
  const draft = await getCheckoutDraft(id);
  if (!draft) return { ok: false as const, error: 'Session not found' };
  const shipping = draft.shippingMethodId ? getShippingMethod(draft.shippingMethodId) : null;
  if (!shipping || (shipping.type !== 'pickup' && shipping.type !== 'store')) {
    return { ok: false as const, error: 'Wybrana dostawa nie wymaga punktu odbioru' };
  }
  if (!point.id || !point.name) {
    return { ok: false as const, error: 'Nieprawidłowy punkt odbioru' };
  }
  draft.pickupPoint = point;
  await saveCheckoutDraft(draft);
  const cart = await loadCartBySessionId(draft.cartSessionId);
  return { ok: true as const, totals: computeTotals(draft, cart) };
}

export async function updateCourierAddress(id: string, address: CourierAddressDraft) {
  const draft = await getCheckoutDraft(id);
  if (!draft) return { ok: false as const, error: 'Session not found' };
  const shipping = draft.shippingMethodId ? getShippingMethod(draft.shippingMethodId) : null;
  if (!shipping || shipping.type !== 'courier') {
    return { ok: false as const, error: 'Wybrana dostawa nie jest kurierem' };
  }
  draft.courierAddress = address;
  draft.pickupPoint = undefined;
  await saveCheckoutDraft(draft);
  const cart = await loadCartBySessionId(draft.cartSessionId);
  return { ok: true as const, totals: computeTotals(draft, cart) };
}

export async function updatePaymentMethod(id: string, paymentMethodId: string) {
  const draft = await getCheckoutDraft(id);
  if (!draft) return { ok: false as const, error: 'Session not found' };
  if (!draft.shippingMethodId) {
    return { ok: false as const, error: 'Najpierw wybierz dostawę' };
  }
  const payment = getPaymentMethod(paymentMethodId);
  if (!payment || payment.disabled) {
    return { ok: false as const, error: 'Nieprawidłowa metoda płatności' };
  }
  const allowed = getPaymentMethodsForShipping(draft.shippingMethodId);
  if (!allowed.some((p) => p.id === paymentMethodId)) {
    return { ok: false as const, error: 'Płatność niedostępna dla tej dostawy' };
  }
  draft.paymentMethodId = paymentMethodId;
  await saveCheckoutDraft(draft);
  return { ok: true as const, paymentMethod: paymentMethodId, paymentMethodLabel: payment.name };
}

export async function updateInvoice(id: string, invoice: InvoiceDraft) {
  const draft = await getCheckoutDraft(id);
  if (!draft) return { ok: false as const, error: 'Session not found' };
  if (!invoice.firstname?.trim() || !invoice.lastname?.trim() || !invoice.email || !invoice.phone) {
    return { ok: false as const, error: 'Uzupełnij dane do rachunku' };
  }
  draft.invoice = invoice;
  await saveCheckoutDraft(draft);
  return { ok: true as const };
}

export function draftToOrderBody(draft: CheckoutDraft, cart: CartSnapshot): Record<string, unknown> | null {
  const shipping = draft.shippingMethodId ? getShippingMethod(draft.shippingMethodId) : null;
  if (!shipping || !draft.paymentMethodId || !draft.invoice) return null;

  const inv = draft.invoice;
  let address = '';
  let city = '';
  let zip = '';

  if (shipping.type === 'courier' && draft.courierAddress) {
    address = `${draft.courierAddress.street} ${draft.courierAddress.number}`.trim();
    city = draft.courierAddress.city;
    zip = draft.courierAddress.zip;
  } else if (draft.pickupPoint) {
    address = `${draft.pickupPoint.name}, ${draft.pickupPoint.address}`;
    city = draft.pickupPoint.city ?? '-';
    zip = draft.pickupPoint.zip ?? '00-000';
  } else {
    return null;
  }

  return {
    firstname: inv.firstname,
    lastname: inv.lastname,
    phone: inv.phone,
    email: inv.email,
    address,
    city,
    zip,
    country: 'PL',
    paymentMethod: draft.paymentMethodId,
    shippingMethodId: draft.shippingMethodId,
    shippingMethodName: shipping.name,
    shippingPrice: shipping.price,
    deliveryType: shipping.type,
    pickupPointId: draft.pickupPoint?.id,
    pickupPointJson: draft.pickupPoint,
    invoiceIsCompany: inv.invoiceIsCompany ?? false,
    companyName: inv.companyName,
    vatin: inv.vatin,
    regon: inv.regon,
  };
}

export async function commitDraft(
  draftId: string,
  opts: { visitorId?: string; clearCart?: boolean },
) {
  const draft = await getCheckoutDraft(draftId);
  if (!draft) return { ok: false as const, error: 'Session not found' };
  if (draft.orderId) {
    return {
      ok: true as const,
      orderId: draft.orderId,
      reference: draft.orderReference ?? '',
      alreadyCommitted: true,
    };
  }

  const cart = await loadCartBySessionId(draft.cartSessionId);
  const body = draftToOrderBody(draft, cart);
  if (!body) {
    return { ok: false as const, error: 'Uzupełnij dostawę, punkt odbioru i dane do rachunku' };
  }

  const validation = validateOrderSubmit(body, cart.totalPrice);
  if (!validation.ok) return { ok: false as const, error: validation.error };

  const data = validation.data;
  const amount = orderTotalWithShipping(cart.totalPrice, data.shippingPrice);
  if (!cart.items.length || amount <= 0) {
    return { ok: false as const, error: 'Koszyk jest pusty' };
  }

  let dbVisitorId: string | undefined;
  if (opts.visitorId) {
    const v = await prisma.visitor.findUnique({ where: { uuid: opts.visitorId } });
    if (v) dbVisitorId = v.id;
  }

  const reference = randomRef(9);
  const pickupJson = data.pickupPointJson ? JSON.stringify(data.pickupPointJson) : null;

  const order = await prisma.order.create({
    data: {
      visitorId: dbVisitorId,
      reference,
      name: `${data.firstname} ${data.lastname}`.trim(),
      firstname: data.firstname,
      lastname: data.lastname,
      phone: data.phone,
      email: data.email,
      address: data.address,
      city: data.city,
      zip: data.zip,
      country: data.country,
      paymentMethod: data.paymentMethod,
      shippingMethodId: data.shippingMethodId,
      shippingMethodName: data.shippingMethodName ?? formatShippingLabel(data.shippingMethodId),
      shippingPrice: data.shippingPrice,
      deliveryType: data.deliveryType,
      pickupPointId: data.pickupPointId ?? data.pickupPointJson?.id,
      pickupPointJson: pickupJson,
      invoiceIsCompany: data.invoiceIsCompany ?? false,
      companyName: data.companyName,
      vatin: data.vatin,
      regon: data.regon,
      total: amount,
      currency: cart.currency || 'PLN',
      items: {
        create: cart.items.map((item) => ({
          productId: item.productId,
          name: item.name,
          price: item.price,
          qty: item.quantity,
          size: item.size,
          color: item.color,
          imageUrl: item.imageUrl,
        })),
      },
    },
  });

  draft.orderId = order.id;
  draft.orderReference = reference;
  await saveCheckoutDraft(draft);

  if (opts.clearCart !== false) {
    const redis = getRedis();
    await redis.del(REDIS_KEYS.cart(draft.cartSessionId));
  }

  return { ok: true as const, orderId: order.id, reference };
}

export async function payDraft(
  draftId: string,
  opts?: { webhookUrl?: string; returnUrlSuccess?: string; returnUrlFailure?: string },
) {
  const draft = await getCheckoutDraft(draftId);
  if (!draft) return { ok: false as const, error: 'Session not found' };
  if (!draft.orderId) {
    return { ok: false as const, error: 'Najpierw złóż zamówienie (commit)' };
  }
  if (!draft.paymentMethodId) {
    return { ok: false as const, error: 'Brak metody płatności' };
  }

  if (opts?.webhookUrl) {
    const redis = getRedis();
    await redis.setex(
      REDIS_KEYS.botWebhook(draft.orderId),
      60 * 60 * 24 * 7,
      JSON.stringify({ webhookUrl: opts.webhookUrl.slice(0, 512) }),
    );
  }

  if (config.checkout.payEnabled) {
    const token = await createPaymentToken(draft.orderId);
    draft.paymentStatus = 'pending';
    await saveCheckoutDraft(draft);
    return {
      ok: true as const,
      status: 'pending' as const,
      orderId: draft.orderId,
      reference: draft.orderReference,
      paymentMethodId: draft.paymentMethodId,
      paymentMethodLabel: formatPaymentLabel(draft.paymentMethodId),
      redirectUrl: buildFelopayRedirectUrl(token),
      message: 'Przekierowanie do bramki płatności',
    };
  }

  draft.paymentStatus = 'pending';
  await saveCheckoutDraft(draft);
  return {
    ok: true as const,
    status: 'pending' as const,
    orderId: draft.orderId,
    reference: draft.orderReference,
    paymentMethodId: draft.paymentMethodId,
    paymentMethodLabel: formatPaymentLabel(draft.paymentMethodId),
    message:
      'Płatność oczekuje na integrację backendu (ustaw CHECKOUT_PAY_ENABLED=true lub podłącz PSP w POST .../pay)',
  };
}

export async function createBotSessionFromItems(
  items: Array<Record<string, unknown>>,
  currency = 'PLN',
): Promise<{ checkoutSessionId: string; cartSessionId: string }> {
  const cartSessionId = `bot-${uuidv4()}`;
  const parsed: CartItem[] = items.slice(0, 50).map((row, i) => ({
    id: `bot-${i}`,
    productId: String(row.productId ?? `bot-${i}`),
    name: String(row.name ?? 'Product').slice(0, 200),
    price: Math.max(0, Number(row.price ?? 0)),
    currency,
    quantity: Math.max(1, Math.min(99, Number(row.qty ?? row.quantity ?? 1))),
    size: row.size ? String(row.size) : undefined,
    color: row.color ? String(row.color) : undefined,
    imageUrl: row.imageUrl ? String(row.imageUrl) : undefined,
  }));
  await saveBotCart(cartSessionId, parsed);
  const { checkoutSessionId } = await createSession(cartSessionId);
  return { checkoutSessionId, cartSessionId };
}
