import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
import { db, dbGet, paymentOptions, eventPaymentOverrides, events, tickets } from '../db/index.js';
import { eq } from 'drizzle-orm';
import { requireAuth, getAuthUser } from '../lib/auth.js';
import { generateId, getNow, convertBooleansForDb } from '../lib/utils.js';

const paymentOptionsRouter = new Hono();

// Helper to normalize boolean (handles true/false and 0/1 from database)
const booleanOrNumber = z.union([z.boolean(), z.number()]).transform((val) => {
  if (typeof val === 'boolean') return val;
  return val !== 0;
});

// Schema for updating global payment options
const updatePaymentOptionsSchema = z.object({
  tpagoEnabled: booleanOrNumber.optional(),
  tpagoLink: z.string().optional().nullable(),
  tpagoLink2: z.string().optional().nullable(),
  tpagoLink3: z.string().optional().nullable(),
  tpagoLink4: z.string().optional().nullable(),
  tpagoLink5: z.string().optional().nullable(),
  tpagoInstructions: z.string().optional().nullable(),
  tpagoInstructionsEs: z.string().optional().nullable(),
  bankTransferEnabled: booleanOrNumber.optional(),
  bankName: z.string().optional().nullable(),
  bankAccountHolder: z.string().optional().nullable(),
  bankAccountNumber: z.string().optional().nullable(),
  bankAlias: z.string().optional().nullable(),
  bankPhone: z.string().optional().nullable(),
  bankNotes: z.string().optional().nullable(),
  bankNotesEs: z.string().optional().nullable(),
  lightningEnabled: booleanOrNumber.optional(),
  cashEnabled: booleanOrNumber.optional(),
  cashInstructions: z.string().optional().nullable(),
  cashInstructionsEs: z.string().optional().nullable(),
  // Booking settings
  allowDuplicateBookings: booleanOrNumber.optional(),
});

/** Strip bank account numbers from payment options for anonymous callers. */
function publicPaymentOptions(merged: Record<string, any>) {
  return {
    ...merged,
    bankName: null,
    bankAccountHolder: null,
    bankAccountNumber: null,
    bankAlias: null,
    bankPhone: null,
    tpagoLink: null,
    tpagoLink2: null,
    tpagoLink3: null,
    tpagoLink4: null,
    tpagoLink5: null,
  };
}

// Schema for event-level overrides
const updateEventOverridesSchema = z.object({
  tpagoEnabled: booleanOrNumber.optional().nullable(),
  tpagoLink: z.string().optional().nullable(),
  tpagoLink2: z.string().optional().nullable(),
  tpagoLink3: z.string().optional().nullable(),
  tpagoLink4: z.string().optional().nullable(),
  tpagoLink5: z.string().optional().nullable(),
  tpagoInstructions: z.string().optional().nullable(),
  tpagoInstructionsEs: z.string().optional().nullable(),
  bankTransferEnabled: booleanOrNumber.optional().nullable(),
  bankName: z.string().optional().nullable(),
  bankAccountHolder: z.string().optional().nullable(),
  bankAccountNumber: z.string().optional().nullable(),
  bankAlias: z.string().optional().nullable(),
  bankPhone: z.string().optional().nullable(),
  bankNotes: z.string().optional().nullable(),
  bankNotesEs: z.string().optional().nullable(),
  lightningEnabled: booleanOrNumber.optional().nullable(),
  cashEnabled: booleanOrNumber.optional().nullable(),
  cashInstructions: z.string().optional().nullable(),
  cashInstructionsEs: z.string().optional().nullable(),
});

// Get global payment options
paymentOptionsRouter.get('/', requireAuth(['admin']), async (c) => {
  const options = await dbGet<any>(
    (db as any).select().from(paymentOptions)
  );

  // If no options exist yet, return defaults
  if (!options) {
    return c.json({
      paymentOptions: {
        tpagoEnabled: false,
        tpagoLink: null,
        tpagoLink2: null,
        tpagoLink3: null,
        tpagoLink4: null,
        tpagoLink5: null,
        tpagoInstructions: null,
        tpagoInstructionsEs: null,
        bankTransferEnabled: false,
        bankName: null,
        bankAccountHolder: null,
        bankAccountNumber: null,
        bankAlias: null,
        bankPhone: null,
        bankNotes: null,
        bankNotesEs: null,
        lightningEnabled: true,
        cashEnabled: true,
        cashInstructions: null,
        cashInstructionsEs: null,
        allowDuplicateBookings: false,
      },
    });
  }

  return c.json({ paymentOptions: options });
});

// Update global payment options
paymentOptionsRouter.put('/', requireAuth(['admin']), zValidator('json', updatePaymentOptionsSchema), async (c) => {
  const data = c.req.valid('json');
  const user = (c as any).get('user');
  const now = getNow();

  // Check if options exist
  const existing = await dbGet<any>(
    (db as any)
      .select()
      .from(paymentOptions)
  );

  // Convert boolean fields for database compatibility
  const dbData = convertBooleansForDb(data);
  
  if (existing) {
    // Update existing
    await (db as any)
      .update(paymentOptions)
      .set({
        ...dbData,
        updatedAt: now,
        updatedBy: user.id,
      })
      .where(eq((paymentOptions as any).id, existing.id));
  } else {
    // Create new
    const id = generateId();
    await (db as any).insert(paymentOptions).values({
      id,
      ...dbData,
      updatedAt: now,
      updatedBy: user.id,
    });
  }

  const updated = await dbGet(
    (db as any)
      .select()
      .from(paymentOptions)
  );

  return c.json({ paymentOptions: updated, message: 'Payment options updated successfully' });
});

// Get payment options for a specific event (merged with global)
paymentOptionsRouter.get('/event/:eventId', async (c) => {
  const eventId = c.req.param('eventId');
  const ticketId = c.req.query('ticketId');

  // Get the event first to verify it exists
  const event = await dbGet(
    (db as any)
      .select()
      .from(events)
      .where(eq((events as any).id, eventId))
  );

  if (!event) {
    return c.json({ error: 'Event not found' }, 404);
  }

  // Get global options
  const globalOptions = await dbGet<any>(
    (db as any)
      .select()
      .from(paymentOptions)
  );

  // Get event overrides
  const overrides = await dbGet<any>(
    (db as any)
      .select()
      .from(eventPaymentOverrides)
      .where(eq((eventPaymentOverrides as any).eventId, eventId))
  );

  // Merge global with overrides (override takes precedence if not null)
  const defaults = {
    tpagoEnabled: false,
    tpagoLink: null,
    tpagoLink2: null,
    tpagoLink3: null,
    tpagoLink4: null,
    tpagoLink5: null,
    tpagoInstructions: null,
    tpagoInstructionsEs: null,
    bankTransferEnabled: false,
    bankName: null,
    bankAccountHolder: null,
    bankAccountNumber: null,
    bankAlias: null,
    bankPhone: null,
    bankNotes: null,
    bankNotesEs: null,
    lightningEnabled: true,
    cashEnabled: true,
    cashInstructions: null,
    cashInstructionsEs: null,
  };

  const global = globalOptions || defaults;

  // Merge: override values take precedence if they're not null/undefined
  const merged = {
    tpagoEnabled: overrides?.tpagoEnabled ?? global.tpagoEnabled,
    tpagoLink: overrides?.tpagoLink ?? global.tpagoLink,
    tpagoLink2: overrides?.tpagoLink2 ?? global.tpagoLink2,
    tpagoLink3: overrides?.tpagoLink3 ?? global.tpagoLink3,
    tpagoLink4: overrides?.tpagoLink4 ?? global.tpagoLink4,
    tpagoLink5: overrides?.tpagoLink5 ?? global.tpagoLink5,
    tpagoInstructions: overrides?.tpagoInstructions ?? global.tpagoInstructions,
    tpagoInstructionsEs: overrides?.tpagoInstructionsEs ?? global.tpagoInstructionsEs,
    bankTransferEnabled: overrides?.bankTransferEnabled ?? global.bankTransferEnabled,
    bankName: overrides?.bankName ?? global.bankName,
    bankAccountHolder: overrides?.bankAccountHolder ?? global.bankAccountHolder,
    bankAccountNumber: overrides?.bankAccountNumber ?? global.bankAccountNumber,
    bankAlias: overrides?.bankAlias ?? global.bankAlias,
    bankPhone: overrides?.bankPhone ?? global.bankPhone,
    bankNotes: overrides?.bankNotes ?? global.bankNotes,
    bankNotesEs: overrides?.bankNotesEs ?? global.bankNotesEs,
    lightningEnabled: overrides?.lightningEnabled ?? global.lightningEnabled,
    cashEnabled: overrides?.cashEnabled ?? global.cashEnabled,
    cashInstructions: overrides?.cashInstructions ?? global.cashInstructions,
    cashInstructionsEs: overrides?.cashInstructionsEs ?? global.cashInstructionsEs,
  };

  // Full bank/TPago credentials are only returned when the caller proves they hold
  // a valid ticket for this event (the ticket UUID is the booking capability token),
  // or when an authenticated admin/organizer requests them.
  let revealSensitive = false;
  const authUser: any = await getAuthUser(c);
  if (authUser && ['admin', 'organizer'].includes(authUser.role)) {
    revealSensitive = true;
  } else if (ticketId) {
    const ticket = await dbGet<any>(
      (db as any).select().from(tickets).where(eq((tickets as any).id, ticketId))
    );
    if (ticket && ticket.eventId === eventId && ticket.status !== 'cancelled') {
      revealSensitive = true;
    }
  }

  return c.json({
    paymentOptions: revealSensitive ? merged : publicPaymentOptions(merged),
    hasOverrides: !!overrides,
  });
});

// Get event payment overrides (admin only)
paymentOptionsRouter.get('/event/:eventId/overrides', requireAuth(['admin', 'organizer']), async (c) => {
  const eventId = c.req.param('eventId');

  const overrides = await dbGet<any>(
    (db as any).select().from(eventPaymentOverrides).where(eq((eventPaymentOverrides as any).eventId, eventId))
  );

  return c.json({ overrides: overrides || null });
});

// Update event payment overrides
paymentOptionsRouter.put('/event/:eventId/overrides', requireAuth(['admin', 'organizer']), zValidator('json', updateEventOverridesSchema), async (c) => {
  const eventId = c.req.param('eventId');
  const data = c.req.valid('json');
  const now = getNow();

  // Verify event exists
  const event = await dbGet<any>(
    (db as any).select().from(events).where(eq((events as any).id, eventId))
  );

  if (!event) {
    return c.json({ error: 'Event not found' }, 404);
  }

  // Check if overrides exist
  const existing = await dbGet<any>(
    (db as any).select().from(eventPaymentOverrides).where(eq((eventPaymentOverrides as any).eventId, eventId))
  );

  // Convert boolean fields for database compatibility
  const dbData = convertBooleansForDb(data);
  
  if (existing) {
    await (db as any)
      .update(eventPaymentOverrides)
      .set({
        ...dbData,
        updatedAt: now,
      })
      .where(eq((eventPaymentOverrides as any).id, existing.id));
  } else {
    const id = generateId();
    await (db as any).insert(eventPaymentOverrides).values({
      id,
      eventId,
      ...dbData,
      createdAt: now,
      updatedAt: now,
    });
  }

  const updated = await dbGet(
    (db as any)
      .select()
      .from(eventPaymentOverrides)
      .where(eq((eventPaymentOverrides as any).eventId, eventId))
  );

  return c.json({ overrides: updated, message: 'Event payment overrides updated successfully' });
});

// Delete event payment overrides (revert to global)
paymentOptionsRouter.delete('/event/:eventId/overrides', requireAuth(['admin', 'organizer']), async (c) => {
  const eventId = c.req.param('eventId');

  await (db as any)
    .delete(eventPaymentOverrides)
    .where(eq((eventPaymentOverrides as any).eventId, eventId));

  return c.json({ message: 'Event payment overrides removed' });
});

export default paymentOptionsRouter;
