import { Response, NextFunction } from 'express';
import { z } from 'zod';
import { projectsService, CreateProjectDto, UpdateProjectDto, ProjectFilters } from './projects.service.js';
import { tasksService, CreateTaskDto, UpdateTaskDto, TaskFilters } from './tasks.service.js';
import { timesheetsService, CreateTimesheetDto, UpdateTimesheetDto, TimesheetFilters } from './timesheets.service.js';
import { AuthenticatedRequest } from '../../shared/middleware/auth.middleware.js';
import { ValidationError } from '../../shared/errors/index.js';

// Project schemas
const createProjectSchema = z.object({
  company_id: z.string().uuid({ message: 'La empresa es requerida' }),
  name: z.string().min(1, 'El nombre es requerido').max(255),
  code: z.string().max(50).optional(),
  description: z.string().optional(),
  manager_id: z.string().uuid().optional(),
  partner_id: z.string().uuid().optional(),
  date_start: z.string().optional(),
  date_end: z.string().optional(),
  privacy: z.enum(['public', 'private', 'followers']).default('public'),
  allow_timesheets: z.boolean().default(true),
  color: z.string().max(20).optional(),
});

const updateProjectSchema = z.object({
  name: z.string().min(1).max(255).optional(),
  code: z.string().max(50).optional().nullable(),
  description: z.string().optional().nullable(),
  manager_id: z.string().uuid().optional().nullable(),
  partner_id: z.string().uuid().optional().nullable(),
  date_start: z.string().optional().nullable(),
  date_end: z.string().optional().nullable(),
  status: z.enum(['draft', 'active', 'completed', 'cancelled', 'on_hold']).optional(),
  privacy: z.enum(['public', 'private', 'followers']).optional(),
  allow_timesheets: z.boolean().optional(),
  color: z.string().max(20).optional().nullable(),
});

const projectQuerySchema = z.object({
  company_id: z.string().uuid().optional(),
  manager_id: z.string().uuid().optional(),
  partner_id: z.string().uuid().optional(),
  status: z.enum(['draft', 'active', 'completed', 'cancelled', 'on_hold']).optional(),
  search: z.string().optional(),
  page: z.coerce.number().int().positive().default(1),
  limit: z.coerce.number().int().positive().max(100).default(20),
});

// Task schemas
const createTaskSchema = z.object({
  project_id: z.string().uuid({ message: 'El proyecto es requerido' }),
  stage_id: z.string().uuid().optional(),
  name: z.string().min(1, 'El nombre es requerido').max(255),
  description: z.string().optional(),
  assigned_to: z.string().uuid().optional(),
  parent_id: z.string().uuid().optional(),
  date_deadline: z.string().optional(),
  estimated_hours: z.number().positive().optional(),
  priority: z.enum(['low', 'normal', 'high', 'urgent']).default('normal'),
  color: z.string().max(20).optional(),
});

const updateTaskSchema = z.object({
  stage_id: z.string().uuid().optional().nullable(),
  name: z.string().min(1).max(255).optional(),
  description: z.string().optional().nullable(),
  assigned_to: z.string().uuid().optional().nullable(),
  parent_id: z.string().uuid().optional().nullable(),
  date_deadline: z.string().optional().nullable(),
  estimated_hours: z.number().positive().optional().nullable(),
  priority: z.enum(['low', 'normal', 'high', 'urgent']).optional(),
  status: z.enum(['todo', 'in_progress', 'review', 'done', 'cancelled']).optional(),
  sequence: z.number().int().positive().optional(),
  color: z.string().max(20).optional().nullable(),
});

const taskQuerySchema = z.object({
  project_id: z.string().uuid().optional(),
  stage_id: z.string().uuid().optional(),
  assigned_to: z.string().uuid().optional(),
  status: z.enum(['todo', 'in_progress', 'review', 'done', 'cancelled']).optional(),
  priority: z.enum(['low', 'normal', 'high', 'urgent']).optional(),
  search: z.string().optional(),
  page: z.coerce.number().int().positive().default(1),
  limit: z.coerce.number().int().positive().max(100).default(20),
});

const moveTaskSchema = z.object({
  stage_id: z.string().uuid().nullable(),
  sequence: z.number().int().positive(),
});

const assignTaskSchema = z.object({
  user_id: z.string().uuid({ message: 'El usuario es requerido' }),
});

// Timesheet schemas
const createTimesheetSchema = z.object({
  company_id: z.string().uuid({ message: 'La empresa es requerida' }),
  project_id: z.string().uuid({ message: 'El proyecto es requerido' }),
  task_id: z.string().uuid().optional(),
  date: z.string({ message: 'La fecha es requerida' }),
  hours: z.number().positive('Las horas deben ser positivas').max(24),
  description: z.string().optional(),
  billable: z.boolean().default(true),
});

const updateTimesheetSchema = z.object({
  task_id: z.string().uuid().optional().nullable(),
  date: z.string().optional(),
  hours: z.number().positive().max(24).optional(),
  description: z.string().optional().nullable(),
  billable: z.boolean().optional(),
});

const timesheetQuerySchema = z.object({
  company_id: z.string().uuid().optional(),
  project_id: z.string().uuid().optional(),
  task_id: z.string().uuid().optional(),
  user_id: z.string().uuid().optional(),
  status: z.enum(['draft', 'submitted', 'approved', 'rejected']).optional(),
  date_from: z.string().optional(),
  date_to: z.string().optional(),
  page: z.coerce.number().int().positive().default(1),
  limit: z.coerce.number().int().positive().max(100).default(20),
});

class ProjectsController {
  // ========== PROJECTS ==========
  async getProjects(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = projectQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }

      const filters: ProjectFilters = queryResult.data;
      const result = await projectsService.findAll(req.tenantId!, filters);

      res.json({
        success: true,
        data: result.data,
        meta: {
          total: result.total,
          page: filters.page,
          limit: filters.limit,
          totalPages: Math.ceil(result.total / (filters.limit || 20)),
        },
      });
    } catch (error) {
      next(error);
    }
  }

  async getProject(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const project = await projectsService.findById(req.params.id, req.tenantId!);
      res.json({ success: true, data: project });
    } catch (error) {
      next(error);
    }
  }

  async createProject(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createProjectSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de proyecto inválidos', parseResult.error.errors);
      }

      const dto: CreateProjectDto = parseResult.data;
      const project = await projectsService.create(dto, req.tenantId!, req.user!.userId);

      res.status(201).json({
        success: true,
        data: project,
        message: 'Proyecto creado exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

  async updateProject(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateProjectSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de proyecto inválidos', parseResult.error.errors);
      }

      const dto: UpdateProjectDto = parseResult.data;
      const project = await projectsService.update(req.params.id, dto, req.tenantId!, req.user!.userId);

      res.json({
        success: true,
        data: project,
        message: 'Proyecto actualizado exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

  async deleteProject(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      await projectsService.delete(req.params.id, req.tenantId!, req.user!.userId);
      res.json({ success: true, message: 'Proyecto eliminado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async getProjectStats(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const stats = await projectsService.getStats(req.params.id, req.tenantId!);
      res.json({ success: true, data: stats });
    } catch (error) {
      next(error);
    }
  }

  async getProjectTasks(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = taskQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }

      const filters: TaskFilters = { ...queryResult.data, project_id: req.params.id };
      const result = await tasksService.findAll(req.tenantId!, filters);

      res.json({
        success: true,
        data: result.data,
        meta: {
          total: result.total,
          page: filters.page,
          limit: filters.limit,
          totalPages: Math.ceil(result.total / (filters.limit || 20)),
        },
      });
    } catch (error) {
      next(error);
    }
  }

  async getProjectTimesheets(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = timesheetQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }

      const filters: TimesheetFilters = { ...queryResult.data, project_id: req.params.id };
      const result = await timesheetsService.findAll(req.tenantId!, filters);

      res.json({
        success: true,
        data: result.data,
        meta: {
          total: result.total,
          page: filters.page,
          limit: filters.limit,
          totalPages: Math.ceil(result.total / (filters.limit || 20)),
        },
      });
    } catch (error) {
      next(error);
    }
  }

  // ========== TASKS ==========
  async getTasks(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = taskQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }

      const filters: TaskFilters = queryResult.data;
      const result = await tasksService.findAll(req.tenantId!, filters);

      res.json({
        success: true,
        data: result.data,
        meta: {
          total: result.total,
          page: filters.page,
          limit: filters.limit,
          totalPages: Math.ceil(result.total / (filters.limit || 20)),
        },
      });
    } catch (error) {
      next(error);
    }
  }

  async getTask(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const task = await tasksService.findById(req.params.id, req.tenantId!);
      res.json({ success: true, data: task });
    } catch (error) {
      next(error);
    }
  }

  async createTask(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createTaskSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de tarea inválidos', parseResult.error.errors);
      }

      const dto: CreateTaskDto = parseResult.data;
      const task = await tasksService.create(dto, req.tenantId!, req.user!.userId);

      res.status(201).json({
        success: true,
        data: task,
        message: 'Tarea creada exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

  async updateTask(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateTaskSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de tarea inválidos', parseResult.error.errors);
      }

      const dto: UpdateTaskDto = parseResult.data;
      const task = await tasksService.update(req.params.id, dto, req.tenantId!, req.user!.userId);

      res.json({
        success: true,
        data: task,
        message: 'Tarea actualizada exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

  async deleteTask(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      await tasksService.delete(req.params.id, req.tenantId!, req.user!.userId);
      res.json({ success: true, message: 'Tarea eliminada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async moveTask(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = moveTaskSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de movimiento inválidos', parseResult.error.errors);
      }

      const { stage_id, sequence } = parseResult.data;
      const task = await tasksService.move(req.params.id, stage_id, sequence, req.tenantId!, req.user!.userId);

      res.json({
        success: true,
        data: task,
        message: 'Tarea movida exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

  async assignTask(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = assignTaskSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de asignación inválidos', parseResult.error.errors);
      }

      const { user_id } = parseResult.data;
      const task = await tasksService.assign(req.params.id, user_id, req.tenantId!, req.user!.userId);

      res.json({
        success: true,
        data: task,
        message: 'Tarea asignada exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

  // ========== TIMESHEETS ==========
  async getTimesheets(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = timesheetQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }

      const filters: TimesheetFilters = queryResult.data;
      const result = await timesheetsService.findAll(req.tenantId!, filters);

      res.json({
        success: true,
        data: result.data,
        meta: {
          total: result.total,
          page: filters.page,
          limit: filters.limit,
          totalPages: Math.ceil(result.total / (filters.limit || 20)),
        },
      });
    } catch (error) {
      next(error);
    }
  }

  async getTimesheet(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const timesheet = await timesheetsService.findById(req.params.id, req.tenantId!);
      res.json({ success: true, data: timesheet });
    } catch (error) {
      next(error);
    }
  }

  async createTimesheet(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createTimesheetSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de timesheet inválidos', parseResult.error.errors);
      }

      const dto: CreateTimesheetDto = parseResult.data;
      const timesheet = await timesheetsService.create(dto, req.tenantId!, req.user!.userId);

      res.status(201).json({
        success: true,
        data: timesheet,
        message: 'Tiempo registrado exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

  async updateTimesheet(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateTimesheetSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de timesheet inválidos', parseResult.error.errors);
      }

      const dto: UpdateTimesheetDto = parseResult.data;
      const timesheet = await timesheetsService.update(req.params.id, dto, req.tenantId!, req.user!.userId);

      res.json({
        success: true,
        data: timesheet,
        message: 'Timesheet actualizado exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

  async deleteTimesheet(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      await timesheetsService.delete(req.params.id, req.tenantId!, req.user!.userId);
      res.json({ success: true, message: 'Timesheet eliminado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async submitTimesheet(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const timesheet = await timesheetsService.submit(req.params.id, req.tenantId!, req.user!.userId);
      res.json({
        success: true,
        data: timesheet,
        message: 'Timesheet enviado para aprobación',
      });
    } catch (error) {
      next(error);
    }
  }

  async approveTimesheet(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const timesheet = await timesheetsService.approve(req.params.id, req.tenantId!, req.user!.userId);
      res.json({
        success: true,
        data: timesheet,
        message: 'Timesheet aprobado exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

  async rejectTimesheet(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const timesheet = await timesheetsService.reject(req.params.id, req.tenantId!, req.user!.userId);
      res.json({
        success: true,
        data: timesheet,
        message: 'Timesheet rechazado',
      });
    } catch (error) {
      next(error);
    }
  }

  async getMyTimesheets(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = timesheetQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }

      const filters: TimesheetFilters = queryResult.data;
      const result = await timesheetsService.getMyTimesheets(req.tenantId!, req.user!.userId, filters);

      res.json({
        success: true,
        data: result.data,
        meta: {
          total: result.total,
          page: filters.page,
          limit: filters.limit,
          totalPages: Math.ceil(result.total / (filters.limit || 20)),
        },
      });
    } catch (error) {
      next(error);
    }
  }

  async getPendingApprovals(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = timesheetQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }

      const filters: TimesheetFilters = queryResult.data;
      const result = await timesheetsService.getPendingApprovals(req.tenantId!, filters);

      res.json({
        success: true,
        data: result.data,
        meta: {
          total: result.total,
          page: filters.page,
          limit: filters.limit,
          totalPages: Math.ceil(result.total / (filters.limit || 20)),
        },
      });
    } catch (error) {
      next(error);
    }
  }
}

export const projectsController = new ProjectsController();
