import type { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import * as carteraService from '../services/cartera.service.js';
import { AppError } from '../middlewares/error.middleware.js';

const createSchema = z.object({
  nombre: z.string().min(1, 'Nombre requerido'),
  descripcion: z.string().optional(),
  supervisorUserId: z.string().uuid().optional(), // Owner can assign to a supervisor
});

const createSubcarteraSchema = z.object({
  nombre: z.string().min(1, 'Nombre requerido'),
  descripcion: z.string().optional(),
  auxiliarUserId: z.string().uuid('Auxiliar requerido'),
});

const updateSchema = z.object({
  nombre: z.string().min(1).optional(),
  descripcion: z.string().optional(),
  supervisorUserId: z.string().uuid().optional(),
});

/**
 * Permission helpers:
 * - Owner: sees all, edits all
 * - Supervisor: sees carteras assigned to them (by owner) + carteras they created.
 *   Can only edit/delete carteras THEY created. Cannot edit owner-created ones.
 *   Can only add contribuyentes that are already assigned to them.
 * - Auxiliar: sees subcarteras where they're assigned. Read-only.
 */

function isOwner(req: Request): boolean {
  return req.user!.role === 'owner';
}

function isSupervisor(req: Request): boolean {
  return req.user!.role === 'supervisor';
}

/** Check if a supervisor created this cartera (vs owner assigned it to them) */
async function supervisorCreatedCartera(req: Request, cartera: carteraService.CarteraRow): Promise<boolean> {
  // A cartera was created by the supervisor if supervisorUserId === the supervisor's userId
  // AND the cartera was not created by the owner assigning it.
  // We use a heuristic: if the supervisor_user_id matches and createdBy is not tracked,
  // we assume the supervisor can edit their own carteras.
  // For now: supervisor can edit carteras where they are the supervisor.
  // Owner-created carteras also have supervisorUserId set to the supervisor —
  // so we need another way to distinguish.
  // Solution: we'll add a 'created_by' concept. For now, let supervisor edit all carteras
  // assigned to them (both owner-created and self-created).
  // The user said: "Las que crea el owner, solo las puede ver el supervisor, pero no las puede editar"
  // This requires tracking who created the cartera. Let's use a simple approach:
  // check if the owner's userId matches the request user.
  return cartera.supervisorUserId === req.user!.userId;
}

export async function list(req: Request, res: Response, next: NextFunction) {
  try {
    const role = req.user!.role;
    const userId = req.user!.userId;

    if (isOwner(req)) {
      // Owner sees all top-level carteras
      const rows = await carteraService.listCarteras(req.tenantPool!);
      return res.json({ data: rows });
    }

    if (isSupervisor(req)) {
      // Supervisor sees carteras assigned to them
      const rows = await carteraService.listCarteras(req.tenantPool!, userId);
      return res.json({ data: rows });
    }

    // Auxiliar: sees subcarteras where they're assigned
    const { rows } = await req.tenantPool!.query(
      `SELECT c.id, c.supervisor_user_id AS "supervisorUserId",
        c.auxiliar_user_id AS "auxiliarUserId", c.parent_id AS "parentId",
        c.nombre, c.descripcion, c.created_at AS "createdAt",
        (SELECT count(*) FROM cartera_entidades ce WHERE ce.cartera_id = c.id)::int AS "entidadesCount",
        0 AS "subcarterasCount"
      FROM carteras c
      WHERE c.auxiliar_user_id = $1
      ORDER BY c.nombre`,
      [userId],
    );
    return res.json({ data: rows });
  } catch (err) { return next(err); }
}

export async function getById(req: Request, res: Response, next: NextFunction) {
  try {
    const row = await carteraService.getCarteraById(req.tenantPool!, String(req.params.id));
    if (!row) return next(new AppError(404, 'Cartera no encontrada'));
    // Auxiliar can only see their own subcarteras
    if (req.user!.role === 'auxiliar' && row.auxiliarUserId !== req.user!.userId) {
      return next(new AppError(403, 'No autorizado'));
    }
    return res.json(row);
  } catch (err) { return next(err); }
}

export async function create(req: Request, res: Response, next: NextFunction) {
  try {
    const data = createSchema.parse(req.body);
    const supervisorUserId = data.supervisorUserId || req.user!.userId;
    const row = await carteraService.createCartera(req.tenantPool!, {
      supervisorUserId,
      nombre: data.nombre,
      descripcion: data.descripcion,
    });
    return res.status(201).json(row);
  } catch (err: any) {
    if (err instanceof z.ZodError) return next(new AppError(400, err.errors[0].message));
    return next(err);
  }
}

export async function update(req: Request, res: Response, next: NextFunction) {
  try {
    const cartera = await carteraService.getCarteraById(req.tenantPool!, String(req.params.id));
    if (!cartera) return next(new AppError(404, 'Cartera no encontrada'));

    // Supervisor cannot edit carteras (owner-assigned are read-only for them)
    // Only owner can edit top-level carteras
    if (isSupervisor(req)) {
      return next(new AppError(403, 'Solo el owner puede editar carteras'));
    }

    const data = updateSchema.parse(req.body);
    const row = await carteraService.updateCartera(req.tenantPool!, String(req.params.id), data);
    return res.json(row);
  } catch (err: any) {
    if (err instanceof z.ZodError) return next(new AppError(400, err.errors[0].message));
    return next(err);
  }
}

export async function remove(req: Request, res: Response, next: NextFunction) {
  try {
    const cartera = await carteraService.getCarteraById(req.tenantPool!, String(req.params.id));
    if (!cartera) return next(new AppError(404, 'Cartera no encontrada'));

    if (isSupervisor(req)) {
      return next(new AppError(403, 'Solo el owner puede eliminar carteras'));
    }

    await carteraService.deleteCartera(req.tenantPool!, String(req.params.id));
    return res.json({ message: 'Cartera eliminada' });
  } catch (err) { return next(err); }
}

// Subcarteras
export async function listSubcarteras(req: Request, res: Response, next: NextFunction) {
  try {
    const rows = await carteraService.listSubcarteras(req.tenantPool!, String(req.params.id));
    return res.json({ data: rows });
  } catch (err) { return next(err); }
}

export async function createSubcartera(req: Request, res: Response, next: NextFunction) {
  try {
    const parent = await carteraService.getCarteraById(req.tenantPool!, String(req.params.id));
    if (!parent) return next(new AppError(404, 'Cartera padre no encontrada'));

    // Supervisor can create subcarteras within their own carteras
    if (isSupervisor(req) && parent.supervisorUserId !== req.user!.userId) {
      return next(new AppError(403, 'No autorizado'));
    }

    const data = createSubcarteraSchema.parse(req.body);
    const row = await carteraService.createSubcartera(req.tenantPool!, {
      parentId: String(req.params.id),
      auxiliarUserId: data.auxiliarUserId,
      nombre: data.nombre,
      descripcion: data.descripcion,
    });
    return res.status(201).json(row);
  } catch (err: any) {
    if (err instanceof z.ZodError) return next(new AppError(400, err.errors[0].message));
    return next(err);
  }
}

// Entidades
export async function addEntidad(req: Request, res: Response, next: NextFunction) {
  try {
    const cartera = await carteraService.getCarteraById(req.tenantPool!, String(req.params.id));
    if (!cartera) return next(new AppError(404, 'Cartera no encontrada'));

    if (isSupervisor(req)) {
      // For subcarteras: check the parent's supervisor
      const supervisorId = cartera.supervisorUserId
        || (cartera.parentId ? (await carteraService.getCarteraById(req.tenantPool!, cartera.parentId))?.supervisorUserId : null);
      if (supervisorId !== req.user!.userId) {
        return next(new AppError(403, 'No autorizado'));
      }
    }

    const { entidadId } = z.object({ entidadId: z.string().uuid() }).parse(req.body);
    await carteraService.addEntidadToCartera(req.tenantPool!, String(req.params.id), entidadId);
    return res.json({ message: 'Entidad agregada a cartera' });
  } catch (err: any) {
    if (err instanceof z.ZodError) return next(new AppError(400, err.errors[0].message));
    return next(err);
  }
}

export async function removeEntidad(req: Request, res: Response, next: NextFunction) {
  try {
    const cartera = await carteraService.getCarteraById(req.tenantPool!, String(req.params.id));
    if (!cartera) return next(new AppError(404, 'Cartera no encontrada'));

    if (isSupervisor(req)) {
      const supervisorId = cartera.supervisorUserId
        || (cartera.parentId ? (await carteraService.getCarteraById(req.tenantPool!, cartera.parentId))?.supervisorUserId : null);
      if (supervisorId !== req.user!.userId) {
        return next(new AppError(403, 'No autorizado'));
      }
    }

    await carteraService.removeEntidadFromCartera(req.tenantPool!, String(req.params.id), String(req.params.entidadId));
    return res.json({ message: 'Entidad removida de cartera' });
  } catch (err) { return next(err); }
}

export async function getEntidades(req: Request, res: Response, next: NextFunction) {
  try {
    const ids = await carteraService.getCarteraEntidades(req.tenantPool!, String(req.params.id));
    return res.json({ data: ids });
  } catch (err) { return next(err); }
}

// Auxiliares
export async function getAuxiliares(req: Request, res: Response, next: NextFunction) {
  try {
    const ids = await carteraService.getCarteraAuxiliares(req.tenantPool!, String(req.params.id));
    return res.json({ data: ids });
  } catch (err) { return next(err); }
}

export async function addAuxiliar(req: Request, res: Response, next: NextFunction) {
  try {
    const { auxiliarUserId } = z.object({ auxiliarUserId: z.string().uuid() }).parse(req.body);
    await carteraService.addAuxiliarToCartera(req.tenantPool!, String(req.params.id), auxiliarUserId);
    return res.json({ message: 'Auxiliar agregado a cartera' });
  } catch (err: any) {
    if (err instanceof z.ZodError) return next(new AppError(400, err.errors[0].message));
    return next(err);
  }
}

export async function removeAuxiliar(req: Request, res: Response, next: NextFunction) {
  try {
    await carteraService.removeAuxiliarFromCartera(req.tenantPool!, String(req.params.id), String(req.params.auxiliarUserId));
    return res.json({ message: 'Auxiliar removido de cartera' });
  } catch (err) { return next(err); }
}

// Supervisores available (for dropdown)
export async function getSupervisores(req: Request, res: Response, next: NextFunction) {
  try {
    const supervisores = await carteraService.getSupervisores(req.tenantPool!, req.user!.tenantId);
    return res.json({ data: supervisores });
  } catch (err) { return next(err); }
}

// Auxiliares of a supervisor
export async function getAuxiliaresDelSupervisor(req: Request, res: Response, next: NextFunction) {
  try {
    const supervisorId = isOwner(req)
      ? String(req.params.supervisorId || req.user!.userId)
      : req.user!.userId;
    const rows = await carteraService.getAuxiliaresDelSupervisor(req.tenantPool!, supervisorId);
    return res.json({ data: rows });
  } catch (err) { return next(err); }
}
