import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
import { db, dbGet, dbAll, payments, tickets, events } from '../db/index.js';
import { eq, desc, and, or, sql } from 'drizzle-orm';
import { requireAuth } from '../lib/auth.js';
import { getNow } from '../lib/utils.js';
import emailService from '../lib/email.js';

const paymentsRouter = new Hono();

const updatePaymentSchema = z.object({
  status: z.enum(['pending', 'pending_approval', 'paid', 'refunded', 'failed']),
  reference: z.string().optional(),
  adminNote: z.string().optional(),
});

const approvePaymentSchema = z.object({
  adminNote: z.string().optional(),
  sendEmail: z.boolean().optional().default(true),
});

const rejectPaymentSchema = z.object({
  adminNote: z.string().optional(),
  sendEmail: z.boolean().optional().default(true),
});

// Get all payments (admin) - with ticket and event details
paymentsRouter.get('/', requireAuth(['admin']), async (c) => {
  const status = c.req.query('status');
  const provider = c.req.query('provider');
  const pendingApproval = c.req.query('pendingApproval');
  const eventId = c.req.query('eventId');
  const eventIds = c.req.query('eventIds');
  
  // Get all payments with their associated tickets
  let allPayments = await dbAll<any>(
    (db as any)
      .select()
      .from(payments)
      .orderBy(desc((payments as any).createdAt))
  );
  
  // Filter by status
  if (status) {
    allPayments = allPayments.filter((p: any) => p.status === status);
  }
  
  // Filter for pending approval specifically
  if (pendingApproval === 'true') {
    allPayments = allPayments.filter((p: any) => p.status === 'pending_approval');
  }
  
  // Filter by provider
  if (provider) {
    allPayments = allPayments.filter((p: any) => p.provider === provider);
  }
  
  // Enrich with ticket and event data
  let enrichedPayments = await Promise.all(
    allPayments.map(async (payment: any) => {
      const ticket = await dbGet<any>(
        (db as any)
          .select()
          .from(tickets)
          .where(eq((tickets as any).id, payment.ticketId))
      );
      
      let event: any = null;
      if (ticket) {
        event = await dbGet<any>(
          (db as any)
            .select()
            .from(events)
            .where(eq((events as any).id, ticket.eventId))
        );
      }
      
      return {
        ...payment,
        ticket: ticket ? {
          id: ticket.id,
          bookingId: ticket.bookingId,
          attendeeFirstName: ticket.attendeeFirstName,
          attendeeLastName: ticket.attendeeLastName,
          attendeeEmail: ticket.attendeeEmail,
          attendeePhone: ticket.attendeePhone,
          status: ticket.status,
        } : null,
        event: event ? {
          id: event.id,
          title: event.title,
          startDatetime: event.startDatetime,
        } : null,
      };
    })
  );
  
  // Filter by event(s)
  if (eventId) {
    enrichedPayments = enrichedPayments.filter((p: any) => p.event?.id === eventId);
  } else if (eventIds) {
    const ids = eventIds.split(',').map((s: string) => s.trim()).filter(Boolean);
    if (ids.length > 0) {
      enrichedPayments = enrichedPayments.filter((p: any) => p.event && ids.includes(p.event.id));
    }
  }
  
  return c.json({ payments: enrichedPayments });
});

// Get payments pending approval (admin dashboard view)
paymentsRouter.get('/pending-approval', requireAuth(['admin', 'organizer']), async (c) => {
  const pendingPayments = await dbAll<any>(
    (db as any)
      .select()
      .from(payments)
      .where(eq((payments as any).status, 'pending_approval'))
      .orderBy(desc((payments as any).userMarkedPaidAt))
  );
  
  // Enrich with ticket and event data
  const enrichedPayments = await Promise.all(
    pendingPayments.map(async (payment: any) => {
      const ticket = await dbGet<any>(
        (db as any)
          .select()
          .from(tickets)
          .where(eq((tickets as any).id, payment.ticketId))
      );
      
      let event: any = null;
      if (ticket) {
        event = await dbGet<any>(
          (db as any)
            .select()
            .from(events)
            .where(eq((events as any).id, ticket.eventId))
        );
      }
      
      return {
        ...payment,
        ticket: ticket ? {
          id: ticket.id,
          bookingId: ticket.bookingId,
          attendeeFirstName: ticket.attendeeFirstName,
          attendeeLastName: ticket.attendeeLastName,
          attendeeEmail: ticket.attendeeEmail,
          attendeePhone: ticket.attendeePhone,
          status: ticket.status,
        } : null,
        event: event ? {
          id: event.id,
          title: event.title,
          startDatetime: event.startDatetime,
        } : null,
      };
    })
  );
  
  return c.json({ payments: enrichedPayments });
});

// Get payment by ID (admin)
paymentsRouter.get('/:id', requireAuth(['admin', 'organizer']), async (c) => {
  const id = c.req.param('id');
  
  const payment = await dbGet<any>(
    (db as any)
      .select()
      .from(payments)
      .where(eq((payments as any).id, id))
  );
  
  if (!payment) {
    return c.json({ error: 'Payment not found' }, 404);
  }
  
  // Get associated ticket
  const ticket = await dbGet(
    (db as any)
      .select()
      .from(tickets)
      .where(eq((tickets as any).id, payment.ticketId))
  );
  
  return c.json({ payment: { ...payment, ticket } });
});

// Update payment (admin) - for manual payment confirmation
paymentsRouter.put('/:id', requireAuth(['admin', 'organizer']), zValidator('json', updatePaymentSchema), async (c) => {
  const id = c.req.param('id');
  const data = c.req.valid('json');
  const user = (c as any).get('user');
  
  const existing = await dbGet<any>(
    (db as any)
      .select()
      .from(payments)
      .where(eq((payments as any).id, id))
  );
  
  if (!existing) {
    return c.json({ error: 'Payment not found' }, 404);
  }
  
  const now = getNow();
  
  const updateData: any = { ...data, updatedAt: now };
  
  // If marking as paid, record who approved it and when
  if (data.status === 'paid' && existing.status !== 'paid') {
    updateData.paidAt = now;
    updateData.paidByAdminId = user.id;
  }
  
  // If payment confirmed, handle multi-ticket booking
  if (data.status === 'paid') {
    // Get the ticket associated with this payment
    const ticket = await dbGet<any>(
      (db as any)
        .select()
        .from(tickets)
        .where(eq((tickets as any).id, existing.ticketId))
    );
    
    // Check if this is part of a multi-ticket booking
    let ticketsToConfirm: any[] = [ticket];
    
    if (ticket?.bookingId) {
      // Get all tickets in this booking
      ticketsToConfirm = await dbAll(
        (db as any)
          .select()
          .from(tickets)
          .where(eq((tickets as any).bookingId, ticket.bookingId))
      );
      console.log(`[Payment] Confirming multi-ticket booking: ${ticket.bookingId}, ${ticketsToConfirm.length} tickets`);
    }
    
    // Update all payments and tickets in the booking
    for (const t of ticketsToConfirm) {
      await (db as any)
        .update(payments)
        .set(updateData)
        .where(eq((payments as any).ticketId, (t as any).id));
      
      await (db as any)
        .update(tickets)
        .set({ status: 'confirmed' })
        .where(eq((tickets as any).id, (t as any).id));
    }
    
    // Send confirmation emails asynchronously (don't block the response)
    Promise.all([
      emailService.sendBookingConfirmation(existing.ticketId),
      emailService.sendPaymentReceipt(id),
    ]).catch(err => {
      console.error('[Email] Failed to send confirmation emails:', err);
    });
  } else {
    // For non-paid status updates, just update this payment
    await (db as any)
      .update(payments)
      .set(updateData)
      .where(eq((payments as any).id, id));
  }
  
  const updated = await dbGet(
    (db as any)
      .select()
      .from(payments)
      .where(eq((payments as any).id, id))
  );
  
  return c.json({ payment: updated });
});

// Approve payment (admin) - specifically for pending_approval payments
paymentsRouter.post('/:id/approve', requireAuth(['admin', 'organizer']), zValidator('json', approvePaymentSchema), async (c) => {
  const id = c.req.param('id');
  const { adminNote, sendEmail } = c.req.valid('json');
  const user = (c as any).get('user');
  
  const payment = await dbGet<any>(
    (db as any)
      .select()
      .from(payments)
      .where(eq((payments as any).id, id))
  );
  
  if (!payment) {
    return c.json({ error: 'Payment not found' }, 404);
  }
  
  // Can approve pending or pending_approval payments
  if (!['pending', 'pending_approval'].includes(payment.status)) {
    return c.json({ error: 'Payment cannot be approved in its current state' }, 400);
  }
  
  const now = getNow();
  
  // Get the ticket associated with this payment
  const ticket = await dbGet<any>(
    (db as any)
      .select()
      .from(tickets)
      .where(eq((tickets as any).id, payment.ticketId))
  );
  
  // Check if this is part of a multi-ticket booking
  let ticketsToConfirm: any[] = [ticket];
  
  if (ticket?.bookingId) {
    // Get all tickets in this booking
    ticketsToConfirm = await dbAll(
      (db as any)
        .select()
        .from(tickets)
        .where(eq((tickets as any).bookingId, ticket.bookingId))
    );
    console.log(`[Payment] Approving multi-ticket booking: ${ticket.bookingId}, ${ticketsToConfirm.length} tickets`);
  }
  
  // Update all payments in the booking to paid
  for (const t of ticketsToConfirm) {
    await (db as any)
      .update(payments)
      .set({ 
        status: 'paid',
        paidAt: now,
        paidByAdminId: user.id,
        adminNote: adminNote || payment.adminNote,
        updatedAt: now,
      })
      .where(eq((payments as any).ticketId, (t as any).id));
    
    // Update ticket status to confirmed
    await (db as any)
      .update(tickets)
      .set({ status: 'confirmed' })
      .where(eq((tickets as any).id, (t as any).id));
  }
  
  // Send confirmation emails asynchronously (if sendEmail is true, which is the default)
  if (sendEmail !== false) {
    Promise.all([
      emailService.sendBookingConfirmation(payment.ticketId),
      emailService.sendPaymentReceipt(id),
    ]).catch(err => {
      console.error('[Email] Failed to send confirmation emails:', err);
    });
  } else {
    console.log('[Payment] Skipping confirmation emails per admin request');
  }
  
  const updated = await dbGet(
    (db as any)
      .select()
      .from(payments)
      .where(eq((payments as any).id, id))
  );
  
  return c.json({ payment: updated, message: 'Payment approved successfully' });
});

// Reject payment (admin)
paymentsRouter.post('/:id/reject', requireAuth(['admin', 'organizer']), zValidator('json', rejectPaymentSchema), async (c) => {
  const id = c.req.param('id');
  const { adminNote, sendEmail } = c.req.valid('json');
  const user = (c as any).get('user');
  
  const payment = await dbGet<any>(
    (db as any)
      .select()
      .from(payments)
      .where(eq((payments as any).id, id))
  );
  
  if (!payment) {
    return c.json({ error: 'Payment not found' }, 404);
  }
  
  if (!['pending', 'pending_approval'].includes(payment.status)) {
    return c.json({ error: 'Payment cannot be rejected in its current state' }, 400);
  }
  
  const now = getNow();
  
  // Update payment status to failed
  await (db as any)
    .update(payments)
    .set({ 
      status: 'failed',
      paidByAdminId: user.id,
      adminNote: adminNote || payment.adminNote,
      updatedAt: now,
    })
    .where(eq((payments as any).id, id));
  
  // Cancel the ticket - booking is no longer valid after rejection
  await (db as any)
    .update(tickets)
    .set({ 
      status: 'cancelled',
      updatedAt: now,
    })
    .where(eq((tickets as any).id, payment.ticketId));
  
  // Send rejection email asynchronously (for manual payment methods only, if sendEmail is true)
  if (sendEmail !== false && ['bank_transfer', 'tpago'].includes(payment.provider)) {
    emailService.sendPaymentRejectionEmail(id).catch(err => {
      console.error('[Email] Failed to send payment rejection email:', err);
    });
  } else if (sendEmail === false) {
    console.log('[Payment] Skipping rejection email per admin request');
  }
  
  const updated = await dbGet(
    (db as any)
      .select()
      .from(payments)
      .where(eq((payments as any).id, id))
  );
  
  return c.json({ payment: updated, message: 'Payment rejected and booking cancelled' });
});

// Send payment reminder email
paymentsRouter.post('/:id/send-reminder', requireAuth(['admin', 'organizer']), async (c) => {
  const id = c.req.param('id');
  
  const payment = await dbGet<any>(
    (db as any)
      .select()
      .from(payments)
      .where(eq((payments as any).id, id))
  );
  
  if (!payment) {
    return c.json({ error: 'Payment not found' }, 404);
  }
  
  // Only allow sending reminders for pending payments
  if (!['pending', 'pending_approval'].includes(payment.status)) {
    return c.json({ error: 'Payment reminder can only be sent for pending payments' }, 400);
  }
  
  try {
    const result = await emailService.sendPaymentReminder(id);
    
    if (result.success) {
      const now = getNow();
      
      // Record when reminder was sent
      await (db as any)
        .update(payments)
        .set({ 
          reminderSentAt: now,
          updatedAt: now,
        })
        .where(eq((payments as any).id, id));
      
      return c.json({ message: 'Payment reminder sent successfully', reminderSentAt: now });
    } else {
      return c.json({ error: result.error || 'Failed to send payment reminder' }, 500);
    }
  } catch (err: any) {
    console.error('[Payment] Failed to send payment reminder:', err);
    return c.json({ error: 'Failed to send payment reminder' }, 500);
  }
});

// Update admin note
paymentsRouter.post('/:id/note', requireAuth(['admin', 'organizer']), async (c) => {
  const id = c.req.param('id');
  const body = await c.req.json();
  const { adminNote } = body;
  
  const payment = await dbGet<any>(
    (db as any)
      .select()
      .from(payments)
      .where(eq((payments as any).id, id))
  );
  
  if (!payment) {
    return c.json({ error: 'Payment not found' }, 404);
  }
  
  const now = getNow();
  
  await (db as any)
    .update(payments)
    .set({ 
      adminNote: adminNote || null,
      updatedAt: now,
    })
    .where(eq((payments as any).id, id));
  
  const updated = await dbGet(
    (db as any)
      .select()
      .from(payments)
      .where(eq((payments as any).id, id))
  );
  
  return c.json({ payment: updated, message: 'Note updated' });
});

// Process refund (admin)
paymentsRouter.post('/:id/refund', requireAuth(['admin']), async (c) => {
  const id = c.req.param('id');
  
  const payment = await dbGet<any>(
    (db as any)
      .select()
      .from(payments)
      .where(eq((payments as any).id, id))
  );
  
  if (!payment) {
    return c.json({ error: 'Payment not found' }, 404);
  }
  
  if (payment.status !== 'paid') {
    return c.json({ error: 'Can only refund paid payments' }, 400);
  }
  
  const now = getNow();
  
  // Update payment status
  await (db as any)
    .update(payments)
    .set({ status: 'refunded', updatedAt: now })
    .where(eq((payments as any).id, id));
  
  // Cancel associated ticket
  await (db as any)
    .update(tickets)
    .set({ status: 'cancelled' })
    .where(eq((tickets as any).id, payment.ticketId));
  
  return c.json({ message: 'Refund processed successfully' });
});

// Payment webhook (for Stripe/MercadoPago)
paymentsRouter.post('/webhook', async (c) => {
  // This would handle webhook notifications from payment providers
  // Implementation depends on which provider is used
  
  const body = await c.req.json();
  
  // Log webhook for debugging
  console.log('Payment webhook received:', body);
  
  // TODO: Implement provider-specific webhook handling
  // - Verify webhook signature
  // - Update payment status
  // - Update ticket status
  
  return c.json({ received: true });
});

// Get payment statistics (admin)
paymentsRouter.get('/stats/overview', requireAuth(['admin']), async (c) => {
  const allPayments = await dbAll<any>((db as any).select().from(payments));
  
  const stats = {
    total: allPayments.length,
    pending: allPayments.filter((p: any) => p.status === 'pending').length,
    paid: allPayments.filter((p: any) => p.status === 'paid').length,
    refunded: allPayments.filter((p: any) => p.status === 'refunded').length,
    failed: allPayments.filter((p: any) => p.status === 'failed').length,
    totalRevenue: allPayments
      .filter((p: any) => p.status === 'paid')
      .reduce((sum: number, p: any) => sum + Number(p.amount || 0), 0),
  };
  
  return c.json({ stats });
});

export default paymentsRouter;
