import { Response, NextFunction } from 'express';
import { z } from 'zod';
import { messagesService, CreateMessageDto, MessageFilters, AddFollowerDto } from './messages.service.js';
import { notificationsService, CreateNotificationDto, NotificationFilters } from './notifications.service.js';
import { activitiesService, CreateActivityDto, UpdateActivityDto, ActivityFilters } from './activities.service.js';
import { AuthenticatedRequest } from '../../shared/middleware/auth.middleware.js';
import { ValidationError } from '../../shared/errors/index.js';

// ========== MESSAGE SCHEMAS ==========
const createMessageSchema = z.object({
  model: z.string().min(1).max(100),
  record_id: z.string().uuid(),
  message_type: z.enum(['comment', 'note', 'email', 'notification', 'system']).default('comment'),
  subject: z.string().max(255).optional(),
  body: z.string().min(1),
  parent_id: z.string().uuid().optional(),
});

const messageQuerySchema = z.object({
  model: z.string().optional(),
  record_id: z.string().uuid().optional(),
  message_type: z.enum(['comment', 'note', 'email', 'notification', 'system']).optional(),
  author_id: z.string().uuid().optional(),
  search: z.string().optional(),
  page: z.coerce.number().int().positive().default(1),
  limit: z.coerce.number().int().positive().max(100).default(50),
});

const addFollowerSchema = z.object({
  model: z.string().min(1).max(100),
  record_id: z.string().uuid(),
  user_id: z.string().uuid().optional(),
  partner_id: z.string().uuid().optional(),
  email_notifications: z.boolean().default(true),
}).refine(data => data.user_id || data.partner_id, {
  message: 'Debe especificar user_id o partner_id',
});

// ========== NOTIFICATION SCHEMAS ==========
const createNotificationSchema = z.object({
  user_id: z.string().uuid(),
  title: z.string().min(1).max(255),
  message: z.string().min(1),
  url: z.string().max(500).optional(),
  model: z.string().max(100).optional(),
  record_id: z.string().uuid().optional(),
});

const notificationQuerySchema = z.object({
  user_id: z.string().uuid().optional(),
  status: z.enum(['pending', 'sent', 'read', 'failed']).optional(),
  unread_only: z.coerce.boolean().optional(),
  model: z.string().optional(),
  search: z.string().optional(),
  page: z.coerce.number().int().positive().default(1),
  limit: z.coerce.number().int().positive().max(100).default(50),
});

// ========== ACTIVITY SCHEMAS ==========
const createActivitySchema = z.object({
  model: z.string().min(1).max(100),
  record_id: z.string().uuid(),
  activity_type: z.enum(['call', 'meeting', 'email', 'todo', 'follow_up', 'custom']),
  summary: z.string().min(1).max(255),
  description: z.string().optional(),
  assigned_to: z.string().uuid().optional(),
  due_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
  due_time: z.string().regex(/^\d{2}:\d{2}(:\d{2})?$/).optional(),
});

const updateActivitySchema = z.object({
  activity_type: z.enum(['call', 'meeting', 'email', 'todo', 'follow_up', 'custom']).optional(),
  summary: z.string().min(1).max(255).optional(),
  description: z.string().optional().nullable(),
  assigned_to: z.string().uuid().optional().nullable(),
  due_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
  due_time: z.string().regex(/^\d{2}:\d{2}(:\d{2})?$/).optional().nullable(),
});

const rescheduleActivitySchema = z.object({
  due_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
  due_time: z.string().regex(/^\d{2}:\d{2}(:\d{2})?$/).optional().nullable(),
});

const activityQuerySchema = z.object({
  model: z.string().optional(),
  record_id: z.string().uuid().optional(),
  activity_type: z.enum(['call', 'meeting', 'email', 'todo', 'follow_up', 'custom']).optional(),
  assigned_to: z.string().uuid().optional(),
  status: z.enum(['planned', 'done', 'cancelled', 'overdue']).optional(),
  due_from: z.string().optional(),
  due_to: z.string().optional(),
  overdue_only: z.coerce.boolean().optional(),
  search: z.string().optional(),
  page: z.coerce.number().int().positive().default(1),
  limit: z.coerce.number().int().positive().max(100).default(50),
});

class SystemController {
  // ========== MESSAGES ==========
  async getMessages(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = messageQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }
      const filters: MessageFilters = queryResult.data;
      const result = await messagesService.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 || 50)) },
      });
    } catch (error) {
      next(error);
    }
  }

  async getMessagesByRecord(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const { model, recordId } = req.params;
      const messages = await messagesService.findByRecord(model, recordId, req.tenantId!);
      res.json({ success: true, data: messages });
    } catch (error) {
      next(error);
    }
  }

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

  async createMessage(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createMessageSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de mensaje inválidos', parseResult.error.errors);
      }
      const dto: CreateMessageDto = parseResult.data;
      const message = await messagesService.create(dto, req.tenantId!, req.user!.userId);
      res.status(201).json({ success: true, data: message, message: 'Mensaje creado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

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

  // ========== FOLLOWERS ==========
  async getFollowers(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const { model, recordId } = req.params;
      const followers = await messagesService.getFollowers(model, recordId);
      res.json({ success: true, data: followers });
    } catch (error) {
      next(error);
    }
  }

  async addFollower(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = addFollowerSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de seguidor inválidos', parseResult.error.errors);
      }
      const dto: AddFollowerDto = parseResult.data;
      const follower = await messagesService.addFollower(dto);
      res.status(201).json({ success: true, data: follower, message: 'Seguidor agregado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async removeFollower(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const { model, recordId } = req.params;
      const { user_id, partner_id } = req.query;
      await messagesService.removeFollower(model, recordId, user_id as string, partner_id as string);
      res.json({ success: true, message: 'Seguidor eliminado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  // ========== NOTIFICATIONS ==========
  async getNotifications(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = notificationQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }
      const filters: NotificationFilters = queryResult.data;
      const result = await notificationsService.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 || 50)) },
      });
    } catch (error) {
      next(error);
    }
  }

  async getMyNotifications(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const unreadOnly = req.query.unread_only === 'true';
      const notifications = await notificationsService.findByUser(req.user!.userId, req.tenantId!, unreadOnly);
      const unreadCount = await notificationsService.getUnreadCount(req.user!.userId, req.tenantId!);
      res.json({ success: true, data: notifications, meta: { unread_count: unreadCount } });
    } catch (error) {
      next(error);
    }
  }

  async getUnreadCount(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const count = await notificationsService.getUnreadCount(req.user!.userId, req.tenantId!);
      res.json({ success: true, data: { count } });
    } catch (error) {
      next(error);
    }
  }

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

  async createNotification(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createNotificationSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de notificación inválidos', parseResult.error.errors);
      }
      const dto: CreateNotificationDto = parseResult.data;
      const notification = await notificationsService.create(dto, req.tenantId!);
      res.status(201).json({ success: true, data: notification, message: 'Notificación creada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async markNotificationAsRead(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const notification = await notificationsService.markAsRead(req.params.id, req.tenantId!);
      res.json({ success: true, data: notification, message: 'Notificación marcada como leída' });
    } catch (error) {
      next(error);
    }
  }

  async markAllNotificationsAsRead(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const count = await notificationsService.markAllAsRead(req.user!.userId, req.tenantId!);
      res.json({ success: true, message: `${count} notificaciones marcadas como leídas` });
    } catch (error) {
      next(error);
    }
  }

  async deleteNotification(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      await notificationsService.delete(req.params.id, req.tenantId!);
      res.json({ success: true, message: 'Notificación eliminada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  // ========== ACTIVITIES ==========
  async getActivities(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = activityQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }
      const filters: ActivityFilters = queryResult.data;
      const result = await activitiesService.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 || 50)) },
      });
    } catch (error) {
      next(error);
    }
  }

  async getActivitiesByRecord(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const { model, recordId } = req.params;
      const activities = await activitiesService.findByRecord(model, recordId, req.tenantId!);
      res.json({ success: true, data: activities });
    } catch (error) {
      next(error);
    }
  }

  async getMyActivities(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const status = req.query.status as string | undefined;
      const activities = await activitiesService.findByUser(req.user!.userId, req.tenantId!, status);
      res.json({ success: true, data: activities });
    } catch (error) {
      next(error);
    }
  }

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

  async createActivity(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createActivitySchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de actividad inválidos', parseResult.error.errors);
      }
      const dto: CreateActivityDto = parseResult.data;
      const activity = await activitiesService.create(dto, req.tenantId!, req.user!.userId);
      res.status(201).json({ success: true, data: activity, message: 'Actividad creada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async updateActivity(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateActivitySchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de actividad inválidos', parseResult.error.errors);
      }
      const dto: UpdateActivityDto = parseResult.data;
      const activity = await activitiesService.update(req.params.id, dto, req.tenantId!);
      res.json({ success: true, data: activity, message: 'Actividad actualizada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async markActivityDone(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const activity = await activitiesService.markDone(req.params.id, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: activity, message: 'Actividad completada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async cancelActivity(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const activity = await activitiesService.cancel(req.params.id, req.tenantId!);
      res.json({ success: true, data: activity, message: 'Actividad cancelada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async rescheduleActivity(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = rescheduleActivitySchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de reprogramación inválidos', parseResult.error.errors);
      }
      const { due_date, due_time } = parseResult.data;
      const activity = await activitiesService.reschedule(req.params.id, due_date, due_time ?? null, req.tenantId!);
      res.json({ success: true, data: activity, message: 'Actividad reprogramada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

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

export const systemController = new SystemController();
