import { Response, NextFunction } from 'express';
import { z } from 'zod';
import { leadsService, CreateLeadDto, UpdateLeadDto, LeadFilters } from './leads.service.js';
import { opportunitiesService, CreateOpportunityDto, UpdateOpportunityDto, OpportunityFilters } from './opportunities.service.js';
import { stagesService, CreateLeadStageDto, UpdateLeadStageDto, CreateOpportunityStageDto, UpdateOpportunityStageDto, CreateLostReasonDto, UpdateLostReasonDto } from './stages.service.js';
import { AuthenticatedRequest } from '../../shared/middleware/auth.middleware.js';
import { ValidationError } from '../../shared/errors/index.js';

// Lead schemas
const createLeadSchema = z.object({
  company_id: z.string().uuid(),
  name: z.string().min(1).max(255),
  ref: z.string().max(100).optional(),
  contact_name: z.string().max(255).optional(),
  email: z.string().email().max(255).optional(),
  phone: z.string().max(50).optional(),
  mobile: z.string().max(50).optional(),
  website: z.string().url().max(255).optional(),
  company_prospect_name: z.string().max(255).optional(),
  job_position: z.string().max(100).optional(),
  industry: z.string().max(100).optional(),
  employee_count: z.string().max(50).optional(),
  annual_revenue: z.number().min(0).optional(),
  street: z.string().max(255).optional(),
  city: z.string().max(100).optional(),
  state: z.string().max(100).optional(),
  zip: z.string().max(20).optional(),
  country: z.string().max(100).optional(),
  stage_id: z.string().uuid().optional(),
  user_id: z.string().uuid().optional(),
  sales_team_id: z.string().uuid().optional(),
  source: z.enum(['website', 'phone', 'email', 'referral', 'social_media', 'advertising', 'event', 'other']).optional(),
  priority: z.number().int().min(0).max(3).optional(),
  probability: z.number().min(0).max(100).optional(),
  expected_revenue: z.number().min(0).optional(),
  date_deadline: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
  description: z.string().optional(),
  notes: z.string().optional(),
  tags: z.array(z.string()).optional(),
});

const updateLeadSchema = z.object({
  name: z.string().min(1).max(255).optional(),
  ref: z.string().max(100).optional().nullable(),
  contact_name: z.string().max(255).optional().nullable(),
  email: z.string().email().max(255).optional().nullable(),
  phone: z.string().max(50).optional().nullable(),
  mobile: z.string().max(50).optional().nullable(),
  website: z.string().url().max(255).optional().nullable(),
  company_prospect_name: z.string().max(255).optional().nullable(),
  job_position: z.string().max(100).optional().nullable(),
  industry: z.string().max(100).optional().nullable(),
  employee_count: z.string().max(50).optional().nullable(),
  annual_revenue: z.number().min(0).optional().nullable(),
  street: z.string().max(255).optional().nullable(),
  city: z.string().max(100).optional().nullable(),
  state: z.string().max(100).optional().nullable(),
  zip: z.string().max(20).optional().nullable(),
  country: z.string().max(100).optional().nullable(),
  stage_id: z.string().uuid().optional().nullable(),
  user_id: z.string().uuid().optional().nullable(),
  sales_team_id: z.string().uuid().optional().nullable(),
  source: z.enum(['website', 'phone', 'email', 'referral', 'social_media', 'advertising', 'event', 'other']).optional().nullable(),
  priority: z.number().int().min(0).max(3).optional(),
  probability: z.number().min(0).max(100).optional(),
  expected_revenue: z.number().min(0).optional().nullable(),
  date_deadline: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().nullable(),
  description: z.string().optional().nullable(),
  notes: z.string().optional().nullable(),
  tags: z.array(z.string()).optional().nullable(),
});

const leadQuerySchema = z.object({
  company_id: z.string().uuid().optional(),
  status: z.enum(['new', 'contacted', 'qualified', 'converted', 'lost']).optional(),
  stage_id: z.string().uuid().optional(),
  user_id: z.string().uuid().optional(),
  source: z.enum(['website', 'phone', 'email', 'referral', 'social_media', 'advertising', 'event', 'other']).optional(),
  priority: z.coerce.number().int().optional(),
  search: z.string().optional(),
  page: z.coerce.number().int().positive().default(1),
  limit: z.coerce.number().int().positive().max(100).default(20),
});

const lostSchema = z.object({
  lost_reason_id: z.string().uuid(),
  notes: z.string().optional(),
});

const moveStageSchema = z.object({
  stage_id: z.string().uuid(),
});

// Opportunity schemas
const createOpportunitySchema = z.object({
  company_id: z.string().uuid(),
  name: z.string().min(1).max(255),
  ref: z.string().max(100).optional(),
  partner_id: z.string().uuid(),
  contact_name: z.string().max(255).optional(),
  email: z.string().email().max(255).optional(),
  phone: z.string().max(50).optional(),
  stage_id: z.string().uuid().optional(),
  user_id: z.string().uuid().optional(),
  sales_team_id: z.string().uuid().optional(),
  priority: z.number().int().min(0).max(3).optional(),
  probability: z.number().min(0).max(100).optional(),
  expected_revenue: z.number().min(0).optional(),
  recurring_revenue: z.number().min(0).optional(),
  recurring_plan: z.string().max(50).optional(),
  date_deadline: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
  source: z.enum(['website', 'phone', 'email', 'referral', 'social_media', 'advertising', 'event', 'other']).optional(),
  description: z.string().optional(),
  notes: z.string().optional(),
  tags: z.array(z.string()).optional(),
});

const updateOpportunitySchema = z.object({
  name: z.string().min(1).max(255).optional(),
  ref: z.string().max(100).optional().nullable(),
  partner_id: z.string().uuid().optional(),
  contact_name: z.string().max(255).optional().nullable(),
  email: z.string().email().max(255).optional().nullable(),
  phone: z.string().max(50).optional().nullable(),
  stage_id: z.string().uuid().optional().nullable(),
  user_id: z.string().uuid().optional().nullable(),
  sales_team_id: z.string().uuid().optional().nullable(),
  priority: z.number().int().min(0).max(3).optional(),
  probability: z.number().min(0).max(100).optional(),
  expected_revenue: z.number().min(0).optional().nullable(),
  recurring_revenue: z.number().min(0).optional().nullable(),
  recurring_plan: z.string().max(50).optional().nullable(),
  date_deadline: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().nullable(),
  description: z.string().optional().nullable(),
  notes: z.string().optional().nullable(),
  tags: z.array(z.string()).optional().nullable(),
});

const opportunityQuerySchema = z.object({
  company_id: z.string().uuid().optional(),
  status: z.enum(['open', 'won', 'lost']).optional(),
  stage_id: z.string().uuid().optional(),
  user_id: z.string().uuid().optional(),
  partner_id: z.string().uuid().optional(),
  priority: z.coerce.number().int().optional(),
  search: z.string().optional(),
  page: z.coerce.number().int().positive().default(1),
  limit: z.coerce.number().int().positive().max(100).default(20),
});

// Stage schemas
const createStageSchema = z.object({
  name: z.string().min(1).max(100),
  sequence: z.number().int().optional(),
  is_won: z.boolean().optional(),
  probability: z.number().min(0).max(100).optional(),
  requirements: z.string().optional(),
});

const updateStageSchema = z.object({
  name: z.string().min(1).max(100).optional(),
  sequence: z.number().int().optional(),
  is_won: z.boolean().optional(),
  probability: z.number().min(0).max(100).optional(),
  requirements: z.string().optional().nullable(),
  active: z.boolean().optional(),
});

// Lost reason schemas
const createLostReasonSchema = z.object({
  name: z.string().min(1).max(100),
  description: z.string().optional(),
});

const updateLostReasonSchema = z.object({
  name: z.string().min(1).max(100).optional(),
  description: z.string().optional().nullable(),
  active: z.boolean().optional(),
});

class CrmController {
  // ========== LEADS ==========

  async getLeads(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = leadQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parametros de consulta invalidos', queryResult.error.errors);
      }

      const filters: LeadFilters = queryResult.data;
      const result = await leadsService.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 getLead(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const lead = await leadsService.findById(req.params.id, req.tenantId!);
      res.json({ success: true, data: lead });
    } catch (error) {
      next(error);
    }
  }

  async createLead(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createLeadSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de lead invalidos', parseResult.error.errors);
      }

      const dto: CreateLeadDto = parseResult.data;
      const lead = await leadsService.create(dto, req.tenantId!, req.user!.userId);

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

  async updateLead(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateLeadSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de lead invalidos', parseResult.error.errors);
      }

      const dto: UpdateLeadDto = parseResult.data;
      const lead = await leadsService.update(req.params.id, dto, req.tenantId!, req.user!.userId);

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

  async moveLeadStage(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = moveStageSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos invalidos', parseResult.error.errors);
      }

      const lead = await leadsService.moveStage(req.params.id, parseResult.data.stage_id, req.tenantId!, req.user!.userId);

      res.json({
        success: true,
        data: lead,
        message: 'Lead movido a nueva etapa',
      });
    } catch (error) {
      next(error);
    }
  }

  async convertLead(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const result = await leadsService.convert(req.params.id, req.tenantId!, req.user!.userId);

      res.json({
        success: true,
        data: result.lead,
        opportunity_id: result.opportunity_id,
        message: 'Lead convertido a oportunidad exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

  async markLeadLost(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = lostSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos invalidos', parseResult.error.errors);
      }

      const lead = await leadsService.markLost(
        req.params.id,
        parseResult.data.lost_reason_id,
        parseResult.data.notes,
        req.tenantId!,
        req.user!.userId
      );

      res.json({
        success: true,
        data: lead,
        message: 'Lead marcado como perdido',
      });
    } catch (error) {
      next(error);
    }
  }

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

  // ========== OPPORTUNITIES ==========

  async getOpportunities(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = opportunityQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parametros de consulta invalidos', queryResult.error.errors);
      }

      const filters: OpportunityFilters = queryResult.data;
      const result = await opportunitiesService.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 getOpportunity(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const opportunity = await opportunitiesService.findById(req.params.id, req.tenantId!);
      res.json({ success: true, data: opportunity });
    } catch (error) {
      next(error);
    }
  }

  async createOpportunity(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createOpportunitySchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de oportunidad invalidos', parseResult.error.errors);
      }

      const dto: CreateOpportunityDto = parseResult.data;
      const opportunity = await opportunitiesService.create(dto, req.tenantId!, req.user!.userId);

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

  async updateOpportunity(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateOpportunitySchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de oportunidad invalidos', parseResult.error.errors);
      }

      const dto: UpdateOpportunityDto = parseResult.data;
      const opportunity = await opportunitiesService.update(req.params.id, dto, req.tenantId!, req.user!.userId);

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

  async moveOpportunityStage(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = moveStageSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos invalidos', parseResult.error.errors);
      }

      const opportunity = await opportunitiesService.moveStage(req.params.id, parseResult.data.stage_id, req.tenantId!, req.user!.userId);

      res.json({
        success: true,
        data: opportunity,
        message: 'Oportunidad movida a nueva etapa',
      });
    } catch (error) {
      next(error);
    }
  }

  async markOpportunityWon(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const opportunity = await opportunitiesService.markWon(req.params.id, req.tenantId!, req.user!.userId);

      res.json({
        success: true,
        data: opportunity,
        message: 'Oportunidad marcada como ganada',
      });
    } catch (error) {
      next(error);
    }
  }

  async markOpportunityLost(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = lostSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos invalidos', parseResult.error.errors);
      }

      const opportunity = await opportunitiesService.markLost(
        req.params.id,
        parseResult.data.lost_reason_id,
        parseResult.data.notes,
        req.tenantId!,
        req.user!.userId
      );

      res.json({
        success: true,
        data: opportunity,
        message: 'Oportunidad marcada como perdida',
      });
    } catch (error) {
      next(error);
    }
  }

  async createOpportunityQuotation(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const result = await opportunitiesService.createQuotation(req.params.id, req.tenantId!, req.user!.userId);

      res.status(201).json({
        success: true,
        data: result.opportunity,
        quotation_id: result.quotation_id,
        message: 'Cotizacion creada exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

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

  async getPipeline(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const companyId = req.query.company_id as string | undefined;
      const pipeline = await opportunitiesService.getPipeline(req.tenantId!, companyId);

      res.json({
        success: true,
        data: pipeline,
      });
    } catch (error) {
      next(error);
    }
  }

  // ========== LEAD STAGES ==========

  async getLeadStages(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const includeInactive = req.query.include_inactive === 'true';
      const stages = await stagesService.getLeadStages(req.tenantId!, includeInactive);
      res.json({ success: true, data: stages });
    } catch (error) {
      next(error);
    }
  }

  async createLeadStage(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createStageSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de etapa invalidos', parseResult.error.errors);
      }

      const dto: CreateLeadStageDto = parseResult.data;
      const stage = await stagesService.createLeadStage(dto, req.tenantId!);

      res.status(201).json({
        success: true,
        data: stage,
        message: 'Etapa de lead creada exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

  async updateLeadStage(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateStageSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de etapa invalidos', parseResult.error.errors);
      }

      const dto: UpdateLeadStageDto = parseResult.data;
      const stage = await stagesService.updateLeadStage(req.params.id, dto, req.tenantId!);

      res.json({
        success: true,
        data: stage,
        message: 'Etapa de lead actualizada exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

  async deleteLeadStage(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      await stagesService.deleteLeadStage(req.params.id, req.tenantId!);
      res.json({ success: true, message: 'Etapa de lead eliminada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  // ========== OPPORTUNITY STAGES ==========

  async getOpportunityStages(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const includeInactive = req.query.include_inactive === 'true';
      const stages = await stagesService.getOpportunityStages(req.tenantId!, includeInactive);
      res.json({ success: true, data: stages });
    } catch (error) {
      next(error);
    }
  }

  async createOpportunityStage(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createStageSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de etapa invalidos', parseResult.error.errors);
      }

      const dto: CreateOpportunityStageDto = parseResult.data;
      const stage = await stagesService.createOpportunityStage(dto, req.tenantId!);

      res.status(201).json({
        success: true,
        data: stage,
        message: 'Etapa de oportunidad creada exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

  async updateOpportunityStage(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateStageSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de etapa invalidos', parseResult.error.errors);
      }

      const dto: UpdateOpportunityStageDto = parseResult.data;
      const stage = await stagesService.updateOpportunityStage(req.params.id, dto, req.tenantId!);

      res.json({
        success: true,
        data: stage,
        message: 'Etapa de oportunidad actualizada exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

  async deleteOpportunityStage(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      await stagesService.deleteOpportunityStage(req.params.id, req.tenantId!);
      res.json({ success: true, message: 'Etapa de oportunidad eliminada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  // ========== LOST REASONS ==========

  async getLostReasons(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const includeInactive = req.query.include_inactive === 'true';
      const reasons = await stagesService.getLostReasons(req.tenantId!, includeInactive);
      res.json({ success: true, data: reasons });
    } catch (error) {
      next(error);
    }
  }

  async createLostReason(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createLostReasonSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de razon invalidos', parseResult.error.errors);
      }

      const dto: CreateLostReasonDto = parseResult.data;
      const reason = await stagesService.createLostReason(dto, req.tenantId!);

      res.status(201).json({
        success: true,
        data: reason,
        message: 'Razon de perdida creada exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

  async updateLostReason(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateLostReasonSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de razon invalidos', parseResult.error.errors);
      }

      const dto: UpdateLostReasonDto = parseResult.data;
      const reason = await stagesService.updateLostReason(req.params.id, dto, req.tenantId!);

      res.json({
        success: true,
        data: reason,
        message: 'Razon de perdida actualizada exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

  async deleteLostReason(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      await stagesService.deleteLostReason(req.params.id, req.tenantId!);
      res.json({ success: true, message: 'Razon de perdida eliminada exitosamente' });
    } catch (error) {
      next(error);
    }
  }
}

export const crmController = new CrmController();
