import { getDb } from './db';
import { randomTokenBase64Url, sha256Hex } from './crypto';
import { WEB_BASE_URL } from './env';
import { toIsoSqlUTC } from './datetime';

export type CalendarTokenType = 'personal' | 'group' | 'aggregate';

function requireBaseUrl(): string {
  const base = (WEB_BASE_URL || '').trim();
  if (!base) {
    throw new Error('[calendar-tokens] WEB_BASE_URL no está configurado');
  }
  return base.replace(/\/+$/, '');
}

export function buildCalendarIcsUrl(type: CalendarTokenType, token: string): string {
  const base = requireBaseUrl();
  const segment = type === 'personal' ? 'personal' : type === 'group' ? 'group' : 'aggregate';
  return `${base}/ics/${segment}/${token}.ics`;
}

export async function findActiveToken(
  type: CalendarTokenType,
  userId: string,
  groupId?: string | null
): Promise<{
  id: number;
  type: CalendarTokenType;
  user_id: string;
  group_id: string | null;
  token_hash: string;
  token_plain: string | null;
  created_at: string;
  revoked_at: string | null;
  last_used_at: string | null;
} | null> {
  const db = await getDb();
  const sql = groupId
    ? `
    SELECT id, type, user_id, group_id, token_hash, token_plain, created_at, revoked_at, last_used_at
    FROM calendar_tokens
    WHERE type = ? AND user_id = ? AND group_id = ? AND revoked_at IS NULL
    ORDER BY id DESC
    LIMIT 1
  `
    : `
    SELECT id, type, user_id, group_id, token_hash, token_plain, created_at, revoked_at, last_used_at
    FROM calendar_tokens
    WHERE type = ? AND user_id = ? AND group_id IS NULL AND revoked_at IS NULL
    ORDER BY id DESC
    LIMIT 1
  `;
  const stmt = db.prepare(sql);
  const row = groupId
    ? stmt.get(type, userId, groupId)
    : stmt.get(type, userId);
  return (row as any) || null;
}

/**
 * Crea un nuevo token ICS y devuelve la URL completa (no se guarda el token en claro).
 * Lanza si existe una entrada activa y se viola la unicidad; usar findActiveToken antes si quieres evitar error.
 */
export async function createCalendarTokenUrl(
  type: CalendarTokenType,
  userId: string,
  groupId?: string | null
): Promise<{ url: string; token: string; id: number }> {
  const db = await getDb();

  const token = randomTokenBase64Url(32);
  const tokenHash = await sha256Hex(token);
  const createdAt = toIsoSqlUTC(new Date());

  const insert = db.prepare(`
    INSERT INTO calendar_tokens (type, user_id, group_id, token_hash, token_plain, created_at)
    VALUES (?, ?, ?, ?, ?, ?)
  `);
  const res = insert.run(type, userId, groupId ?? null, tokenHash, token, createdAt);
  const id = Number(res.lastInsertRowid || 0);

  return { url: buildCalendarIcsUrl(type, token), token, id };
}

/**
 * Revoca el token activo (si existe) y crea uno nuevo. Devuelve la nueva URL completa.
 */
export async function rotateCalendarTokenUrl(
  type: CalendarTokenType,
  userId: string,
  groupId?: string | null
): Promise<{ url: string; token: string; id: number; revoked: number | null }> {
  const db = await getDb();
  const now = toIsoSqlUTC(new Date());

  const existing = await findActiveToken(type, userId, groupId ?? null);
  let revoked: number | null = null;
  if (existing) {
    db.prepare(`UPDATE calendar_tokens SET revoked_at = ? WHERE id = ? AND revoked_at IS NULL`).run(
      now,
      existing.id
    );
    revoked = existing.id;
  }

  const created = await createCalendarTokenUrl(type, userId, groupId ?? null);
  return { ...created, revoked };
}
