import { NextResponse } from 'next/server';
import bcrypt from 'bcryptjs';
import { timingSafeEqual } from 'crypto';
import { eq, and, between, asc, sql, count } from 'drizzle-orm';
import { db, users, restaurants, branches, orders, bookings, aiAgentConfigs, plans, subscriptions } from '@server/db/drizzle';
import { initDatabase } from '@server/db/init';
import { withErrorHandler } from '@server/middleware/withErrorHandler';
import { seedNoidaMenu } from '@server/data/noida-menu-seed';
import { getSession } from '@server/auth';

import { childLogger } from '@server/logger';
const log = childLogger('route.auth.seed_demo_users');

function safeCompare(a: string, b: string): boolean {
  const ab = Buffer.from(a);
  const bb = Buffer.from(b);
  if (ab.length !== bb.length) {
    timingSafeEqual(ab, ab);
    return false;
  }
  return timingSafeEqual(ab, bb);
}

const DEMO_ACCOUNTS = [
  { email: 'demo@restroagent.com', password: 'demo123456', name: 'Demo Owner', restaurantName: 'The Rustic Fork', role: 'owner' as const },
  { email: 'admin@restroagent.com', password: 'admin123456', name: 'Admin User', role: 'superadmin' as const },
];

type AccountResult = {
  email: string;
  status: 'created' | 'already_exists' | 'error';
  loginVerified?: boolean;
  error?: string;
};

async function seedDemoBranchData(restaurantId: string, mainBranchId: string, noidaBranchId: string | null) {
  await db.delete(orders).where(
    and(eq(orders.restaurantId, restaurantId), between(orders.orderNumber, 90001, 90006))
  );

  const demoGuests = ['Oliver Bennett', 'Rachel Turner', 'Neha Kapoor', 'Vikram Singh'];
  for (const guest of demoGuests) {
    await db.delete(bookings).where(
      and(eq(bookings.restaurantId, restaurantId), eq(bookings.guestName, guest))
    );
  }

  const today = new Date();
  const tomorrow = new Date(today); tomorrow.setDate(today.getDate() + 1);
  const in2Days = new Date(today); in2Days.setDate(today.getDate() + 2);
  const ago5 = new Date(today); ago5.setDate(today.getDate() - 5);
  const ago7 = new Date(today); ago7.setDate(today.getDate() - 7);

  const fmt = (d: Date) => d.toISOString().slice(0, 10);

  const mainOrders = [
    {
      orderNumber: 90001, branchId: mainBranchId, status: 'completed' as const, channel: 'chat' as const, deliveryType: 'dine-in',
      customerName: 'Sarah Mitchell', customerPhone: '+1-555-0101', tableNumber: '4',
      items: [
        { name: 'Grilled Atlantic Salmon', quantity: 1, price: 24.00 },
        { name: 'Caesar Salad', quantity: 1, price: 9.00 },
      ],
      subtotal: '33.00', tax: '2.64', total: '35.64',
    },
    {
      orderNumber: 90002, branchId: mainBranchId, status: 'pending' as const, channel: 'chat' as const, deliveryType: 'takeaway',
      customerName: 'James Park', customerPhone: '+1-555-0102', tableNumber: null,
      items: [
        { name: 'Beef Burger', quantity: 1, price: 14.50 },
        { name: 'Seasoned Fries', quantity: 1, price: 4.00 },
      ],
      subtotal: '18.50', tax: '1.48', total: '19.98',
    },
    {
      orderNumber: 90003, branchId: mainBranchId, status: 'completed' as const, channel: 'chat' as const, deliveryType: 'delivery',
      customerName: 'Emily Johnson', customerPhone: '+1-555-0103', tableNumber: null,
      deliveryAddress: '42 Maple Street, Downtown',
      items: [
        { name: 'Pasta Carbonara', quantity: 1, price: 18.00 },
        { name: 'Tiramisu', quantity: 2, price: 8.50 },
        { name: 'Focaccia Bread', quantity: 1, price: 6.00 },
        { name: 'Sparkling Water', quantity: 2, price: 3.75 },
      ],
      subtotal: '48.50', tax: '3.88', total: '52.38',
    },
  ];

  for (const o of mainOrders) {
    await db.insert(orders).values({
      restaurantId,
      branchId: o.branchId,
      customerName: o.customerName,
      customerPhone: o.customerPhone,
      items: o.items,
      status: o.status,
      channel: o.channel,
      deliveryType: o.deliveryType,
      tableNumber: o.tableNumber,
      deliveryAddress: (o as { deliveryAddress?: string }).deliveryAddress ?? null,
      subtotal: o.subtotal,
      tax: o.tax,
      total: o.total,
      orderNumber: o.orderNumber,
    });
  }

  await db.insert(bookings).values({
    restaurantId, branchId: mainBranchId, guestName: 'Oliver Bennett', guestPhone: '+1-555-0111',
    partySize: 2, bookingDate: fmt(tomorrow), bookingTime: '19:00', status: 'confirmed', channel: 'chat', zone: 'Main Hall',
  });
  await db.insert(bookings).values({
    restaurantId, branchId: mainBranchId, guestName: 'Rachel Turner', guestPhone: '+1-555-0112',
    partySize: 4, bookingDate: fmt(ago7), bookingTime: '20:30', status: 'completed', channel: 'chat', zone: 'Terrace', notes: 'Anniversary dinner',
  });

  if (noidaBranchId) {
    const noidaOrders = [
      {
        orderNumber: 90004, branchId: noidaBranchId, status: 'completed' as const, channel: 'chat' as const, deliveryType: 'dine-in',
        customerName: 'Arjun Sharma', customerPhone: '+91-98765-43210', tableNumber: '2',
        items: [
          { name: 'Butter Chicken', quantity: 1, price: 14.00 },
          { name: 'Garlic Naan', quantity: 2, price: 2.50 },
          { name: 'Sweet Lassi', quantity: 1, price: 3.50 },
        ],
        subtotal: '22.50', tax: '1.80', total: '24.30',
      },
      {
        orderNumber: 90005, branchId: noidaBranchId, status: 'pending' as const, channel: 'chat' as const, deliveryType: 'takeaway',
        customerName: 'Priya Mehta', customerPhone: '+91-98765-11223', tableNumber: null,
        items: [
          { name: 'Paneer Tikka', quantity: 1, price: 12.00 },
          { name: 'Mint Chutney', quantity: 1, price: 2.00 },
        ],
        subtotal: '14.00', tax: '1.12', total: '15.12',
      },
      {
        orderNumber: 90006, branchId: noidaBranchId, status: 'completed' as const, channel: 'chat' as const, deliveryType: 'dine-in',
        customerName: 'Ravi Gupta', customerPhone: '+91-98765-99887', tableNumber: '5',
        items: [
          { name: 'Dal Makhani', quantity: 1, price: 10.00 },
          { name: 'Jeera Rice', quantity: 1, price: 5.00 },
          { name: 'Raita', quantity: 1, price: 3.00 },
        ],
        subtotal: '18.00', tax: '1.44', total: '19.44',
      },
    ];

    for (const o of noidaOrders) {
      await db.insert(orders).values({
        restaurantId,
        branchId: o.branchId,
        customerName: o.customerName,
        customerPhone: o.customerPhone,
        items: o.items,
        status: o.status,
        channel: o.channel,
        deliveryType: o.deliveryType,
        tableNumber: o.tableNumber,
        subtotal: o.subtotal,
        tax: o.tax,
        total: o.total,
        orderNumber: o.orderNumber,
      });
    }

    await db.insert(bookings).values({
      restaurantId, branchId: noidaBranchId, guestName: 'Neha Kapoor', guestPhone: '+91-98765-55501',
      partySize: 6, bookingDate: fmt(in2Days), bookingTime: '13:00', status: 'confirmed', channel: 'chat', zone: 'Ground Floor',
    });
    await db.insert(bookings).values({
      restaurantId, branchId: noidaBranchId, guestName: 'Vikram Singh', guestPhone: '+91-98765-77742',
      partySize: 3, bookingDate: fmt(ago5), bookingTime: '19:30', status: 'completed', channel: 'chat', zone: 'Rooftop', notes: 'Birthday celebration',
    });
  }
}

async function upsertDemoSubscription(restaurantId: string) {
  const planRows = await db
    .select({ id: plans.id })
    .from(plans)
    .where(eq(plans.name, 'Enterprise'))
    .limit(1);
  const enterprisePlan = planRows[0];

  if (!enterprisePlan) {
    log.warn('Enterprise plan not found; activating subscription without plan assignment');
  }

  if (enterprisePlan) {
    await db
      .insert(subscriptions)
      .values({ restaurantId, planId: enterprisePlan.id, status: 'active' })
      .onConflictDoUpdate({
        target: subscriptions.restaurantId,
        set: { planId: enterprisePlan.id, status: 'active' },
      });
  } else {
    await db
      .insert(subscriptions)
      .values({ restaurantId, status: 'active' })
      .onConflictDoUpdate({
        target: subscriptions.restaurantId,
        set: { status: 'active' },
      });
  }
}

export const POST = withErrorHandler(async (req: Request) => {
  // When DEMO_MODE is on, the operator has explicitly opted into a public demo
  // deployment, so we allow self-bootstrap seeding without SEED_SECRET — this
  // is what powers the launch buttons on /demo.
  const demoModeOn = process.env.NEXT_PUBLIC_DEMO_MODE === 'on';
  if (process.env.NODE_ENV === 'production' && !demoModeOn) {
    const secret = process.env.SEED_SECRET;
    if (!secret) {
      return NextResponse.json(
        { error: 'SEED_SECRET is not configured. Set it in your .env file and pass it via the x-seed-secret header.' },
        { status: 503 }
      );
    }
    const provided = req.headers.get('x-seed-secret') ?? '';
    if (!safeCompare(provided, secret)) {
      return NextResponse.json({ error: 'Unauthorized: invalid or missing x-seed-secret header' }, { status: 401 });
    }
  }

  const countRows = await db.select({ value: count() }).from(users);
  const hasExistingUsers = (countRows[0]?.value ?? 0) > 0;

  if (hasExistingUsers) {
    const session = await getSession();
    if (!session) {
      return NextResponse.json({ error: 'Authentication required: this system is already initialized. Sign in as a superadmin to re-seed.' }, { status: 401 });
    }
    if (session.role !== 'superadmin') {
      return NextResponse.json({ error: 'Forbidden: superadmin role required to re-seed an initialized system.' }, { status: 403 });
    }
  }

  await initDatabase();
  const results: AccountResult[] = [];

  for (const account of DEMO_ACCOUNTS) {
    try {
      const existingRows = await db
        .select({ id: users.id, role: users.role, restaurantId: users.restaurantId })
        .from(users)
        .where(eq(users.email, account.email));
      const existing = existingRows[0];

      if (existing) {
        if (existing.role !== account.role) {
          await db.update(users).set({ role: account.role }).where(eq(users.id, existing.id));
        }
        if (existing.restaurantId) {
          await upsertDemoSubscription(existing.restaurantId);

          const branchRows = await db
            .select({ id: branches.id, name: branches.name })
            .from(branches)
            .where(eq(branches.restaurantId, existing.restaurantId))
            .orderBy(asc(branches.createdAt));
          const mainBranch = branchRows.find(b => b.name.includes('Main Branch'));
          const noidaBranch = branchRows.find(b => b.name === 'Noida');
          if (mainBranch) {
            await seedDemoBranchData(existing.restaurantId, mainBranch.id, noidaBranch?.id ?? null);
          }
        }
        results.push({ email: account.email, status: 'already_exists', loginVerified: true });
        continue;
      }

      const passwordHash = await bcrypt.hash(account.password, 12);

      if (account.role === 'superadmin') {
        await db.insert(users).values({
          role: account.role,
          name: account.name,
          email: account.email,
          passwordHash,
          isActive: true,
        });
        results.push({ email: account.email, status: 'created', loginVerified: true });
        continue;
      }

      const restaurantName = (account as { restaurantName?: string }).restaurantName ?? account.name;

      const restRows = await db
        .insert(restaurants)
        .values({ name: restaurantName, ownerId: sql`gen_random_uuid()`, plan: 'starter', status: 'active' })
        .returning();
      const restaurant = restRows[0];
      if (!restaurant) throw new Error('Failed to create restaurant');

      const brRows = await db
        .insert(branches)
        .values({ restaurantId: restaurant.id, name: restaurantName + ' — Main Branch', isActive: true })
        .returning();
      const branch = brRows[0];
      if (!branch) throw new Error('Failed to create branch');

      const uRows = await db
        .insert(users)
        .values({
          restaurantId: restaurant.id,
          branchId: branch.id,
          role: account.role,
          name: account.name,
          email: account.email,
          passwordHash,
          isActive: true,
        })
        .returning();
      const user = uRows[0];
      if (!user) throw new Error('Failed to create user');

      await db.update(restaurants).set({ ownerId: user.id }).where(eq(restaurants.id, restaurant.id));

      await db.insert(aiAgentConfigs).values({
        restaurantId: restaurant.id,
        branchId: branch.id,
        voiceEnabled: true,
        chatEnabled: true,
        model: 'gpt-4o',
      });

      let noidaBranchId: string | null = null;
      if (restaurantName === 'The Rustic Fork') {
        const nRows = await db
          .insert(branches)
          .values({ restaurantId: restaurant.id, name: 'Noida', isActive: true })
          .returning({ id: branches.id });
        const noidaBranch = nRows[0];
        if (noidaBranch) {
          noidaBranchId = noidaBranch.id;
          await seedNoidaMenu(restaurant.id, noidaBranch.id);
        }
      }

      await upsertDemoSubscription(restaurant.id);
      await seedDemoBranchData(restaurant.id, branch.id, noidaBranchId);

      results.push({ email: account.email, status: 'created', loginVerified: true });
    } catch (err: unknown) {
      const msg = err instanceof Error ? err.message : 'Unknown error';
      results.push({ email: account.email, status: 'error', error: msg });
    }
  }

  const allReady = results.every(r => r.status === 'created' || r.status === 'already_exists');
  return NextResponse.json({
    results,
    needs_confirmation: false,
    note: allReady ? 'All demo accounts are ready.' : null,
  });
});
