import { NextResponse } from 'next/server';
import { withErrorHandler, RouteContext } from '@server/middleware/withErrorHandler';
import { withAuth, AuthedRequest } from '@server/middleware/withAuth';
import { requirePlanFeature } from '@server/utils/features';
import { assertBranchAccess } from '@server/utils/branch-access';
import { getBranch } from '@server/services/branches.service';
import { listTables } from '@server/services/tables.service';
import { db } from '@server/db/drizzle';
import { sql } from 'drizzle-orm';
import { initDatabase } from '@server/db/init';
import QRCode from 'qrcode';
import PDFDocument from 'pdfkit';

interface QrStyleJson {
  fg?: string;
  bg?: string;
  margin?: number;
  ecc?: 'L' | 'M' | 'Q' | 'H';
  logo_data_url?: string | null;
  frame?: 'none' | 'scan-me' | null;
}

interface BranchRow {
  id: string;
  name: string;
  slug: string | null;
  qr_style_json: Record<string, unknown> | null;
}

interface TableRow {
  id: string;
  table_number: string;
  label: string | null;
  is_active: boolean;
}

interface RestaurantRow {
  name: string;
  slug: string;
}

const A6_WIDTH = 297.638;  // 105mm in points
const A6_HEIGHT = 419.528; // 148mm in points

function decodeDataUrl(dataUrl: string): Buffer | null {
  try {
    const m = /^data:([^;]+);base64,(.+)$/.exec(dataUrl.trim());
    if (!m) return null;
    return Buffer.from(m[2], 'base64');
  } catch {
    return null;
  }
}

export const GET = withErrorHandler(
  withAuth(async (req: AuthedRequest, ctx: RouteContext) => {
    const { id: branchId } = await ctx.params;
    const restaurantId = req.session.restaurantId!;
    assertBranchAccess(req.session, branchId);
    await requirePlanFeature(restaurantId, 'storefront');
    await initDatabase();

    const branch = (await getBranch(branchId, restaurantId)) as unknown as BranchRow;
    if (!branch.slug) {
      return NextResponse.json(
        { error: 'Branch slug is missing — open branch settings to publish the storefront first.' },
        { status: 400 }
      );
    }

    const { rows: restRows } = await db.execute(sql`
      SELECT name, slug FROM restaurants WHERE id = ${restaurantId} LIMIT 1
    `);
    const restaurant = restRows[0] as unknown as RestaurantRow | undefined;
    if (!restaurant?.slug) {
      return NextResponse.json({ error: 'Restaurant slug is missing.' }, { status: 400 });
    }

    const allTables = (await listTables(restaurantId, branchId)) as unknown as TableRow[];
    const tables = allTables.filter((t) => t.is_active);
    if (tables.length === 0) {
      return NextResponse.json({ error: 'No active tables to print.' }, { status: 400 });
    }

    const style: QrStyleJson = {
      fg: (branch.qr_style_json?.fg as string) ?? '#111827',
      bg: (branch.qr_style_json?.bg as string) ?? '#ffffff',
      margin: (branch.qr_style_json?.margin as number) ?? 2,
      ecc: ((branch.qr_style_json?.ecc as 'L' | 'M' | 'Q' | 'H') ?? 'M'),
      logo_data_url: (branch.qr_style_json?.logo_data_url as string | null) ?? null,
      frame: (branch.qr_style_json?.frame as 'none' | 'scan-me' | null) ?? 'none',
    };
    // When a logo is overlaid, force ECC=H so QR remains scannable (mirrors client utility behavior).
    if (style.logo_data_url) {
      style.ecc = 'H';
    }

    // Origin: use the request's origin so QR links work in any environment
    const origin = req.headers.get('x-forwarded-proto') && req.headers.get('host')
      ? `${req.headers.get('x-forwarded-proto')}://${req.headers.get('host')}`
      : new URL(req.url).origin;
    const baseUrl = `${origin}/r/${encodeURIComponent(restaurant.slug)}/${encodeURIComponent(branch.slug)}`;

    const logoBuf = style.logo_data_url ? decodeDataUrl(style.logo_data_url) : null;

    // Pre-generate QR buffers in parallel
    const qrBuffers = await Promise.all(
      tables.map(async (t) => {
        const url = `${baseUrl}?table=${encodeURIComponent(t.table_number)}`;
        const buf = await QRCode.toBuffer(url, {
          errorCorrectionLevel: style.ecc,
          margin: style.margin,
          width: 800,
          color: { dark: style.fg!, light: style.bg! },
        });
        return { table: t, buf };
      })
    );

    const doc = new PDFDocument({
      size: [A6_WIDTH, A6_HEIGHT],
      margin: 0,
      autoFirstPage: false,
      info: { Title: `${restaurant.name} — ${branch.name} QR codes` },
    });

    const chunks: Buffer[] = [];
    doc.on('data', (c: Buffer) => chunks.push(c));
    const done = new Promise<Buffer>((resolve, reject) => {
      doc.on('end', () => resolve(Buffer.concat(chunks)));
      doc.on('error', reject);
    });

    const PAGE_MARGIN = 18;
    const QR_SIZE = 200;
    const qrX = (A6_WIDTH - QR_SIZE) / 2;

    for (const { table, buf } of qrBuffers) {
      doc.addPage({ size: [A6_WIDTH, A6_HEIGHT], margin: 0 });

      // Outer frame
      if (style.frame === 'scan-me') {
        doc.lineWidth(1.5).strokeColor(style.fg ?? '#111827');
        doc.roundedRect(8, 8, A6_WIDTH - 16, A6_HEIGHT - 16, 12).stroke();
      }

      let y = PAGE_MARGIN + 6;
      doc.fillColor('#64748b').fontSize(9).font('Helvetica-Bold')
        .text(restaurant.name.toUpperCase(), 0, y, { width: A6_WIDTH, align: 'center', characterSpacing: 1 });
      y += 14;
      doc.fillColor('#0f172a').fontSize(11).font('Helvetica')
        .text(branch.name, 0, y, { width: A6_WIDTH, align: 'center' });
      y += 18;
      doc.fillColor('#0f172a').fontSize(28).font('Helvetica-Bold')
        .text(`Table ${table.table_number}`, 0, y, { width: A6_WIDTH, align: 'center' });
      y += 32;
      if (table.label) {
        doc.fillColor('#475569').fontSize(10).font('Helvetica-Oblique')
          .text(table.label, 0, y, { width: A6_WIDTH, align: 'center' });
        y += 14;
      }

      // QR background pad
      const padX = qrX - 8;
      const padY = y;
      doc.fillColor(style.bg ?? '#ffffff').rect(padX, padY, QR_SIZE + 16, QR_SIZE + 16).fill();
      doc.image(buf, qrX, padY + 8, { width: QR_SIZE, height: QR_SIZE });

      // Logo overlay (centered)
      if (logoBuf) {
        const logoSize = QR_SIZE * 0.22;
        const lx = qrX + (QR_SIZE - logoSize) / 2;
        const ly = padY + 8 + (QR_SIZE - logoSize) / 2;
        const padding = 4;
        doc.fillColor('#ffffff')
          .rect(lx - padding, ly - padding, logoSize + padding * 2, logoSize + padding * 2)
          .fill();
        try {
          doc.image(logoBuf, lx, ly, { width: logoSize, height: logoSize });
        } catch {
          /* skip bad logo */
        }
      }

      y = padY + QR_SIZE + 30;
      doc.fillColor('#0f172a').fontSize(16).font('Helvetica-Bold')
        .text('Scan to order', 0, y, { width: A6_WIDTH, align: 'center' });
      y += 20;
      doc.fillColor('#64748b').fontSize(9).font('Helvetica')
        .text('No app needed — pay in person.', 0, y, { width: A6_WIDTH, align: 'center' });
    }

    doc.end();
    const pdf = await done;

    return new NextResponse(new Uint8Array(pdf), {
      status: 200,
      headers: {
        'Content-Type': 'application/pdf',
        'Content-Disposition': `inline; filename="qr-${branch.slug}.pdf"`,
        'Cache-Control': 'no-store',
      },
    });
  })
);
