import { Response, NextFunction } from 'express';
import { z } from 'zod';
import { currenciesService, CreateCurrencyDto, UpdateCurrencyDto } from './currencies.service.js';
import { countriesService } from './countries.service.js';
import { statesService, CreateStateDto, UpdateStateDto } from './states.service.js';
import { currencyRatesService, CreateCurrencyRateDto, ConvertCurrencyDto } from './currency-rates.service.js';
import { uomService, CreateUomDto, UpdateUomDto } from './uom.service.js';
import { productCategoriesService, CreateProductCategoryDto, UpdateProductCategoryDto } from './product-categories.service.js';
import { paymentTermsService, CreatePaymentTermDto, UpdatePaymentTermDto } from './payment-terms.service.js';
import { discountRulesService, CreateDiscountRuleDto, UpdateDiscountRuleDto, ApplyDiscountContext } from './discount-rules.service.js';
import { PaymentTermLineType } from './entities/payment-term.entity.js';
import { DiscountType, DiscountAppliesTo, DiscountCondition } from './entities/discount-rule.entity.js';
import { AuthenticatedRequest } from '../../shared/middleware/auth.middleware.js';
import { ValidationError } from '../../shared/errors/index.js';

// Schemas
const createCurrencySchema = z.object({
  code: z.string().length(3, 'El código debe tener 3 caracteres').toUpperCase(),
  name: z.string().min(1, 'El nombre es requerido').max(100),
  symbol: z.string().min(1).max(10),
  decimal_places: z.number().int().min(0).max(6).optional(),
  decimals: z.number().int().min(0).max(6).optional(), // Accept camelCase
}).refine((data) => data.decimal_places !== undefined || data.decimals !== undefined, {
  message: 'decimal_places or decimals is required',
});

const updateCurrencySchema = z.object({
  name: z.string().min(1).max(100).optional(),
  symbol: z.string().min(1).max(10).optional(),
  decimal_places: z.number().int().min(0).max(6).optional(),
  decimals: z.number().int().min(0).max(6).optional(), // Accept camelCase
  active: z.boolean().optional(),
});

const createUomSchema = z.object({
  name: z.string().min(1, 'El nombre es requerido').max(100),
  code: z.string().min(1).max(20),
  category_id: z.string().uuid().optional(),
  categoryId: z.string().uuid().optional(), // Accept camelCase
  uom_type: z.enum(['reference', 'bigger', 'smaller']).optional(),
  uomType: z.enum(['reference', 'bigger', 'smaller']).optional(), // Accept camelCase
  ratio: z.number().positive().default(1),
}).refine((data) => data.category_id !== undefined || data.categoryId !== undefined, {
  message: 'category_id or categoryId is required',
});

const updateUomSchema = z.object({
  name: z.string().min(1).max(100).optional(),
  ratio: z.number().positive().optional(),
  active: z.boolean().optional(),
});

const createCategorySchema = z.object({
  name: z.string().min(1, 'El nombre es requerido').max(100),
  code: z.string().min(1).max(50),
  parent_id: z.string().uuid().optional(),
  parentId: z.string().uuid().optional(), // Accept camelCase
});

const updateCategorySchema = z.object({
  name: z.string().min(1).max(100).optional(),
  parent_id: z.string().uuid().optional().nullable(),
  parentId: z.string().uuid().optional().nullable(), // Accept camelCase
  active: z.boolean().optional(),
});

// Payment Terms Schemas
const paymentTermLineSchema = z.object({
  sequence: z.number().int().min(1).optional(),
  line_type: z.enum(['balance', 'percent', 'fixed']).optional(),
  lineType: z.enum(['balance', 'percent', 'fixed']).optional(),
  value_percent: z.number().min(0).max(100).optional(),
  valuePercent: z.number().min(0).max(100).optional(),
  value_amount: z.number().min(0).optional(),
  valueAmount: z.number().min(0).optional(),
  days: z.number().int().min(0).optional(),
  day_of_month: z.number().int().min(1).max(31).optional(),
  dayOfMonth: z.number().int().min(1).max(31).optional(),
  end_of_month: z.boolean().optional(),
  endOfMonth: z.boolean().optional(),
});

const createPaymentTermSchema = z.object({
  code: z.string().min(1).max(50),
  name: z.string().min(1).max(255),
  description: z.string().optional(),
  due_days: z.number().int().min(0).optional(),
  dueDays: z.number().int().min(0).optional(),
  discount_percent: z.number().min(0).max(100).optional(),
  discountPercent: z.number().min(0).max(100).optional(),
  discount_days: z.number().int().min(0).optional(),
  discountDays: z.number().int().min(0).optional(),
  is_immediate: z.boolean().optional(),
  isImmediate: z.boolean().optional(),
  lines: z.array(paymentTermLineSchema).optional(),
});

const updatePaymentTermSchema = z.object({
  name: z.string().min(1).max(255).optional(),
  description: z.string().optional().nullable(),
  due_days: z.number().int().min(0).optional(),
  dueDays: z.number().int().min(0).optional(),
  discount_percent: z.number().min(0).max(100).optional().nullable(),
  discountPercent: z.number().min(0).max(100).optional().nullable(),
  discount_days: z.number().int().min(0).optional().nullable(),
  discountDays: z.number().int().min(0).optional().nullable(),
  is_immediate: z.boolean().optional(),
  isImmediate: z.boolean().optional(),
  is_active: z.boolean().optional(),
  isActive: z.boolean().optional(),
  lines: z.array(paymentTermLineSchema).optional(),
});

const calculateDueDateSchema = z.object({
  invoice_date: z.string().datetime().optional(),
  invoiceDate: z.string().datetime().optional(),
  total_amount: z.number().min(0),
  totalAmount: z.number().min(0).optional(),
});

// Discount Rules Schemas
const createDiscountRuleSchema = z.object({
  code: z.string().min(1).max(50),
  name: z.string().min(1).max(255),
  description: z.string().optional(),
  discount_type: z.enum(['percentage', 'fixed', 'price_override']).optional(),
  discountType: z.enum(['percentage', 'fixed', 'price_override']).optional(),
  discount_value: z.number().min(0),
  discountValue: z.number().min(0).optional(),
  max_discount_amount: z.number().min(0).optional().nullable(),
  maxDiscountAmount: z.number().min(0).optional().nullable(),
  applies_to: z.enum(['all', 'category', 'product', 'customer', 'customer_group']).optional(),
  appliesTo: z.enum(['all', 'category', 'product', 'customer', 'customer_group']).optional(),
  applies_to_id: z.string().uuid().optional().nullable(),
  appliesToId: z.string().uuid().optional().nullable(),
  condition_type: z.enum(['none', 'min_quantity', 'min_amount', 'date_range', 'first_purchase']).optional(),
  conditionType: z.enum(['none', 'min_quantity', 'min_amount', 'date_range', 'first_purchase']).optional(),
  condition_value: z.number().optional().nullable(),
  conditionValue: z.number().optional().nullable(),
  start_date: z.string().datetime().optional().nullable(),
  startDate: z.string().datetime().optional().nullable(),
  end_date: z.string().datetime().optional().nullable(),
  endDate: z.string().datetime().optional().nullable(),
  priority: z.number().int().min(0).optional(),
  combinable: z.boolean().optional(),
  usage_limit: z.number().int().min(0).optional().nullable(),
  usageLimit: z.number().int().min(0).optional().nullable(),
});

const updateDiscountRuleSchema = z.object({
  name: z.string().min(1).max(255).optional(),
  description: z.string().optional().nullable(),
  discount_type: z.enum(['percentage', 'fixed', 'price_override']).optional(),
  discountType: z.enum(['percentage', 'fixed', 'price_override']).optional(),
  discount_value: z.number().min(0).optional(),
  discountValue: z.number().min(0).optional(),
  max_discount_amount: z.number().min(0).optional().nullable(),
  maxDiscountAmount: z.number().min(0).optional().nullable(),
  applies_to: z.enum(['all', 'category', 'product', 'customer', 'customer_group']).optional(),
  appliesTo: z.enum(['all', 'category', 'product', 'customer', 'customer_group']).optional(),
  applies_to_id: z.string().uuid().optional().nullable(),
  appliesToId: z.string().uuid().optional().nullable(),
  condition_type: z.enum(['none', 'min_quantity', 'min_amount', 'date_range', 'first_purchase']).optional(),
  conditionType: z.enum(['none', 'min_quantity', 'min_amount', 'date_range', 'first_purchase']).optional(),
  condition_value: z.number().optional().nullable(),
  conditionValue: z.number().optional().nullable(),
  start_date: z.string().datetime().optional().nullable(),
  startDate: z.string().datetime().optional().nullable(),
  end_date: z.string().datetime().optional().nullable(),
  endDate: z.string().datetime().optional().nullable(),
  priority: z.number().int().min(0).optional(),
  combinable: z.boolean().optional(),
  usage_limit: z.number().int().min(0).optional().nullable(),
  usageLimit: z.number().int().min(0).optional().nullable(),
  is_active: z.boolean().optional(),
  isActive: z.boolean().optional(),
});

const applyDiscountsSchema = z.object({
  product_id: z.string().uuid().optional(),
  productId: z.string().uuid().optional(),
  category_id: z.string().uuid().optional(),
  categoryId: z.string().uuid().optional(),
  customer_id: z.string().uuid().optional(),
  customerId: z.string().uuid().optional(),
  customer_group_id: z.string().uuid().optional(),
  customerGroupId: z.string().uuid().optional(),
  quantity: z.number().min(0),
  unit_price: z.number().min(0),
  unitPrice: z.number().min(0).optional(),
  total_amount: z.number().min(0),
  totalAmount: z.number().min(0).optional(),
  is_first_purchase: z.boolean().optional(),
  isFirstPurchase: z.boolean().optional(),
});

// States Schemas
const createStateSchema = z.object({
  country_id: z.string().uuid().optional(),
  countryId: z.string().uuid().optional(),
  code: z.string().min(1).max(10).toUpperCase(),
  name: z.string().min(1).max(255),
  timezone: z.string().max(50).optional(),
  is_active: z.boolean().optional(),
  isActive: z.boolean().optional(),
}).refine((data) => data.country_id !== undefined || data.countryId !== undefined, {
  message: 'country_id or countryId is required',
});

const updateStateSchema = z.object({
  name: z.string().min(1).max(255).optional(),
  timezone: z.string().max(50).optional().nullable(),
  is_active: z.boolean().optional(),
  isActive: z.boolean().optional(),
});

// Currency Rates Schemas
const createCurrencyRateSchema = z.object({
  from_currency_code: z.string().length(3).toUpperCase().optional(),
  fromCurrencyCode: z.string().length(3).toUpperCase().optional(),
  to_currency_code: z.string().length(3).toUpperCase().optional(),
  toCurrencyCode: z.string().length(3).toUpperCase().optional(),
  rate: z.number().positive(),
  rate_date: z.string().optional(),
  rateDate: z.string().optional(),
  source: z.enum(['manual', 'banxico', 'xe', 'openexchange']).optional(),
}).refine((data) => data.from_currency_code !== undefined || data.fromCurrencyCode !== undefined, {
  message: 'from_currency_code or fromCurrencyCode is required',
}).refine((data) => data.to_currency_code !== undefined || data.toCurrencyCode !== undefined, {
  message: 'to_currency_code or toCurrencyCode is required',
});

const convertCurrencySchema = z.object({
  amount: z.number().min(0),
  from_currency_code: z.string().length(3).toUpperCase().optional(),
  fromCurrencyCode: z.string().length(3).toUpperCase().optional(),
  to_currency_code: z.string().length(3).toUpperCase().optional(),
  toCurrencyCode: z.string().length(3).toUpperCase().optional(),
  date: z.string().optional(),
}).refine((data) => data.from_currency_code !== undefined || data.fromCurrencyCode !== undefined, {
  message: 'from_currency_code or fromCurrencyCode is required',
}).refine((data) => data.to_currency_code !== undefined || data.toCurrencyCode !== undefined, {
  message: 'to_currency_code or toCurrencyCode is required',
});

class CoreController {
  // ========== CURRENCIES ==========
  async getCurrencies(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const activeOnly = req.query.active === 'true';
      const currencies = await currenciesService.findAll(activeOnly);
      res.json({ success: true, data: currencies });
    } catch (error) {
      next(error);
    }
  }

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

  async createCurrency(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createCurrencySchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de moneda inválidos', parseResult.error.errors);
      }
      const dto: CreateCurrencyDto = parseResult.data;
      const currency = await currenciesService.create(dto);
      res.status(201).json({ success: true, data: currency, message: 'Moneda creada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async updateCurrency(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateCurrencySchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de moneda inválidos', parseResult.error.errors);
      }
      const dto: UpdateCurrencyDto = parseResult.data;
      const currency = await currenciesService.update(req.params.id, dto);
      res.json({ success: true, data: currency, message: 'Moneda actualizada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  // ========== COUNTRIES ==========
  async getCountries(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const countries = await countriesService.findAll();
      res.json({ success: true, data: countries });
    } catch (error) {
      next(error);
    }
  }

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

  // ========== STATES ==========
  async getStates(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const filter = {
        countryId: req.query.country_id as string | undefined,
        countryCode: req.query.country_code as string | undefined,
        isActive: req.query.active === 'true' ? true : undefined,
      };
      const states = await statesService.findAll(filter);
      res.json({ success: true, data: states });
    } catch (error) {
      next(error);
    }
  }

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

  async getStatesByCountry(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const states = await statesService.findByCountry(req.params.countryId);
      res.json({ success: true, data: states });
    } catch (error) {
      next(error);
    }
  }

  async getStatesByCountryCode(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const states = await statesService.findByCountryCode(req.params.countryCode);
      res.json({ success: true, data: states });
    } catch (error) {
      next(error);
    }
  }

  async createState(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createStateSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de estado inválidos', parseResult.error.errors);
      }
      const data = parseResult.data;
      const dto: CreateStateDto = {
        countryId: data.country_id ?? data.countryId!,
        code: data.code,
        name: data.name,
        timezone: data.timezone,
        isActive: data.is_active ?? data.isActive,
      };
      const state = await statesService.create(dto);
      res.status(201).json({ success: true, data: state, message: 'Estado creado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async updateState(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateStateSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de estado inválidos', parseResult.error.errors);
      }
      const data = parseResult.data;
      const dto: UpdateStateDto = {
        name: data.name,
        timezone: data.timezone ?? undefined,
        isActive: data.is_active ?? data.isActive,
      };
      const state = await statesService.update(req.params.id, dto);
      res.json({ success: true, data: state, message: 'Estado actualizado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

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

  // ========== CURRENCY RATES ==========
  async getCurrencyRates(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const filter = {
        tenantId: req.tenantId,
        fromCurrencyCode: req.query.from as string | undefined,
        toCurrencyCode: req.query.to as string | undefined,
        limit: req.query.limit ? parseInt(req.query.limit as string, 10) : 100,
      };
      const rates = await currencyRatesService.findAll(filter);
      res.json({ success: true, data: rates });
    } catch (error) {
      next(error);
    }
  }

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

  async getLatestRate(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const fromCode = req.params.from.toUpperCase();
      const toCode = req.params.to.toUpperCase();
      const dateStr = req.query.date as string | undefined;
      const date = dateStr ? new Date(dateStr) : new Date();

      const rate = await currencyRatesService.getRate(fromCode, toCode, date, req.tenantId);

      if (rate === null) {
        res.status(404).json({
          success: false,
          message: `No se encontró tipo de cambio para ${fromCode}/${toCode}`
        });
        return;
      }

      res.json({
        success: true,
        data: {
          from: fromCode,
          to: toCode,
          rate,
          date: date.toISOString().split('T')[0]
        }
      });
    } catch (error) {
      next(error);
    }
  }

  async createCurrencyRate(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createCurrencyRateSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de tipo de cambio inválidos', parseResult.error.errors);
      }
      const data = parseResult.data;
      const dto: CreateCurrencyRateDto = {
        tenantId: req.tenantId,
        fromCurrencyCode: data.from_currency_code ?? data.fromCurrencyCode!,
        toCurrencyCode: data.to_currency_code ?? data.toCurrencyCode!,
        rate: data.rate,
        rateDate: data.rate_date ?? data.rateDate ? new Date(data.rate_date ?? data.rateDate!) : new Date(),
        source: data.source,
        createdBy: req.user?.userId,
      };
      const rate = await currencyRatesService.create(dto);
      res.status(201).json({ success: true, data: rate, message: 'Tipo de cambio creado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async convertCurrency(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = convertCurrencySchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de conversión inválidos', parseResult.error.errors);
      }
      const data = parseResult.data;
      const dto: ConvertCurrencyDto = {
        amount: data.amount,
        fromCurrencyCode: data.from_currency_code ?? data.fromCurrencyCode!,
        toCurrencyCode: data.to_currency_code ?? data.toCurrencyCode!,
        date: data.date ? new Date(data.date) : new Date(),
        tenantId: req.tenantId,
      };
      const result = await currencyRatesService.convert(dto);

      if (result === null) {
        res.status(404).json({
          success: false,
          message: `No se encontró tipo de cambio para ${dto.fromCurrencyCode}/${dto.toCurrencyCode}`
        });
        return;
      }

      res.json({
        success: true,
        data: {
          originalAmount: dto.amount,
          convertedAmount: result.amount,
          rate: result.rate,
          from: dto.fromCurrencyCode,
          to: dto.toCurrencyCode,
        }
      });
    } catch (error) {
      next(error);
    }
  }

  async deleteCurrencyRate(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      await currencyRatesService.delete(req.params.id);
      res.json({ success: true, message: 'Tipo de cambio eliminado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async getCurrencyRateHistory(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const fromCode = req.params.from.toUpperCase();
      const toCode = req.params.to.toUpperCase();
      const days = req.query.days ? parseInt(req.query.days as string, 10) : 30;

      const history = await currencyRatesService.getHistory(fromCode, toCode, days, req.tenantId);
      res.json({ success: true, data: history });
    } catch (error) {
      next(error);
    }
  }

  async getLatestRates(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const baseCurrency = (req.query.base as string) || 'MXN';
      const ratesMap = await currencyRatesService.getLatestRates(baseCurrency, req.tenantId);

      // Convert Map to object for JSON response
      const rates: Record<string, number> = {};
      ratesMap.forEach((value, key) => {
        rates[key] = value;
      });

      res.json({
        success: true,
        data: {
          base: baseCurrency,
          rates,
          date: new Date().toISOString().split('T')[0],
        }
      });
    } catch (error) {
      next(error);
    }
  }

  // ========== UOM CATEGORIES ==========
  async getUomCategories(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const activeOnly = req.query.active === 'true';
      const categories = await uomService.findAllCategories(activeOnly);
      res.json({ success: true, data: categories });
    } catch (error) {
      next(error);
    }
  }

  async getUomCategory(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const category = await uomService.findCategoryById(req.params.id);
      res.json({ success: true, data: category });
    } catch (error) {
      next(error);
    }
  }

  // ========== UOM ==========
  async getUoms(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const activeOnly = req.query.active === 'true';
      const categoryId = req.query.category_id as string | undefined;
      const uoms = await uomService.findAll(categoryId, activeOnly);
      res.json({ success: true, data: uoms });
    } catch (error) {
      next(error);
    }
  }

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

  async createUom(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createUomSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de UdM inválidos', parseResult.error.errors);
      }
      const dto: CreateUomDto = parseResult.data;
      const uom = await uomService.create(dto);
      res.status(201).json({ success: true, data: uom, message: 'Unidad de medida creada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async updateUom(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateUomSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de UdM inválidos', parseResult.error.errors);
      }
      const dto: UpdateUomDto = parseResult.data;
      const uom = await uomService.update(req.params.id, dto);
      res.json({ success: true, data: uom, message: 'Unidad de medida actualizada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async getUomByCode(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const uom = await uomService.findByCode(req.params.code);
      if (!uom) {
        res.status(404).json({ success: false, message: 'Unidad de medida no encontrada' });
        return;
      }
      res.json({ success: true, data: uom });
    } catch (error) {
      next(error);
    }
  }

  async convertUom(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const { quantity, from_uom_id, fromUomId, to_uom_id, toUomId } = req.body;
      const fromId = from_uom_id ?? fromUomId;
      const toId = to_uom_id ?? toUomId;

      if (!quantity || !fromId || !toId) {
        throw new ValidationError('Se requiere quantity, from_uom_id y to_uom_id');
      }

      const result = await uomService.convertQuantity(quantity, fromId, toId);
      const fromUom = await uomService.findById(fromId);
      const toUom = await uomService.findById(toId);

      res.json({
        success: true,
        data: {
          originalQuantity: quantity,
          originalUom: fromUom.name,
          convertedQuantity: result,
          targetUom: toUom.name,
        }
      });
    } catch (error) {
      next(error);
    }
  }

  async getUomConversions(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const result = await uomService.getConversionTable(req.params.categoryId);
      res.json({ success: true, data: result });
    } catch (error) {
      next(error);
    }
  }

  // ========== PRODUCT CATEGORIES ==========
  async getProductCategories(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const activeOnly = req.query.active === 'true';
      const parentId = req.query.parent_id as string | undefined;
      const categories = await productCategoriesService.findAll(req.tenantId!, parentId, activeOnly);
      res.json({ success: true, data: categories });
    } catch (error) {
      next(error);
    }
  }

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

  async createProductCategory(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createCategorySchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de categoría inválidos', parseResult.error.errors);
      }
      const dto: CreateProductCategoryDto = parseResult.data;
      const category = await productCategoriesService.create(dto, req.tenantId!, req.user!.userId);
      res.status(201).json({ success: true, data: category, message: 'Categoría creada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async updateProductCategory(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateCategorySchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de categoría inválidos', parseResult.error.errors);
      }
      const dto: UpdateProductCategoryDto = parseResult.data;
      const category = await productCategoriesService.update(req.params.id, dto, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: category, message: 'Categoría actualizada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

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

  // ========== PAYMENT TERMS ==========
  async getPaymentTerms(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const activeOnly = req.query.active === 'true';
      const paymentTerms = await paymentTermsService.findAll(req.tenantId!, activeOnly);
      res.json({ success: true, data: paymentTerms });
    } catch (error) {
      next(error);
    }
  }

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

  async createPaymentTerm(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createPaymentTermSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de término de pago inválidos', parseResult.error.errors);
      }
      const dto: CreatePaymentTermDto = parseResult.data;
      const paymentTerm = await paymentTermsService.create(dto, req.tenantId!, req.user?.userId);
      res.status(201).json({ success: true, data: paymentTerm, message: 'Término de pago creado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async updatePaymentTerm(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updatePaymentTermSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de término de pago inválidos', parseResult.error.errors);
      }
      const dto: UpdatePaymentTermDto = parseResult.data;
      const paymentTerm = await paymentTermsService.update(req.params.id, dto, req.tenantId!, req.user?.userId);
      res.json({ success: true, data: paymentTerm, message: 'Término de pago actualizado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async deletePaymentTerm(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      await paymentTermsService.delete(req.params.id, req.tenantId!, req.user?.userId);
      res.json({ success: true, message: 'Término de pago eliminado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async calculateDueDate(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = calculateDueDateSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos inválidos para cálculo', parseResult.error.errors);
      }
      const paymentTerm = await paymentTermsService.findById(req.params.id, req.tenantId!);
      const invoiceDate = parseResult.data.invoice_date ?? parseResult.data.invoiceDate ?? new Date().toISOString();
      const totalAmount = parseResult.data.total_amount ?? parseResult.data.totalAmount ?? 0;
      const result = paymentTermsService.calculateDueDate(paymentTerm, new Date(invoiceDate), totalAmount);
      res.json({ success: true, data: result });
    } catch (error) {
      next(error);
    }
  }

  async getStandardPaymentTerms(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const standardTerms = paymentTermsService.getStandardTerms();
      res.json({ success: true, data: standardTerms });
    } catch (error) {
      next(error);
    }
  }

  async initializePaymentTerms(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      await paymentTermsService.initializeForTenant(req.tenantId!, req.user?.userId);
      res.json({ success: true, message: 'Términos de pago inicializados exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  // ========== DISCOUNT RULES ==========
  async getDiscountRules(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const activeOnly = req.query.active === 'true';
      const discountRules = await discountRulesService.findAll(req.tenantId!, activeOnly);
      res.json({ success: true, data: discountRules });
    } catch (error) {
      next(error);
    }
  }

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

  async createDiscountRule(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createDiscountRuleSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de regla de descuento inválidos', parseResult.error.errors);
      }
      const dto: CreateDiscountRuleDto = parseResult.data;
      const discountRule = await discountRulesService.create(dto, req.tenantId!, req.user?.userId);
      res.status(201).json({ success: true, data: discountRule, message: 'Regla de descuento creada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async updateDiscountRule(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateDiscountRuleSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de regla de descuento inválidos', parseResult.error.errors);
      }
      const dto: UpdateDiscountRuleDto = parseResult.data;
      const discountRule = await discountRulesService.update(req.params.id, dto, req.tenantId!, req.user?.userId);
      res.json({ success: true, data: discountRule, message: 'Regla de descuento actualizada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

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

  async applyDiscounts(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = applyDiscountsSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos inválidos para aplicar descuentos', parseResult.error.errors);
      }
      const data = parseResult.data;
      const context: ApplyDiscountContext = {
        productId: data.product_id ?? data.productId,
        categoryId: data.category_id ?? data.categoryId,
        customerId: data.customer_id ?? data.customerId,
        customerGroupId: data.customer_group_id ?? data.customerGroupId,
        quantity: data.quantity,
        unitPrice: data.unit_price ?? data.unitPrice ?? 0,
        totalAmount: data.total_amount ?? data.totalAmount ?? 0,
        isFirstPurchase: data.is_first_purchase ?? data.isFirstPurchase,
      };
      const result = await discountRulesService.applyDiscounts(req.tenantId!, context);
      res.json({ success: true, data: result });
    } catch (error) {
      next(error);
    }
  }

  async resetDiscountRuleUsage(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const discountRule = await discountRulesService.resetUsageCount(req.params.id, req.tenantId!);
      res.json({ success: true, data: discountRule, message: 'Contador de uso reiniciado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async getDiscountRulesByProduct(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const discountRules = await discountRulesService.findByProduct(req.params.productId, req.tenantId!);
      res.json({ success: true, data: discountRules });
    } catch (error) {
      next(error);
    }
  }

  async getDiscountRulesByCustomer(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const discountRules = await discountRulesService.findByCustomer(req.params.customerId, req.tenantId!);
      res.json({ success: true, data: discountRules });
    } catch (error) {
      next(error);
    }
  }
}

export const coreController = new CoreController();
