import type { Pool } from 'pg';

export const ALLOWED_MIMES = new Set([
  'application/pdf',
  'application/msword',
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document', // docx
  'application/vnd.ms-excel',
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // xlsx
]);

export const MAX_SIZE_BYTES = 5 * 1024 * 1024; // 5 MB

export type EstadoPapeleria = 'pendiente' | 'aprobado' | 'rechazado';

export interface PapeleriaItem {
  id: number;
  contribuyenteId: string;
  nombre: string;
  descripcion: string | null;
  archivoFilename: string;
  archivoMime: string;
  archivoSize: number;
  anio: number;
  mes: number;
  requiereAprobacion: boolean;
  estado: EstadoPapeleria | null;
  aprobadoPor: string | null;
  aprobadoAt: Date | null;
  comentarioRechazo: string | null;
  subidoPor: string;
  createdAt: Date;
}

const SELECT = `
  id, contribuyente_id, nombre, descripcion,
  archivo_filename, archivo_mime, archivo_size,
  anio, mes,
  requiere_aprobacion, estado, aprobado_por, aprobado_at, comentario_rechazo,
  subido_por, created_at
`;

const ROW = (r: any): PapeleriaItem => ({
  id: r.id,
  contribuyenteId: r.contribuyente_id,
  nombre: r.nombre,
  descripcion: r.descripcion,
  archivoFilename: r.archivo_filename,
  archivoMime: r.archivo_mime,
  archivoSize: r.archivo_size,
  anio: r.anio,
  mes: r.mes,
  requiereAprobacion: r.requiere_aprobacion,
  estado: r.estado,
  aprobadoPor: r.aprobado_por,
  aprobadoAt: r.aprobado_at,
  comentarioRechazo: r.comentario_rechazo,
  subidoPor: r.subido_por,
  createdAt: r.created_at,
});

function sanitizeUuid(id: string): string {
  return id.replace(/[^a-f0-9-]/gi, '');
}

export interface UploadInput {
  contribuyenteId: string;
  nombre: string;
  descripcion: string | null;
  anio: number;
  mes: number;
  requiereAprobacion: boolean;
  archivo: Buffer;
  archivoFilename: string;
  archivoMime: string;
  subidoPor: string;
}

export async function uploadPapeleria(
  pool: Pool,
  input: UploadInput,
): Promise<PapeleriaItem> {
  if (!ALLOWED_MIMES.has(input.archivoMime)) {
    throw new Error(`Formato no permitido: ${input.archivoMime}. Solo PDF, Word y Excel.`);
  }
  if (input.archivo.length > MAX_SIZE_BYTES) {
    throw new Error(`Archivo excede el máximo de 5 MB (recibido ${(input.archivo.length / 1024 / 1024).toFixed(1)} MB).`);
  }

  const estadoInicial = input.requiereAprobacion ? 'pendiente' : null;

  const { rows: [r] } = await pool.query(
    `INSERT INTO papeleria_trabajo
       (contribuyente_id, nombre, descripcion, archivo, archivo_filename, archivo_mime, archivo_size,
        anio, mes, requiere_aprobacion, estado, subido_por)
     VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
     RETURNING ${SELECT}`,
    [
      sanitizeUuid(input.contribuyenteId),
      input.nombre,
      input.descripcion,
      input.archivo,
      input.archivoFilename,
      input.archivoMime,
      input.archivo.length,
      input.anio,
      input.mes,
      input.requiereAprobacion,
      estadoInicial,
      input.subidoPor,
    ],
  );
  return ROW(r);
}

export interface ListFilters {
  contribuyenteId: string;
  anio?: number;
  mes?: number;
  estado?: EstadoPapeleria | 'sin_aprobacion';
}

export async function listPapeleria(pool: Pool, f: ListFilters): Promise<PapeleriaItem[]> {
  const conds: string[] = ['contribuyente_id = $1'];
  const vals: unknown[] = [sanitizeUuid(f.contribuyenteId)];
  let i = 2;
  if (f.anio) { conds.push(`anio = $${i++}`); vals.push(f.anio); }
  if (f.mes) { conds.push(`mes = $${i++}`); vals.push(f.mes); }
  if (f.estado === 'sin_aprobacion') {
    conds.push('requiere_aprobacion = false');
  } else if (f.estado) {
    conds.push(`estado = $${i++}`); vals.push(f.estado);
  }
  const { rows } = await pool.query(
    `SELECT ${SELECT} FROM papeleria_trabajo
      WHERE ${conds.join(' AND ')}
      ORDER BY anio DESC, mes DESC, created_at DESC`,
    vals,
  );
  return rows.map(ROW);
}

export async function getById(pool: Pool, id: number): Promise<PapeleriaItem | null> {
  const { rows: [r] } = await pool.query(
    `SELECT ${SELECT} FROM papeleria_trabajo WHERE id = $1`,
    [id],
  );
  return r ? ROW(r) : null;
}

export async function downloadArchivo(
  pool: Pool,
  id: number,
): Promise<{ archivo: Buffer; filename: string; mime: string } | null> {
  const { rows: [r] } = await pool.query(
    `SELECT archivo, archivo_filename, archivo_mime FROM papeleria_trabajo WHERE id = $1`,
    [id],
  );
  if (!r) return null;
  return { archivo: r.archivo, filename: r.archivo_filename, mime: r.archivo_mime };
}

const ROLES_APROBADOR = new Set(['owner', 'cfo', 'supervisor']);

export async function aprobar(
  pool: Pool,
  id: number,
  userId: string,
  userRole: string,
): Promise<PapeleriaItem | null> {
  if (!ROLES_APROBADOR.has(userRole)) {
    throw new Error('Solo owner o supervisor pueden aprobar papelería');
  }
  const { rows: [r] } = await pool.query(
    `UPDATE papeleria_trabajo
        SET estado = 'aprobado', aprobado_por = $2, aprobado_at = NOW(),
            comentario_rechazo = NULL
      WHERE id = $1 AND requiere_aprobacion = true
      RETURNING ${SELECT}`,
    [id, userId],
  );
  return r ? ROW(r) : null;
}

export async function rechazar(
  pool: Pool,
  id: number,
  userId: string,
  userRole: string,
  comentario: string | null,
): Promise<PapeleriaItem | null> {
  if (!ROLES_APROBADOR.has(userRole)) {
    throw new Error('Solo owner o supervisor pueden rechazar papelería');
  }
  const { rows: [r] } = await pool.query(
    `UPDATE papeleria_trabajo
        SET estado = 'rechazado', aprobado_por = $2, aprobado_at = NOW(),
            comentario_rechazo = $3
      WHERE id = $1 AND requiere_aprobacion = true
      RETURNING ${SELECT}`,
    [id, userId, comentario],
  );
  return r ? ROW(r) : null;
}

export async function eliminar(pool: Pool, id: number): Promise<boolean> {
  const { rowCount } = await pool.query(
    `DELETE FROM papeleria_trabajo WHERE id = $1`,
    [id],
  );
  return (rowCount ?? 0) > 0;
}
