import { NextResponse } from 'next/server';
import bcrypt from 'bcryptjs';
import { eq, or, asc, sql } from 'drizzle-orm';
import { db, users, restaurants, branches, aiAgentConfigs, plans, subscriptions } from '@server/db/drizzle';
import { createSessionToken, COOKIE_NAME } from '@server/auth';
import { initDatabase } from '@server/db/init';
import { generateDisplayId } from '@server/services/admin.service';
import { withErrorHandler } from '@server/middleware/withErrorHandler';
import { withValidation, ParsedRequest } from '@server/middleware/withValidation';
import { signUpSchema, SignUpInput } from '@server/validators/auth.validator';
import { ConflictError } from '@server/errors';

const signUp = withErrorHandler(
  withValidation(signUpSchema, async (req: ParsedRequest<SignUpInput>) => {
    await initDatabase();
    const { email, password, ownerName, restaurantName, plan } = req.parsedBody;

    const existingRows = await db
      .select({ id: users.id })
      .from(users)
      .where(eq(users.email, email.toLowerCase().trim()));
    if (existingRows.length > 0) throw new ConflictError('An account with this email already exists');

    const passwordHash = await bcrypt.hash(password, 12);
    const name = ownerName || email.split('@')[0];
    const restName = restaurantName || 'My Restaurant';

    let displayId = '';
    for (let attempt = 0; attempt < 10; attempt++) {
      const candidate = generateDisplayId();
      const conflictRows = await db
        .select({ id: restaurants.id })
        .from(restaurants)
        .where(eq(restaurants.displayId, candidate));
      if (conflictRows.length === 0) { displayId = candidate; break; }
    }
    if (!displayId) displayId = generateDisplayId();

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

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

    const userRows = await db
      .insert(users)
      .values({
        restaurantId: restaurant.id,
        branchId: branch.id,
        role: 'owner',
        name,
        email: email.toLowerCase().trim(),
        passwordHash,
        isActive: true,
      })
      .returning();
    const user = userRows[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',
    });

    const planSlug = plan || 'starter';
    let planRows = await db
      .select({ id: plans.id })
      .from(plans)
      .where(or(eq(sql`LOWER(${plans.name})`, sql`LOWER(${planSlug})`), eq(sql`LOWER(${plans.slug})`, sql`LOWER(${planSlug})`)))
      .limit(1);
    if (planRows.length === 0) {
      planRows = await db.select({ id: plans.id }).from(plans).orderBy(asc(plans.priceMonthly)).limit(1);
    }
    const planRow = planRows[0];

    await db
      .insert(subscriptions)
      .values({
        restaurantId: restaurant.id,
        planId: planRow?.id ?? null,
        status: 'trial',
        trialStart: sql`NOW()`,
        trialEnd: sql`NOW() + INTERVAL '14 days'`,
      })
      .onConflictDoUpdate({
        target: subscriptions.restaurantId,
        set: {
          planId: sql`EXCLUDED.plan_id`,
          status: 'trial',
          trialStart: sql`EXCLUDED.trial_start`,
          trialEnd: sql`EXCLUDED.trial_end`,
        },
      });

    const token = await createSessionToken({
      userId: user.id,
      email: user.email,
      role: user.role!,
      restaurantId: user.restaurantId!,
      branchId: user.branchId!,
    });

    const response = NextResponse.json({
      user: {
        id: user.id,
        email: user.email,
        name: user.name,
        role: user.role,
        restaurant_id: user.restaurantId,
        branch_id: user.branchId,
        restaurants: { id: restaurant.id, name: restaurant.name },
        branches: { id: branch.id, name: branch.name },
      },
    });

    response.cookies.set(COOKIE_NAME, token, {
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'lax',
      maxAge: 7 * 24 * 60 * 60,
      path: '/',
    });

    return response;
  })
);

export const POST = signUp;
