import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';

import { config } from '../config';
import { prisma } from '../db/client';
import { getRedis, REDIS_KEYS } from '../redis';
import {
  getCatalogResponse,
  getPaymentMethodsForShipping,
  getShippingMethod,
  getShippingMethods,
  formatPaymentLabel,
  formatShippingLabel,
} from '../checkout/catalog';
import {
  commitDraft,
  createBotSessionFromItems,
  createSession,
  getSessionView,
  loadCartBySessionId,
  payDraft,
  updateCourierAddress,
  updateInvoice,
  updatePaymentMethod,
  updatePickup,
  updateShipping,
} from '../checkout/draftService';
import { orderTotalWithShipping, validateOrderSubmit } from '../checkout/orderSubmit';
import { buildFelopayRedirectUrl, createPaymentToken, randomRef } from '../intercept/payment';
import type { CartItem } from '../intercept/cart';

function requireBotApiKey(
  request: FastifyRequest,
  reply: FastifyReply,
  done: () => void,
): void {
  const key = config.bot.apiKey;
  if (!key) {
    reply.status(503).send({ ok: false, error: 'Bot API disabled' });
    return;
  }
  const provided = String(request.headers['x-api-key'] ?? '');
  const authHeader = String(request.headers.authorization ?? '');
  const bearer = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : '';
  if ((provided || bearer) !== key) {
    reply.status(401).send({ ok: false, error: 'Unauthorized' });
    return;
  }
  done();
}

function sanitizeOrder(order: {
  id: number;
  reference: string;
  status: string;
  total: number;
  currency: string;
  paymentMethod: string;
  shippingMethodId: string | null;
  createdAt: Date;
}) {
  return {
    orderId: order.id,
    reference: order.reference,
    status: order.status,
    total: order.total,
    currency: order.currency,
    paymentMethod: order.paymentMethod,
    shippingMethodId: order.shippingMethodId,
    createdAt: order.createdAt.toISOString(),
  };
}

export async function registerBotApiRoutes(app: FastifyInstance): Promise<void> {
  if (!config.bot.apiKey) {
    app.log.warn('[bot] BOT_API_KEY not set — /api/bot/v1 routes disabled');
    return;
  }

  await app.register(
    async (botApp) => {
      botApp.addHook('onRequest', requireBotApiKey);

      botApp.get('/payment-methods', async (_request, reply) => {
        const catalog = getCatalogResponse();
        reply.send({
          ok: true,
          methods: catalog.payments.map((m) => ({
            id: m.id,
            name: m.name,
            description: m.description,
            logoUrl: m.logoUrl,
            availableWithGiftCards: m.availableWithGiftCards ?? false,
            disabled: m.disabled ?? false,
          })),
        });
      });

      botApp.get<{ Querystring: { cartTotal?: string; shippingMethodId?: string } }>(
        '/shipping-methods',
        async (request, reply) => {
          const cartTotal = parseFloat(request.query.cartTotal ?? '0') || 0;
          const shippingId = request.query.shippingMethodId;
          if (shippingId) {
            reply.send({
              ok: true,
              payments: getPaymentMethodsForShipping(shippingId),
            });
            return;
          }
          reply.send({
            ok: true,
            methods: getShippingMethods(cartTotal),
          });
        },
      );

      botApp.post('/checkout/sessions', async (request, reply) => {
        const body = (request.body ?? {}) as {
          cartSessionId?: string;
          items?: Array<Record<string, unknown>>;
          currency?: string;
        };
        if (Array.isArray(body.items) && body.items.length) {
          const { checkoutSessionId, cartSessionId } = await createBotSessionFromItems(
            body.items,
            String(body.currency ?? 'PLN'),
          );
          const view = await getSessionView(checkoutSessionId);
          reply.status(201).send({ ok: true, checkoutSessionId, cartSessionId, snapshot: view });
          return;
        }
        const cartSessionId = String(body.cartSessionId ?? '');
        if (!cartSessionId) {
          reply.status(400).send({ ok: false, error: 'cartSessionId or items required' });
          return;
        }
        const result = await createSession(cartSessionId);
        reply.status(201).send({ ok: true, ...result });
      });

      botApp.get<{ Params: { id: string } }>('/checkout/sessions/:id', async (request, reply) => {
        const view = await getSessionView(request.params.id);
        if (!view) {
          reply.status(404).send({ ok: false, error: 'Session not found' });
          return;
        }
        reply.send({ ok: true, snapshot: view });
      });

      botApp.put<{ Params: { id: string } }>(
        '/checkout/sessions/:id/shipping',
        async (request, reply) => {
          const body = request.body as { shippingMethodId?: string };
          if (!body?.shippingMethodId) {
            reply.status(400).send({ ok: false, error: 'shippingMethodId required' });
            return;
          }
          const result = await updateShipping(request.params.id, body.shippingMethodId);
          reply.status(result.ok ? 200 : 400).send(result);
        },
      );

      botApp.put<{ Params: { id: string } }>(
        '/checkout/sessions/:id/pickup',
        async (request, reply) => {
          const body = request.body as Record<string, string | undefined>;
          const result = await updatePickup(request.params.id, {
            id: String(body.id ?? ''),
            name: String(body.name ?? ''),
            address: String(body.address ?? ''),
            city: body.city,
            zip: body.zip,
          });
          reply.status(result.ok ? 200 : 400).send(result);
        },
      );

      botApp.put<{ Params: { id: string } }>(
        '/checkout/sessions/:id/courier-address',
        async (request, reply) => {
          const body = request.body as Record<string, string | undefined>;
          const result = await updateCourierAddress(request.params.id, {
            street: String(body.street ?? ''),
            number: String(body.number ?? ''),
            zip: String(body.zip ?? ''),
            city: String(body.city ?? ''),
          });
          reply.status(result.ok ? 200 : 400).send(result);
        },
      );

      botApp.put<{ Params: { id: string } }>(
        '/checkout/sessions/:id/payment-method',
        async (request, reply) => {
          const body = request.body as { paymentMethodId?: string };
          if (!body?.paymentMethodId) {
            reply.status(400).send({ ok: false, error: 'paymentMethodId required' });
            return;
          }
          const result = await updatePaymentMethod(request.params.id, body.paymentMethodId);
          reply.status(result.ok ? 200 : 400).send(result);
        },
      );

      botApp.put<{ Params: { id: string } }>(
        '/checkout/sessions/:id/invoice',
        async (request, reply) => {
          const body = request.body as Record<string, unknown>;
          const result = await updateInvoice(request.params.id, {
            firstname: String(body.firstname ?? ''),
            lastname: String(body.lastname ?? ''),
            email: String(body.email ?? ''),
            phone: String(body.phone ?? ''),
            invoiceIsCompany: Boolean(body.invoiceIsCompany),
            companyName: body.companyName ? String(body.companyName) : undefined,
            vatin: body.vatin ? String(body.vatin) : undefined,
            regon: body.regon ? String(body.regon) : undefined,
          });
          reply.status(result.ok ? 200 : 400).send(result);
        },
      );

      botApp.post<{ Params: { id: string } }>(
        '/checkout/sessions/:id/commit',
        async (request, reply) => {
          const result = await commitDraft(request.params.id, { clearCart: false });
          reply.status(result.ok ? 200 : 400).send(result);
        },
      );

      botApp.post<{ Params: { id: string } }>(
        '/checkout/sessions/:id/pay',
        async (request, reply) => {
          const body = (request.body ?? {}) as { webhookUrl?: string };
          const result = await payDraft(request.params.id, body);
          reply.status(result.ok ? 200 : 400).send(result);
        },
      );

      /** @deprecated Use POST /checkout/sessions + step endpoints */
      botApp.post('/orders', async (request, reply) => {
        const body = (request.body ?? {}) as Record<string, unknown>;
        let cart: Awaited<ReturnType<typeof loadCartBySessionId>>;

        if (body.useCartSession) {
          const sid =
            String(body.cartSessionId ?? '') ||
            String((request.headers['x-cart-session'] as string) ?? '');
          if (!sid) {
            reply.status(400).send({ ok: false, error: 'cartSessionId required' });
            return;
          }
          cart = await loadCartBySessionId(sid);
        } else if (Array.isArray(body.items)) {
          const currency = String(body.currency ?? 'PLN');
          const items = (body.items as Array<Record<string, unknown>>).slice(0, 50).map(
            (row, i) => ({
              id: `bot-${i}-${String(row.productId ?? 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,
            }),
          );
          cart = {
            items,
            totalPrice: items.reduce((s, it) => s + it.price * it.quantity, 0),
            totalItems: items.reduce((s, it) => s + it.quantity, 0),
            currency: String(body.currency ?? 'PLN'),
          };
        } else {
          reply.status(400).send({ ok: false, error: 'items or useCartSession required' });
          return;
        }

        if (!cart.items.length || cart.totalPrice <= 0) {
          reply.status(400).send({ ok: false, error: 'Cart is empty' });
          return;
        }

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

        const data = validation.data;
        const total = orderTotalWithShipping(cart.totalPrice, data.shippingPrice);
        const reference = randomRef(9);
        const pickupJson = data.pickupPointJson
          ? JSON.stringify(data.pickupPointJson)
          : null;

        const order = await prisma.order.create({
          data: {
            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 ?? getShippingMethod(data.shippingMethodId)?.type,
            pickupPointId: data.pickupPointId ?? data.pickupPointJson?.id,
            pickupPointJson: pickupJson,
            invoiceIsCompany: data.invoiceIsCompany ?? false,
            companyName: data.companyName,
            vatin: data.vatin,
            regon: data.regon,
            notes: data.notes,
            total,
            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,
              })),
            },
          },
        });

        reply
          .header('X-Deprecated', 'Use POST /api/bot/v1/checkout/sessions')
          .status(201)
          .send({ ok: true, ...sanitizeOrder(order) });
      });

      botApp.get<{ Params: { id: string } }>('/orders/:id', async (request, reply) => {
        const id = parseInt(request.params.id, 10);
        if (!Number.isFinite(id)) {
          reply.status(400).send({ ok: false, error: 'Invalid order id' });
          return;
        }
        const order = await prisma.order.findUnique({ where: { id } });
        if (!order) {
          reply.status(404).send({ ok: false, error: 'Order not found' });
          return;
        }
        reply.send({ ok: true, ...sanitizeOrder(order) });
      });

      /** @deprecated Use POST /checkout/sessions/:id/commit then .../pay */
      botApp.post<{ Params: { id: string } }>('/orders/:id/pay', async (request, reply) => {
        const id = parseInt(request.params.id, 10);
        const body = (request.body ?? {}) as { webhookUrl?: string };

        const order = await prisma.order.findUnique({ where: { id } });
        if (!order) {
          reply.status(404).send({ ok: false, error: 'Order not found' });
          return;
        }

        if (body.webhookUrl) {
          const redis = getRedis();
          await redis.setex(
            REDIS_KEYS.botWebhook(order.id),
            60 * 60 * 24 * 7,
            JSON.stringify({ webhookUrl: body.webhookUrl.slice(0, 512) }),
          );
        }

        if (!config.checkout.payEnabled) {
          reply
            .header('X-Deprecated', 'Use POST /api/bot/v1/checkout/sessions/:id/pay')
            .send({
              ok: true,
              status: 'pending',
              orderId: order.id,
              reference: order.reference,
              paymentMethod: order.paymentMethod,
              paymentMethodLabel: formatPaymentLabel(order.paymentMethod),
              message: 'Płatność oczekuje na integrację (CHECKOUT_PAY_ENABLED=false)',
            });
          return;
        }

        const token = await createPaymentToken(order.id);
        reply.send({
          ok: true,
          status: 'pending',
          orderId: order.id,
          reference: order.reference,
          paymentMethod: order.paymentMethod,
          paymentMethodLabel: formatPaymentLabel(order.paymentMethod),
          redirectUrl: buildFelopayRedirectUrl(token),
        });
      });
    },
    { prefix: '/api/bot/v1' },
  );
}
