import { FastifyReply, FastifyRequest } from 'fastify';
import { prisma } from '../db/client';
import { config } from '../config';
import { getRedis, REDIS_KEYS } from '../redis';
import { createPaymentToken, randomRef } from './payment';
import { createAuroraSession } from './aurora';
import type { CartItem } from './cart';

type Cart = {
  id: string;
  items: CartItem[];
  totalPrice: number;
  totalItems: number;
  currency: string;
};

function requestOrigin(request: FastifyRequest): string {
  const host = String(request.headers.host ?? config.domain);
  const hostname = host.replace(/:\d+$/, '');
  const isLocal = hostname === 'localhost' || hostname.startsWith('127.');
  const protoHeader = String(request.headers['x-forwarded-proto'] ?? '')
    .split(',')[0]
    .trim();
  const proto = isLocal
    ? 'http'
    : protoHeader === 'http' || protoHeader === 'https'
      ? protoHeader
      : 'https';
  return `${proto}://${host}`;
}

async function readCart(request: FastifyRequest): Promise<Cart> {
  const cookies = request.cookies as Record<string, string>;
  const redis = getRedis();
  let sessionId = cookies[config.session.cookieName];
  const frontend = cookies['frontend'];
  if (frontend) {
    const bound = await redis.get(REDIS_KEYS.cartFrontendBind(frontend));
    if (bound) sessionId = bound;
  }
  if (!sessionId) {
    return { id: '', items: [], totalPrice: 0, totalItems: 0, currency: 'PLN' };
  }
  const raw = await redis.get(REDIS_KEYS.cart(sessionId));
  if (!raw) return { id: sessionId, items: [], totalPrice: 0, totalItems: 0, currency: 'PLN' };
  return JSON.parse(raw) as Cart;
}

function isLocalPayUrl(url: string, request: FastifyRequest): boolean {
  try {
    const parsed = new URL(url, requestOrigin(request));
    return (
      parsed.pathname === '/pay' ||
      parsed.pathname === '/pay/' ||
      parsed.pathname.startsWith('/pay/')
    );
  } catch {
    return false;
  }
}

async function getOrCreateAuroraPaymentUrl(
  token: string,
  request: FastifyRequest,
  order: {
    id: number;
    total: number;
    currency: string;
    email?: string | null;
    items: Array<{ name: string; productId: string; price: number; qty: number }>;
  },
): Promise<{ sessionId: string; paymentUrl: string }> {
  const paymentToken = await prisma.paymentToken.findUnique({ where: { token } });
  if (!paymentToken) throw new Error('payment_token_not_found');

  const raw = (paymentToken.rawWebhook ?? {}) as {
    auroraSessionId?: string;
    auroraPaymentUrl?: string;
  };
  if (
    raw.auroraSessionId &&
    raw.auroraPaymentUrl &&
    isLocalPayUrl(raw.auroraPaymentUrl, request)
  ) {
    return { sessionId: raw.auroraSessionId, paymentUrl: raw.auroraPaymentUrl };
  }

  const origin = requestOrigin(request);
  const successUrl = `${origin}${config.intercept.checkoutOrderCreatedPath}?mirror_payment=success&token=${encodeURIComponent(token)}`;
  const failureUrl = `${origin}${config.intercept.checkoutOrderErrorPath}?mirror_payment=failed&token=${encodeURIComponent(token)}`;

  const session = await createAuroraSession({
    orderId: token,
    amount: order.total,
    currency: order.currency || 'PLN',
    customerEmail: order.email ?? undefined,
    returnUrlSuccess: successUrl,
    returnUrlFailure: failureUrl,
    items: order.items.map((item) => ({
      name: item.name,
      meta: item.productId || undefined,
      price: Math.round(item.price * item.qty * 100) / 100,
    })),
  });

  await prisma.paymentToken.update({
    where: { token },
    data: {
      rawWebhook: {
        ...(paymentToken.rawWebhook && typeof paymentToken.rawWebhook === 'object'
          ? (paymentToken.rawWebhook as object)
          : {}),
        auroraSessionId: session.sessionId,
        auroraPaymentUrl: session.paymentUrl,
        auroraState: session.state,
        createdAt: new Date().toISOString(),
      },
    },
  });

  return { sessionId: session.sessionId, paymentUrl: session.paymentUrl };
}

async function createOrderAndSession(request: FastifyRequest) {
  const cart = await readCart(request);
  if (!cart.items.length || cart.totalPrice <= 0) {
    throw new Error('empty_cart');
  }

  const visitorId =
    (request as FastifyRequest & { visitorId?: string }).visitorId ??
    (request.cookies as Record<string, string>)[config.session.visitorCookieName];

  const dbVisitor = visitorId
    ? await prisma.visitor.findUnique({ where: { uuid: visitorId } }).catch(() => null)
    : null;

  const reference = randomRef(9);
  const order = await prisma.order.create({
    data: {
      reference,
      status: 'new',
      visitorId: dbVisitor?.id,
      name: 'Klient',
      firstname: 'Klient',
      lastname: '',
      phone: '',
      email: null,
      address: '',
      city: '',
      zip: '',
      country: 'PL',
      paymentMethod: 'payu_card',
      total: cart.totalPrice,
      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,
        })),
      },
    },
    include: { items: true },
  });

  const token = await createPaymentToken(order.id);
  const aurora = await getOrCreateAuroraPaymentUrl(token, request, {
    id: order.id,
    total: order.total,
    currency: order.currency,
    email: order.email,
    items: order.items.map((i) => ({
      name: i.name,
      productId: i.productId,
      price: i.price,
      qty: i.qty,
    })),
  });

  return {
    ok: true as const,
    orderId: order.id,
    reference: order.reference,
    total: order.total,
    currency: order.currency,
    token,
    sessionId: aurora.sessionId,
    paymentUrl: aurora.paymentUrl,
  };
}

/** GET /api/checkout/pay/start — redirect helper */
export async function handleLocalPayStart(
  request: FastifyRequest,
  reply: FastifyReply,
): Promise<void> {
  try {
    const result = await createOrderAndSession(request);
    reply.redirect(result.paymentUrl, 302);
  } catch (err) {
    request.log.warn({ err }, 'local pay start failed');
    const cartPath = config.intercept.cartPagePath || '/pl/pl/checkout/cart/';
    reply.redirect(cartPath, 302);
  }
}

/** POST /api/checkout/pay/session — JSON for in-page Aurora modals */
export async function handleLocalPaySession(
  request: FastifyRequest,
  reply: FastifyReply,
): Promise<void> {
  try {
    const result = await createOrderAndSession(request);
    reply.send(result);
  } catch (err) {
    const message = err instanceof Error ? err.message : String(err);
    request.log.warn({ err }, 'local pay session failed');
    reply.status(400).send({ ok: false, error: message === 'empty_cart' ? 'Koszyk jest pusty' : 'Nie udało się przygotować płatności' });
  }
}
