import { nanoid } from 'nanoid';
import { randomUUID } from 'crypto';
import moment from 'moment-timezone';

/**
 * Get database type (reads env var each time to handle module loading order)
 */
function getDbType(): string {
  return process.env.DB_TYPE || 'sqlite';
}

/**
 * Generate a unique ID appropriate for the database type.
 * - SQLite: returns nanoid (21-char alphanumeric)
 * - PostgreSQL: returns UUID v4
 */
export function generateId(): string {
  return getDbType() === 'postgres' ? randomUUID() : nanoid(21);
}

export function generateTicketCode(): string {
  return `TKT-${nanoid(8).toUpperCase()}`;
}

/**
 * Get current timestamp in the format appropriate for the database type.
 * - SQLite: returns ISO string
 * - PostgreSQL: returns Date object
 */
export function getNow(): string | Date {
  const now = new Date();
  return getDbType() === 'postgres' ? now : now.toISOString();
}

/**
 * Convert a date value to the appropriate format for the database type.
 * - SQLite: returns ISO string
 * - PostgreSQL: returns Date object
 */
export function toDbDate(date: Date | string): string | Date {
  const d = typeof date === 'string' ? new Date(date) : date;
  return getDbType() === 'postgres' ? d : d.toISOString();
}

const NAIVE_DATETIME_RE = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(:\d{2}(\.\d+)?)?$/;

/**
 * Parse a datetime string that represents wall-clock time in a given timezone
 * and return the corresponding UTC Date.
 *
 * Naive strings (no "Z" or offset) are interpreted as wall-clock time in
 * `timezone`.  Strings that already carry a timezone indicator are parsed
 * directly so existing UTC ISO values still work.
 */
export function parseEventDatetime(
  datetime: string,
  timezone: string = 'America/Asuncion',
): Date {
  if (!NAIVE_DATETIME_RE.test(datetime)) {
    return new Date(datetime);
  }

  // Interpret the wall-clock digits as local time in `timezone` using
  // moment-timezone's bundled IANA data. This keeps the conversion correct
  // regardless of the host Node runtime's (possibly stale) tz database.
  return moment.tz(datetime, timezone).toDate();
}

/**
 * Convert a datetime string to the appropriate DB format, interpreting naive
 * strings as wall-clock time in `timezone` (defaults to America/Asuncion).
 */
export function toDbDateTz(
  datetime: string,
  timezone: string = 'America/Asuncion',
): string | Date {
  const d = parseEventDatetime(datetime, timezone);
  return getDbType() === 'postgres' ? d : d.toISOString();
}

/**
 * Convert a boolean value to the appropriate format for the database type.
 * - SQLite: returns boolean (true/false) for mode: 'boolean'
 * - PostgreSQL: returns integer (1/0) for pgInteger columns
 */
export function toDbBool(value: boolean): boolean | number {
  return getDbType() === 'postgres' ? (value ? 1 : 0) : value;
}

/**
 * Convert all boolean values in an object to the appropriate database format.
 * Useful for converting request data before database insert/update.
 */
export function convertBooleansForDb<T extends Record<string, any>>(obj: T): T {
  if (getDbType() !== 'postgres') {
    return obj; // SQLite handles booleans automatically
  }
  
  const result = { ...obj };
  for (const key in result) {
    if (typeof result[key] === 'boolean') {
      (result as any)[key] = result[key] ? 1 : 0;
    }
  }
  return result;
}

export function formatCurrency(amount: number, currency: string = 'PYG'): string {
  return new Intl.NumberFormat('es-PY', {
    style: 'currency',
    currency,
  }).format(amount);
}

export function calculateAvailableSeats(capacity: number, bookedCount: number): number {
  return Math.max(0, capacity - bookedCount);
}

export function isEventSoldOut(capacity: number, bookedCount: number): boolean {
  return bookedCount >= capacity;
}

export function sanitizeHtml(str: string): string {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;');
}
