import type { Database } from 'bun:sqlite';
import { AllowedGroups } from './allowed-groups';
import { GroupSyncService } from './group-sync';
import { normalizeWhatsAppId, isGroupId } from '../utils/whatsapp';
import { Metrics } from './metrics';
import { TaskService } from '../tasks/service';
import { codeId, formatDDMM } from '../utils/formatting';
import { getDb } from '../db/locator';

type AdminContext = {
  sender: string;
  groupId: string;
  message: string;
};

type AdminResponse = { recipient: string; message: string };

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

/** Shared "must be used in a group" guard. */
function requireIsGroup(groupId: string, sender: string): AdminResponse[] | null {
  if (isGroupId(groupId)) return null;
  return [{ recipient: sender, message: 'ℹ️ Este comando se debe usar dentro de un grupo.' }];
}

/** Parses a group_id argument from rest after one of the given prefixes. */
function parseGroupArg(rest: string, ...prefixes: string[]): string | null {
  for (const p of prefixes) {
    if (rest.startsWith(p)) return rest.slice(p.length).trim();
  }
  return null;
}

/** Checks the parsed group arg is valid, returning an error response if not. */
function validateGroupArg(arg: string | null, sender: string): AdminResponse[] | null {
  if (!arg) return null;
  if (!isGroupId(arg)) return [{ recipient: sender, message: '⚠️ Debes indicar un group_id válido terminado en @g.us' }];
  return null;
}

/** Sets a group's status and returns a response. */
function setGroupStatusAndRespond(
  groupId: string,
  status: 'allowed' | 'blocked',
  metricKey: string,
  label: string,
  sender: string,
): AdminResponse[] {
  const changed = AllowedGroups.setStatus(groupId, status);
  try { if (changed) Metrics.inc(metricKey); } catch {}
  return [{ recipient: sender, message: `✅ Grupo ${label}: ${groupId}` }];
}

// ---------------------------------------------------------------------------
// Database operations
// ---------------------------------------------------------------------------

function archiveGroupInDb(db: Database, groupId: string): void {
  db.transaction(() => {
    db.prepare(`
      UPDATE groups SET active = 0, archived = 1, last_verified = strftime('%Y-%m-%d %H:%M:%f','now')
      WHERE id = ?
    `).run(groupId);
    db.prepare(`
      UPDATE calendar_tokens SET revoked_at = strftime('%Y-%m-%d %H:%M:%f','now')
      WHERE group_id = ? AND revoked_at IS NULL
    `).run(groupId);
    db.prepare(`
      UPDATE group_members SET is_active = 0 WHERE group_id = ? AND is_active = 1
    `).run(groupId);
  })();
}

function deleteGroupInDb(db: Database, groupId: string): void {
  db.transaction(() => {
    db.prepare(`DELETE FROM tasks WHERE group_id = ?`).run(groupId);
    db.prepare(`DELETE FROM groups WHERE id = ?`).run(groupId);
    try { db.prepare(`DELETE FROM allowed_groups WHERE group_id = ?`).run(groupId); } catch {}
  })();
}

// ---------------------------------------------------------------------------
// Command handlers
// ---------------------------------------------------------------------------

function handlePending(sender: string): AdminResponse[] {
  const rows = AllowedGroups.listByStatus('pending');
  if (!rows || rows.length === 0) {
    return [{ recipient: sender, message: '✅ No hay grupos pendientes.' }];
  }
  const list = rows.map(r => `- ${r.group_id}${r.label ? ` (${r.label})` : ''}`).join('\n');
  return [{ recipient: sender, message: `Grupos pendientes (${rows.length}):\n${list}` }];
}

function handleEnableHere(ctx: AdminContext, sender: string): AdminResponse[] {
  const err = requireIsGroup(ctx.groupId, sender);
  if (err) return err;
  return setGroupStatusAndRespond(ctx.groupId, 'allowed', 'admin_actions_total_allow', 'habilitado', sender);
}

function handleDisableHere(ctx: AdminContext, sender: string): AdminResponse[] {
  const err = requireIsGroup(ctx.groupId, sender);
  if (err) return err;
  return setGroupStatusAndRespond(ctx.groupId, 'blocked', 'admin_actions_total_block', 'deshabilitado', sender);
}

function handleArchiveHere(ctx: AdminContext, sender: string, db: Database): AdminResponse[] {
  const err = requireIsGroup(ctx.groupId, sender);
  if (err) return err;
  archiveGroupInDb(db, ctx.groupId);
  try { AllowedGroups.setStatus(ctx.groupId, 'blocked'); } catch {}
  return [{ recipient: sender, message: `📦 Grupo archivado: ${ctx.groupId}` }];
}

function handleArchiveGroup(rest: string, sender: string, db: Database): AdminResponse[] {
  const arg = parseGroupArg(rest, 'archivar-grupo ', 'archive-group ');
  const err = validateGroupArg(arg, sender);
  if (err) return err;
  archiveGroupInDb(db, arg!);
  try { AllowedGroups.setStatus(arg!, 'blocked'); } catch {}
  return [{ recipient: sender, message: `📦 Grupo archivado: ${arg}` }];
}

function handleDeleteHere(ctx: AdminContext, sender: string, db: Database): AdminResponse[] {
  const err = requireIsGroup(ctx.groupId, sender);
  if (err) return err;
  deleteGroupInDb(db, ctx.groupId);
  return [{ recipient: sender, message: `🗑️ Grupo borrado y datos asociados eliminados: ${ctx.groupId}` }];
}

function handleDeleteGroup(rest: string, sender: string, db: Database): AdminResponse[] {
  const arg = parseGroupArg(rest, 'borrar-grupo ', 'delete-group ');
  const err = validateGroupArg(arg, sender);
  if (err) return err;
  deleteGroupInDb(db, arg!);
  return [{ recipient: sender, message: `🗑️ Grupo borrado y datos asociados eliminados: ${arg}` }];
}

function handleAllowAll(sender: string): AdminResponse[] {
  const pendings = AllowedGroups.listByStatus('pending');
  if (!pendings || pendings.length === 0) {
    return [{ recipient: sender, message: '✅ No hay grupos pendientes.' }];
  }
  let changed = 0;
  for (const r of pendings) {
    if (AllowedGroups.setStatus(r.group_id, 'allowed', r.label ?? null)) changed++;
    try { Metrics.inc('admin_actions_total_allow'); } catch {}
  }
  return [{ recipient: sender, message: `✅ Grupos habilitados: ${changed}` }];
}

function handleSetGroupArg(
  rest: string,
  prefixes: string[],
  sender: string,
  status: 'allowed' | 'blocked',
  metricKey: string,
  label: string,
): AdminResponse[] {
  const arg = parseGroupArg(rest, ...prefixes);
  if (!arg) return [];
  const err = validateGroupArg(arg, sender);
  if (err) return err;
  return setGroupStatusAndRespond(arg, status, metricKey, label, sender);
}

function handleAllowGroup(rest: string, sender: string): AdminResponse[] {
  // Must not catch the "allow all" variants
  if (rest === 'allow all' || rest === 'allow-all') return [];
  return handleSetGroupArg(rest, ['allow-group ', 'allow '], sender, 'allowed', 'admin_actions_total_allow', 'habilitado');
}

function handleBlockGroup(rest: string, sender: string): AdminResponse[] {
  return handleSetGroupArg(rest, ['block-group ', 'block '], sender, 'blocked', 'admin_actions_total_block', 'bloqueado');
}

async function handleSyncGroups(sender: string): Promise<AdminResponse[]> {
  try {
    const r = await GroupSyncService.syncGroups(true);
    return [{ recipient: sender, message: `✅ Sync de grupos ejecutado: ${r.added} añadidos, ${r.updated} actualizados.` }];
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    return [{ recipient: sender, message: `❌ Error al ejecutar sync de grupos: ${msg}` }];
  }
}

function handleListAll(rest: string, sender: string): AdminResponse[] {
  const DEFAULT_LIMIT = 50;
  const maybeNum = parseInt(rest.split(/\s+/).pop() || '', 10);
  const limit = (Number.isFinite(maybeNum) && maybeNum > 0) ? Math.min(maybeNum, 500) : DEFAULT_LIMIT;

  const tasks = TaskService.listAllActive(limit);
  const total = TaskService.countAllActive();

  if (!tasks || tasks.length === 0) {
    return [{ recipient: sender, message: '✅ No hay tareas activas.' }];
  }

  const lines = tasks.map(t => {
    const ddmm = formatDDMM(t.due_date);
    const groupLabel = t.group_name || t.group_id || 'DM';
    const parts: string[] = [
      `${codeId(t.id, t.display_code)}`,
      String(t.description || '').trim(),
    ];
    if (ddmm) parts.push(`vence ${ddmm}`);
    if (groupLabel) parts.push(`[${groupLabel}]`);
    return `- ${parts.join(' · ')}`;
  });

  const header = total > limit
    ? `Tus tareas — Tareas activas (${total}) — mostrando ${tasks.length} primeras:`
    : `Tus tareas — Tareas activas (${total}):`;
  const footer = `ℹ️ Para ver tareas sin responsable de un grupo, pide el listado desde ese grupo.`;

  try { Metrics.inc('admin_actions_total_list'); } catch {}

  return [{ recipient: sender, message: `${header}\n${lines.join('\n')}\n\n${footer}` }];
}

function handleHelp(sender: string): AdminResponse[] {
  return [{
    recipient: sender,
    message: [
      'Comandos de administración:',
      '- /admin pendientes (alias: pending, pend)',
      '- /admin habilitar-aquí (alias: enable)',
      '- /admin deshabilitar-aquí (alias: disable)',
      '- /admin allow all (alias: habilitar-todos, enable all)',
      '- /admin allow-group <group_id@g.us> (alias: allow)',
      '- /admin block-group <group_id@g.us> (alias: block)',
      '- /admin sync-grupos (alias: group-sync, syncgroups)',
      '- /admin ver todos (alias: listar, list all)',
    ].join('\n'),
  }];
}

// ---------------------------------------------------------------------------
// Command router
// ---------------------------------------------------------------------------

/** Exact-match commands. */
const EXACT_COMMANDS: Record<string, AdminResponse[] | ((ctx: AdminContext, sender: string, db: Database) => AdminResponse[] | Promise<AdminResponse[]>)> = {
  pendientes: (_, s) => handlePending(s),
  pending: (_, s) => handlePending(s),
  pend: (_, s) => handlePending(s),
  'habilitar-aquí': (c, s) => handleEnableHere(c, s),
  'habilitar-aqui': (c, s) => handleEnableHere(c, s),
  enable: (c, s) => handleEnableHere(c, s),
  'deshabilitar-aquí': (c, s) => handleDisableHere(c, s),
  'deshabilitar-aqui': (c, s) => handleDisableHere(c, s),
  disable: (c, s) => handleDisableHere(c, s),
  'archivar-aquí': (c, s, d) => handleArchiveHere(c, s, d),
  'archivar-aqui': (c, s, d) => handleArchiveHere(c, s, d),
  'archive here': (c, s, d) => handleArchiveHere(c, s, d),
  'archive-aquí': (c, s, d) => handleArchiveHere(c, s, d),
  'archive-aqui': (c, s, d) => handleArchiveHere(c, s, d),
  'borrar-aquí': (c, s, d) => handleDeleteHere(c, s, d),
  'borrar-aqui': (c, s, d) => handleDeleteHere(c, s, d),
  'delete here': (c, s, d) => handleDeleteHere(c, s, d),
  'delete-here': (c, s, d) => handleDeleteHere(c, s, d),
  'allow all': (_, s) => handleAllowAll(s),
  'allow-all': (_, s) => handleAllowAll(s),
  'habilitar-todos': (_, s) => handleAllowAll(s),
  'permitir todos': (_, s) => handleAllowAll(s),
  'enable all': (_, s) => handleAllowAll(s),
  'sync-grupos': async (_, s) => handleSyncGroups(s),
  'group-sync': async (_, s) => handleSyncGroups(s),
  syncgroups: async (_, s) => handleSyncGroups(s),
};

export class AdminService {

  private static admins(): Set<string> {
    const raw = String(process.env.ADMIN_USERS || '');
    const set = new Set<string>();
    for (const token of raw.split(',').map(s => s.trim()).filter(Boolean)) {
      const n = normalizeWhatsAppId(token);
      if (n) set.add(n);
    }
    return set;
  }

  static getAdmins(): string[] {
    return Array.from(this.admins());
  }

  static isAdmin(userId: string | null | undefined): boolean {
    const n = normalizeWhatsAppId(userId || '');
    if (!n) return false;
    return this.admins().has(n);
  }

  private static checkAdminAccess(sender: string): AdminResponse[] | null {
    if (!this.isAdmin(sender)) {
      return [{ recipient: sender, message: '🚫 No estás autorizado para usar /admin.' }];
    }
    return null;
  }

  /** Routes an /admin command to the appropriate handler. */
  static async handle(ctx: AdminContext): Promise<AdminResponse[]> {
    const sender = normalizeWhatsAppId(ctx.sender);
    if (!sender) return [];

    const accessDenied = this.checkAdminAccess(sender);
    if (accessDenied) return accessDenied;

    const raw = String(ctx.message || '').trim().toLowerCase();
    if (!raw.startsWith('/admin')) return [];

    const rest = raw.slice('/admin'.length).trim();
    const db = getDb() as Database;

    // 1. Try exact-routed commands first
    const exactHandler = EXACT_COMMANDS[rest];
    if (typeof exactHandler === 'function') {
      return await exactHandler(ctx, sender, db);
    }
    if (exactHandler) {
      return exactHandler;
    }

    // 2. Prefix-routed commands (take a group_id argument)
    if (rest.startsWith('allow-group ') || rest.startsWith('allow ')) {
      const r = handleAllowGroup(rest, sender);
      if (r.length > 0) return r;
    }
    if (rest.startsWith('block-group ') || rest.startsWith('block ')) {
      return handleBlockGroup(rest, sender);
    }
    if (rest.startsWith('archivar-grupo ') || rest.startsWith('archive-group ')) {
      return handleArchiveGroup(rest, sender, db);
    }
    if (rest.startsWith('borrar-grupo ') || rest.startsWith('delete-group ')) {
      return handleDeleteGroup(rest, sender, db);
    }

    // 3. "ver todos" with optional limit
    if (
      rest === 'ver todos' || rest.startsWith('ver todos ') ||
      rest === 'listar' || rest.startsWith('listar ') ||
      rest === 'list all' || rest.startsWith('list all ') ||
      rest === 'list-all' || rest.startsWith('list-all ')
    ) {
      return handleListAll(rest, sender);
    }

    // 4. Default → help
    return handleHelp(sender);
  }
}
