import { Response, NextFunction } from 'express';
import { z } from 'zod';
import { purchasesService, CreatePurchaseOrderDto, UpdatePurchaseOrderDto, PurchaseOrderFilters } from './purchases.service.js';
import { rfqsService, CreateRfqDto, UpdateRfqDto, CreateRfqLineDto, UpdateRfqLineDto, RfqFilters } from './rfqs.service.js';
import { AuthenticatedRequest } from '../../shared/middleware/auth.middleware.js';
import { ValidationError } from '../../shared/errors/index.js';

const orderLineSchema = z.object({
  product_id: z.string().uuid(),
  description: z.string().min(1),
  quantity: z.number().positive(),
  uom_id: z.string().uuid(),
  price_unit: z.number().min(0),
  discount: z.number().min(0).max(100).default(0),
  amount_untaxed: z.number().min(0),
});

const createOrderSchema = z.object({
  company_id: z.string().uuid(),
  name: z.string().min(1).max(100),
  ref: z.string().max(100).optional(),
  partner_id: z.string().uuid(),
  order_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
  expected_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
  currency_id: z.string().uuid(),
  payment_term_id: z.string().uuid().optional(),
  notes: z.string().optional(),
  lines: z.array(orderLineSchema).min(1),
});

const updateOrderSchema = z.object({
  ref: z.string().max(100).optional().nullable(),
  partner_id: z.string().uuid().optional(),
  order_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
  expected_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().nullable(),
  currency_id: z.string().uuid().optional(),
  payment_term_id: z.string().uuid().optional().nullable(),
  notes: z.string().optional().nullable(),
  lines: z.array(orderLineSchema).min(1).optional(),
});

const querySchema = z.object({
  company_id: z.string().uuid().optional(),
  partner_id: z.string().uuid().optional(),
  status: z.enum(['draft', 'sent', 'confirmed', 'done', 'cancelled']).optional(),
  date_from: z.string().optional(),
  date_to: z.string().optional(),
  search: z.string().optional(),
  page: z.coerce.number().int().positive().default(1),
  limit: z.coerce.number().int().positive().max(100).default(20),
});

// ========== RFQ SCHEMAS ==========
const rfqLineSchema = z.object({
  product_id: z.string().uuid().optional(),
  description: z.string().min(1),
  quantity: z.number().positive(),
  uom_id: z.string().uuid(),
});

const createRfqSchema = z.object({
  company_id: z.string().uuid(),
  partner_ids: z.array(z.string().uuid()).min(1),
  request_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
  deadline_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
  description: z.string().optional(),
  notes: z.string().optional(),
  lines: z.array(rfqLineSchema).min(1),
});

const updateRfqSchema = z.object({
  partner_ids: z.array(z.string().uuid()).min(1).optional(),
  deadline_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().nullable(),
  description: z.string().optional().nullable(),
  notes: z.string().optional().nullable(),
});

const createRfqLineSchema = z.object({
  product_id: z.string().uuid().optional(),
  description: z.string().min(1),
  quantity: z.number().positive(),
  uom_id: z.string().uuid(),
});

const updateRfqLineSchema = z.object({
  product_id: z.string().uuid().optional().nullable(),
  description: z.string().min(1).optional(),
  quantity: z.number().positive().optional(),
  uom_id: z.string().uuid().optional(),
});

const rfqQuerySchema = z.object({
  company_id: z.string().uuid().optional(),
  status: z.enum(['draft', 'sent', 'responded', 'accepted', 'rejected', 'cancelled']).optional(),
  date_from: z.string().optional(),
  date_to: z.string().optional(),
  search: z.string().optional(),
  page: z.coerce.number().int().positive().default(1),
  limit: z.coerce.number().int().positive().max(100).default(20),
});

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

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

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

      const dto: CreatePurchaseOrderDto = parseResult.data;
      const order = await purchasesService.create(dto, req.tenantId!, req.user!.userId);

      res.status(201).json({
        success: true,
        data: order,
        message: 'Orden de compra creada exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

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

      const dto: UpdatePurchaseOrderDto = parseResult.data;
      const order = await purchasesService.update(req.params.id, dto, req.tenantId!, req.user!.userId);

      res.json({
        success: true,
        data: order,
        message: 'Orden de compra actualizada exitosamente',
      });
    } catch (error) {
      next(error);
    }
  }

  async confirm(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const order = await purchasesService.confirm(req.params.id, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: order, message: 'Orden de compra confirmada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async cancel(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const order = await purchasesService.cancel(req.params.id, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: order, message: 'Orden de compra cancelada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

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

  // ========== RFQs ==========
  async getRfqs(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = rfqQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }
      const filters: RfqFilters = queryResult.data;
      const result = await rfqsService.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 getRfq(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const rfq = await rfqsService.findById(req.params.id, req.tenantId!);
      res.json({ success: true, data: rfq });
    } catch (error) {
      next(error);
    }
  }

  async createRfq(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createRfqSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de RFQ inválidos', parseResult.error.errors);
      }
      const dto: CreateRfqDto = parseResult.data;
      const rfq = await rfqsService.create(dto, req.tenantId!, req.user!.userId);
      res.status(201).json({ success: true, data: rfq, message: 'Solicitud de cotización creada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async updateRfq(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateRfqSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de RFQ inválidos', parseResult.error.errors);
      }
      const dto: UpdateRfqDto = parseResult.data;
      const rfq = await rfqsService.update(req.params.id, dto, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: rfq, message: 'Solicitud de cotización actualizada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async addRfqLine(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createRfqLineSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de línea inválidos', parseResult.error.errors);
      }
      const dto: CreateRfqLineDto = parseResult.data;
      const line = await rfqsService.addLine(req.params.id, dto, req.tenantId!);
      res.status(201).json({ success: true, data: line, message: 'Línea agregada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async updateRfqLine(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateRfqLineSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de línea inválidos', parseResult.error.errors);
      }
      const dto: UpdateRfqLineDto = parseResult.data;
      const line = await rfqsService.updateLine(req.params.id, req.params.lineId, dto, req.tenantId!);
      res.json({ success: true, data: line, message: 'Línea actualizada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async removeRfqLine(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      await rfqsService.removeLine(req.params.id, req.params.lineId, req.tenantId!);
      res.json({ success: true, message: 'Línea eliminada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async sendRfq(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const rfq = await rfqsService.send(req.params.id, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: rfq, message: 'Solicitud enviada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async markRfqResponded(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const rfq = await rfqsService.markResponded(req.params.id, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: rfq, message: 'Solicitud marcada como respondida' });
    } catch (error) {
      next(error);
    }
  }

  async acceptRfq(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const rfq = await rfqsService.accept(req.params.id, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: rfq, message: 'Solicitud aceptada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async rejectRfq(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const rfq = await rfqsService.reject(req.params.id, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: rfq, message: 'Solicitud rechazada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async cancelRfq(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const rfq = await rfqsService.cancel(req.params.id, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: rfq, message: 'Solicitud cancelada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

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

export const purchasesController = new PurchasesController();
