import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
import { db, dbGet, dbAll, users, tickets, events, payments, magicLinkTokens, userSessions, invoices, auditLogs, emailLogs, paymentOptions, legalPages, siteSettings } from '../db/index.js';
import { eq, desc, sql, and, gte, lte } from 'drizzle-orm';
import { requireAuth } from '../lib/auth.js';
import { authSessions } from '../db/auth-schema.js';
import { getNow, toDbDate } from '../lib/utils.js';

interface UserContext {
  id: string;
  email: string;
  name: string;
  role: string;
}

const usersRouter = new Hono<{ Variables: { user: UserContext } }>();

const updateUserSchema = z.object({
  name: z.string().min(2).optional(),
  email: z.string().email().optional(),
  phone: z.string().optional(),
  role: z.enum(['admin', 'organizer', 'staff', 'marketing', 'user']).optional(),
  languagePreference: z.enum(['en', 'es']).optional(),
  accountStatus: z.enum(['active', 'unclaimed', 'suspended']).optional(),
});

// Get all users (admin only)
usersRouter.get('/', requireAuth(['admin']), async (c) => {
  const role = c.req.query('role');
  const search = c.req.query('search');
  const accountStatus = c.req.query('accountStatus');
  const hasBookings = c.req.query('hasBookings'); // 'yes' | 'no'
  const registeredAfter = c.req.query('registeredAfter');
  const registeredBefore = c.req.query('registeredBefore');
  const eventId = c.req.query('eventId');
  const page = Math.max(parseInt(c.req.query('page') || '1', 10) || 1, 1);
  const pageSize = Math.min(Math.max(parseInt(c.req.query('pageSize') || '50', 10) || 50, 1), 200);

  const conditions: any[] = [];
  if (role) conditions.push(eq((users as any).role, role));
  if (accountStatus) conditions.push(eq((users as any).accountStatus, accountStatus));
  if (registeredAfter) conditions.push(gte((users as any).createdAt, toDbDate(registeredAfter)));
  if (registeredBefore) conditions.push(lte((users as any).createdAt, toDbDate(registeredBefore)));
  if (search) {
    const like = `%${search.toLowerCase()}%`;
    conditions.push(sql`(
      LOWER(${(users as any).name}) LIKE ${like}
      OR LOWER(${(users as any).email}) LIKE ${like}
      OR LOWER(COALESCE(${(users as any).phone}, '')) LIKE ${like}
    )`);
  }
  if (hasBookings === 'yes') {
    conditions.push(sql`EXISTS (SELECT 1 FROM tickets WHERE tickets.user_id = ${(users as any).id})`);
  } else if (hasBookings === 'no') {
    conditions.push(sql`NOT EXISTS (SELECT 1 FROM tickets WHERE tickets.user_id = ${(users as any).id})`);
  }
  if (eventId) {
    conditions.push(sql`EXISTS (SELECT 1 FROM tickets WHERE tickets.user_id = ${(users as any).id} AND tickets.event_id = ${eventId})`);
  }
  const whereClause = conditions.length > 0 ? and(...conditions) : undefined;

  const totalQuery = whereClause
    ? (db as any).select({ count: sql<number>`count(*)` }).from(users).where(whereClause)
    : (db as any).select({ count: sql<number>`count(*)` }).from(users);
  const totalRow = await dbGet<any>(totalQuery);

  let query = (db as any).select({
    id: (users as any).id,
    email: (users as any).email,
    name: (users as any).name,
    phone: (users as any).phone,
    role: (users as any).role,
    languagePreference: (users as any).languagePreference,
    isClaimed: (users as any).isClaimed,
    rucNumber: (users as any).rucNumber,
    accountStatus: (users as any).accountStatus,
    createdAt: (users as any).createdAt,
  }).from(users);

  if (whereClause) {
    query = query.where(whereClause);
  }

  const result = await dbAll(
    query.orderBy(desc((users as any).createdAt)).limit(pageSize).offset((page - 1) * pageSize)
  );

  return c.json({ users: result, total: Number(totalRow?.count || 0), page, pageSize });
});

// Get user statistics (admin) — registered before /:id so "stats" is not parsed as a user id
usersRouter.get('/stats/overview', requireAuth(['admin']), async (c) => {
  const totalUsers = await dbGet<any>(
    (db as any)
      .select({ count: sql<number>`count(*)` })
      .from(users)
  );
  
  const adminCount = await dbGet<any>(
    (db as any)
      .select({ count: sql<number>`count(*)` })
      .from(users)
      .where(eq((users as any).role, 'admin'))
  );
  
  return c.json({
    stats: {
      total: totalUsers?.count || 0,
      admins: adminCount?.count || 0,
    },
  });
});

// Get user by ID (admin or self)
usersRouter.get('/:id', requireAuth(['admin', 'organizer', 'staff', 'marketing', 'user']), async (c) => {
  const id = c.req.param('id');
  const currentUser = c.get('user');
  
  // Users can only view their own profile unless admin
  if (currentUser.role !== 'admin' && currentUser.id !== id) {
    return c.json({ error: 'Forbidden' }, 403);
  }
  
  const user = await dbGet(
    (db as any)
      .select({
        id: (users as any).id,
        email: (users as any).email,
        name: (users as any).name,
        phone: (users as any).phone,
        role: (users as any).role,
        languagePreference: (users as any).languagePreference,
        isClaimed: (users as any).isClaimed,
        rucNumber: (users as any).rucNumber,
        accountStatus: (users as any).accountStatus,
        createdAt: (users as any).createdAt,
      })
      .from(users)
      .where(eq((users as any).id, id))
  );
  
  if (!user) {
    return c.json({ error: 'User not found' }, 404);
  }
  
  return c.json({ user });
});

// Update user (admin or self)
usersRouter.put('/:id', requireAuth(['admin', 'organizer', 'staff', 'marketing', 'user']), zValidator('json', updateUserSchema), async (c) => {
  const id = c.req.param('id');
  const data = c.req.valid('json');
  const currentUser = c.get('user');
  
  // Users can only update their own profile unless admin
  if (currentUser.role !== 'admin' && currentUser.id !== id) {
    return c.json({ error: 'Forbidden' }, 403);
  }
  
  // Only admin can change roles, email, and account status
  if (data.role && currentUser.role !== 'admin') {
    delete data.role;
  }
  if (data.email && currentUser.role !== 'admin') {
    delete data.email;
  }
  if (data.accountStatus && currentUser.role !== 'admin') {
    delete data.accountStatus;
  }
  
  const existing = await dbGet(
    (db as any).select().from(users).where(eq((users as any).id, id))
  );
  if (!existing) {
    return c.json({ error: 'User not found' }, 404);
  }
  
  // Keep the Better Auth admin `banned` flag in sync with accountStatus so
  // sign-in is blocked at the auth layer too, and kill live sessions on
  // suspension so it takes effect immediately (sessions are DB-validated on
  // every request by both the backend and the photo API).
  const statusMirror: Record<string, any> = {};
  if (data.accountStatus === 'suspended') {
    statusMirror.banned = true;
    statusMirror.banReason = 'Suspended by admin';
  } else if (data.accountStatus) {
    statusMirror.banned = false;
    statusMirror.banReason = null;
  }

  await (db as any)
    .update(users)
    .set({ ...data, ...statusMirror, updatedAt: getNow() })
    .where(eq((users as any).id, id));

  if (data.accountStatus === 'suspended') {
    await (db as any).delete(authSessions).where(eq((authSessions as any).userId, id));
  }

  const updated = await dbGet(
    (db as any)
      .select({
        id: (users as any).id,
        email: (users as any).email,
        name: (users as any).name,
        phone: (users as any).phone,
        role: (users as any).role,
        languagePreference: (users as any).languagePreference,
        isClaimed: (users as any).isClaimed,
        rucNumber: (users as any).rucNumber,
        accountStatus: (users as any).accountStatus,
        createdAt: (users as any).createdAt,
      })
      .from(users)
      .where(eq((users as any).id, id))
  );
  
  return c.json({ user: updated });
});

// Get user's ticket history
usersRouter.get('/:id/history', requireAuth(['admin', 'organizer', 'staff', 'marketing', 'user']), async (c) => {
  const id = c.req.param('id');
  const currentUser = c.get('user');
  
  // Users can only view their own history unless admin/organizer
  if (!['admin', 'organizer'].includes(currentUser.role) && currentUser.id !== id) {
    return c.json({ error: 'Forbidden' }, 403);
  }
  
  const userTickets = await dbAll<any>(
    (db as any)
      .select()
      .from(tickets)
      .where(eq((tickets as any).userId, id))
      .orderBy(desc((tickets as any).createdAt))
  );
  
  // Get event details for each ticket
  const history = await Promise.all(
    userTickets.map(async (ticket: any) => {
      const event = await dbGet(
        (db as any)
          .select()
          .from(events)
          .where(eq((events as any).id, ticket.eventId))
      );
      
      return {
        ...ticket,
        event,
      };
    })
  );
  
  return c.json({ history });
});

// Delete user (admin only)
usersRouter.delete('/:id', requireAuth(['admin']), async (c) => {
  const id = c.req.param('id');
  const currentUser = c.get('user');
  
  // Prevent self-deletion
  if (currentUser.id === id) {
    return c.json({ error: 'Cannot delete your own account' }, 400);
  }
  
  const existing = await dbGet<any>(
    (db as any).select().from(users).where(eq((users as any).id, id))
  );
  if (!existing) {
    return c.json({ error: 'User not found' }, 404);
  }
  
  // Prevent deleting admin users
  if (existing.role === 'admin') {
    return c.json({ error: 'Cannot delete admin users' }, 400);
  }
  
  try {
    // Get all tickets for this user
    const userTickets = await dbAll<any>(
      (db as any)
        .select()
        .from(tickets)
        .where(eq((tickets as any).userId, id))
    );
    
    // Delete invoices associated with user's tickets (invoices reference payments which reference tickets)
    for (const ticket of userTickets) {
      // Get payments for this ticket
      const ticketPayments = await dbAll<any>(
        (db as any)
          .select()
          .from(payments)
          .where(eq((payments as any).ticketId, ticket.id))
      );
      
      // Delete invoices for each payment
      for (const payment of ticketPayments) {
        await (db as any).delete(invoices).where(eq((invoices as any).paymentId, payment.id));
      }
      
      // Delete payments for this ticket
      await (db as any).delete(payments).where(eq((payments as any).ticketId, ticket.id));
    }
    
    // Delete invoices directly associated with the user (if any)
    await (db as any).delete(invoices).where(eq((invoices as any).userId, id));
    
    // Delete user's tickets
    await (db as any).delete(tickets).where(eq((tickets as any).userId, id));
    
    // Delete magic link tokens for the user
    await (db as any).delete(magicLinkTokens).where(eq((magicLinkTokens as any).userId, id));
    
    // Delete user sessions
    await (db as any).delete(userSessions).where(eq((userSessions as any).userId, id));
    
    // Set userId to null in audit_logs (nullable reference)
    await (db as any)
      .update(auditLogs)
      .set({ userId: null })
      .where(eq((auditLogs as any).userId, id));
    
    // Set sentBy to null in email_logs (nullable reference)
    await (db as any)
      .update(emailLogs)
      .set({ sentBy: null })
      .where(eq((emailLogs as any).sentBy, id));
    
    // Set updatedBy to null in payment_options (nullable reference)
    await (db as any)
      .update(paymentOptions)
      .set({ updatedBy: null })
      .where(eq((paymentOptions as any).updatedBy, id));
    
    // Set updatedBy to null in legal_pages (nullable reference)
    await (db as any)
      .update(legalPages)
      .set({ updatedBy: null })
      .where(eq((legalPages as any).updatedBy, id));
    
    // Set updatedBy to null in site_settings (nullable reference)
    await (db as any)
      .update(siteSettings)
      .set({ updatedBy: null })
      .where(eq((siteSettings as any).updatedBy, id));
    
    // Clear checkedInByAdminId references in tickets
    await (db as any)
      .update(tickets)
      .set({ checkedInByAdminId: null })
      .where(eq((tickets as any).checkedInByAdminId, id));
    
    // Delete the user
    await (db as any).delete(users).where(eq((users as any).id, id));
    
    return c.json({ message: 'User deleted successfully' });
  } catch (error) {
    console.error('Error deleting user:', error);
    return c.json({ error: 'Failed to delete user. They may have related records.' }, 500);
  }
});

export default usersRouter;
