import type { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { generarEventosFiscales, generarEventosDesdeObligaciones } from '../services/calendario-fiscal.service.js';
import * as recordatoriosService from '../services/recordatorios.service.js';
import { getEventosTareasParaCalendario } from '../services/tareas.service.js';
import { AppError } from '../middlewares/error.middleware.js';
import { isDespachoTenant } from '@horux/shared';
import { prisma } from '../config/database.js';

const createRecordatorioSchema = z.object({
  titulo: z.string().min(1).max(200),
  descripcion: z.string().max(2000).default(''),
  fechaLimite: z.string().min(8), // ISO date o yyyy-mm-dd
  notas: z.string().max(2000).optional(),
  privado: z.boolean().optional(),
  recurrencia: z.enum(['unica', 'mensual', 'bimestral', 'trimestral', 'anual']).default('unica'),
  fechaFin: z.string().min(8).optional(),
});

const updateRecordatorioSchema = z.object({
  titulo: z.string().min(1).max(200).optional(),
  descripcion: z.string().max(2000).optional(),
  fechaLimite: z.string().min(8).optional(),
  notas: z.string().max(2000).optional(),
  privado: z.boolean().optional(),
  completado: z.boolean().optional(),
});

function effectiveTenantId(req: Request): string {
  return req.viewingTenantId || req.user!.tenantId;
}

// Forma compatible con EventoFiscal (sin metadata interna como tareaId/periodoId).
function eventoTareaShape(t: import('../services/tareas.service.js').TareaEventoCalendario) {
  return {
    titulo: t.titulo,
    descripcion: t.descripcion,
    tipo: 'tarea' as const,
    fechaLimite: t.fechaLimite,
    recurrencia: t.recurrencia,
    completado: t.completado,
    notas: t.notas,
    // Metadata adicional para el frontend (links, modales)
    tareaId: t.tareaId,
    periodoId: t.periodoId,
  };
}

export async function getEventosGenerados(req: Request, res: Response, next: NextFunction) {
  try {
    const año = parseInt(req.query.año as string) || new Date().getFullYear();
    const tenantId = effectiveTenantId(req);

    let fiscales;

    // Determine tenant type by looking up the RFC from the central DB
    const tenantRecord = await prisma.tenant.findUnique({
      where: { id: tenantId },
      select: { rfc: true },
    });
    const isDespacho = isDespachoTenant(tenantRecord?.rfc);
    if (isDespacho) {
      const contribuyenteId = (req.query.contribuyenteId as string) || null;
      fiscales = await generarEventosDesdeObligaciones(req.tenantPool!, contribuyenteId, año);
    } else {
      // Horux360: use static catalog as before
      fiscales = await generarEventosFiscales(tenantId, año);
    }

    // Recordatorios custom — always included regardless of tenant type
    const custom = await recordatoriosService.getRecordatorios(
      req.tenantPool!,
      req.user!.userId,
      año
    );

    // Tareas operativas (despacho) — solo si hay contribuyente y rol no es cliente.
    // El usuario tipo cliente no debe ver tareas internas del despacho.
    let tareas: ReturnType<typeof eventoTareaShape>[] = [];
    const contribuyenteIdParam = (req.query.contribuyenteId as string) || null;
    if (contribuyenteIdParam && req.user?.role !== 'cliente') {
      const tareasRaw = await getEventosTareasParaCalendario(
        req.tenantPool!,
        contribuyenteIdParam,
        año,
      );
      tareas = tareasRaw.map(eventoTareaShape);
    }

    // Merge y ordenar por fecha
    const todos = [...fiscales, ...custom, ...tareas].sort((a, b) =>
      a.fechaLimite.localeCompare(b.fechaLimite)
    );

    res.json(todos);
  } catch (error) {
    next(error);
  }
}

export async function createRecordatorio(req: Request, res: Response, next: NextFunction) {
  try {
    if (!['owner', 'cfo', 'contador', 'supervisor', 'auxiliar'].includes(req.user!.role)) {
      return res.status(403).json({ message: 'Solo admin y contador pueden crear recordatorios' });
    }

    const data = createRecordatorioSchema.parse(req.body);

    const evento = await recordatoriosService.createRecordatorio(
      req.tenantPool!,
      req.user!.userId,
      { ...data, tipo: 'custom' }
    );

    res.status(201).json(evento);
  } catch (error) {
    if (error instanceof z.ZodError) return next(new AppError(400, error.errors[0].message));
    next(error);
  }
}

export async function updateRecordatorio(req: Request, res: Response, next: NextFunction) {
  try {
    if (!['owner', 'cfo', 'contador', 'supervisor', 'auxiliar'].includes(req.user!.role)) {
      return res.status(403).json({ message: 'Solo admin y contador pueden editar recordatorios' });
    }

    const id = parseInt(String(req.params.id));
    if (isNaN(id)) {
      return res.status(400).json({ message: 'ID inválido' });
    }

    const data = updateRecordatorioSchema.parse(req.body);
    const evento = await recordatoriosService.updateRecordatorio(
      req.tenantPool!,
      req.user!.userId,
      id,
      data
    );

    if (!evento) {
      return res.status(404).json({ message: 'Recordatorio no encontrado' });
    }

    res.json(evento);
  } catch (error) {
    if (error instanceof z.ZodError) return next(new AppError(400, error.errors[0].message));
    next(error);
  }
}

export async function deleteRecordatorio(req: Request, res: Response, next: NextFunction) {
  try {
    if (!['owner', 'cfo', 'contador', 'supervisor', 'auxiliar'].includes(req.user!.role)) {
      return res.status(403).json({ message: 'Solo admin y contador pueden eliminar recordatorios' });
    }

    const id = parseInt(String(req.params.id));
    if (isNaN(id)) {
      return res.status(400).json({ message: 'ID inválido' });
    }

    const deleted = await recordatoriosService.deleteRecordatorio(
      req.tenantPool!,
      req.user!.userId,
      id
    );

    if (!deleted) {
      return res.status(404).json({ message: 'Recordatorio no encontrado' });
    }

    res.status(204).send();
  } catch (error) {
    next(error);
  }
}
