import { Response, NextFunction } from 'express';
import { z } from 'zod';
import { accountsService, CreateAccountDto, UpdateAccountDto, AccountFilters } from './accounts.service.js';
import { journalsService, CreateJournalDto, UpdateJournalDto, JournalFilters } from './journals.service.js';
import { journalEntriesService, CreateJournalEntryDto, UpdateJournalEntryDto, JournalEntryFilters } from './journal-entries.service.js';
import { invoicesService, CreateInvoiceDto, UpdateInvoiceDto, CreateInvoiceLineDto, UpdateInvoiceLineDto, InvoiceFilters } from './invoices.service.js';
import { paymentsService, CreatePaymentDto, UpdatePaymentDto, ReconcileDto, PaymentFilters } from './payments.service.js';
import { taxesService, CreateTaxDto, UpdateTaxDto, TaxFilters } from './taxes.service.js';
import { incotermsService, IncotermFilters } from './incoterms.service.js';
import { paymentMethodsService, PaymentMethodFilters } from './payment-methods.service.js';
import { paymentTermsService, CreatePaymentTermDto, UpdatePaymentTermDto, PaymentTermLineDto } from './payment-terms.service.js';
import { reconcileModelsService, CreateReconcileModelDto, UpdateReconcileModelDto, ReconcileModelLineDto, ReconcileModelFilters } from './reconcile-models.service.js';
import { AuthenticatedRequest } from '../../shared/middleware/auth.middleware.js';
import { ValidationError } from '../../shared/errors/index.js';

// ===== Case Conversion Helpers =====
function snakeToCamel(str: string): string {
  return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
}

function toCamelCase<T>(obj: Record<string, unknown>): T {
  const result: Record<string, unknown> = {};
  for (const key of Object.keys(obj)) {
    const camelKey = snakeToCamel(key);
    result[camelKey] = obj[key];
  }
  return result as T;
}

function toCamelCaseDeep<R>(obj: unknown): R {
  if (Array.isArray(obj)) {
    return obj.map((item) => toCamelCaseDeep(item)) as R;
  }
  if (obj !== null && typeof obj === 'object') {
    const result: Record<string, unknown> = {};
    for (const key of Object.keys(obj as Record<string, unknown>)) {
      const camelKey = snakeToCamel(key);
      result[camelKey] = toCamelCaseDeep((obj as Record<string, unknown>)[key]);
    }
    return result as R;
  }
  return obj as R;
}

// Schemas
const createAccountSchema = z.object({
  company_id: z.string().uuid(),
  code: z.string().min(1).max(50),
  name: z.string().min(1).max(255),
  account_type_id: z.string().uuid(),
  parent_id: z.string().uuid().optional(),
  currency_id: z.string().uuid().optional(),
  is_reconcilable: z.boolean().default(false),
  notes: z.string().optional(),
});

const updateAccountSchema = z.object({
  name: z.string().min(1).max(255).optional(),
  parent_id: z.string().uuid().optional().nullable(),
  currency_id: z.string().uuid().optional().nullable(),
  is_reconcilable: z.boolean().optional(),
  is_deprecated: z.boolean().optional(),
  notes: z.string().optional().nullable(),
});

const accountQuerySchema = z.object({
  company_id: z.string().uuid().optional(),
  account_type_id: z.string().uuid().optional(),
  parent_id: z.string().optional(),
  is_deprecated: 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),
});

const createJournalSchema = z.object({
  company_id: z.string().uuid(),
  name: z.string().min(1).max(255),
  code: z.string().min(1).max(20),
  journal_type: z.enum(['sale', 'purchase', 'cash', 'bank', 'general']),
  default_account_id: z.string().uuid().optional(),
  sequence_id: z.string().uuid().optional(),
  currency_id: z.string().uuid().optional(),
});

const updateJournalSchema = z.object({
  name: z.string().min(1).max(255).optional(),
  default_account_id: z.string().uuid().optional().nullable(),
  sequence_id: z.string().uuid().optional().nullable(),
  currency_id: z.string().uuid().optional().nullable(),
  active: z.boolean().optional(),
});

const journalQuerySchema = z.object({
  company_id: z.string().uuid().optional(),
  journal_type: z.enum(['sale', 'purchase', 'cash', 'bank', 'general']).optional(),
  active: z.coerce.boolean().optional(),
  page: z.coerce.number().int().positive().default(1),
  limit: z.coerce.number().int().positive().max(100).default(50),
});

const journalEntryLineSchema = z.object({
  account_id: z.string().uuid(),
  partner_id: z.string().uuid().optional(),
  debit: z.number().min(0).default(0),
  credit: z.number().min(0).default(0),
  description: z.string().optional(),
  ref: z.string().optional(),
});

const createJournalEntrySchema = z.object({
  company_id: z.string().uuid(),
  journal_id: z.string().uuid(),
  name: z.string().min(1).max(100),
  ref: z.string().max(255).optional(),
  date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
  notes: z.string().optional(),
  lines: z.array(journalEntryLineSchema).min(2),
});

const updateJournalEntrySchema = z.object({
  ref: z.string().max(255).optional().nullable(),
  date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
  notes: z.string().optional().nullable(),
  lines: z.array(journalEntryLineSchema).min(2).optional(),
});

const journalEntryQuerySchema = z.object({
  company_id: z.string().uuid().optional(),
  journal_id: z.string().uuid().optional(),
  status: z.enum(['draft', 'posted', '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),
});

// ========== INVOICE SCHEMAS ==========
const createInvoiceSchema = z.object({
  company_id: z.string().uuid(),
  partner_id: z.string().uuid(),
  invoice_type: z.enum(['customer', 'supplier']),
  currency_id: z.string().uuid(),
  invoice_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
  due_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
  payment_term_id: z.string().uuid().optional(),
  journal_id: z.string().uuid().optional(),
  ref: z.string().optional(),
  notes: z.string().optional(),
});

const updateInvoiceSchema = z.object({
  partner_id: z.string().uuid().optional(),
  currency_id: z.string().uuid().optional(),
  invoice_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
  due_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().nullable(),
  payment_term_id: z.string().uuid().optional().nullable(),
  journal_id: z.string().uuid().optional().nullable(),
  ref: z.string().optional().nullable(),
  notes: z.string().optional().nullable(),
});

const invoiceQuerySchema = z.object({
  company_id: z.string().uuid().optional(),
  partner_id: z.string().uuid().optional(),
  invoice_type: z.enum(['customer', 'supplier']).optional(),
  status: z.enum(['draft', 'open', 'paid', '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),
});

const createInvoiceLineSchema = z.object({
  product_id: z.string().uuid().optional(),
  description: z.string().min(1),
  quantity: z.number().positive(),
  uom_id: z.string().uuid().optional(),
  price_unit: z.number().min(0),
  tax_ids: z.array(z.string().uuid()).optional(),
  account_id: z.string().uuid().optional(),
});

const updateInvoiceLineSchema = 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().nullable(),
  price_unit: z.number().min(0).optional(),
  tax_ids: z.array(z.string().uuid()).optional(),
  account_id: z.string().uuid().optional().nullable(),
});

// ========== PAYMENT SCHEMAS ==========
const createPaymentSchema = z.object({
  company_id: z.string().uuid(),
  partner_id: z.string().uuid(),
  payment_type: z.enum(['inbound', 'outbound']),
  payment_method: z.enum(['cash', 'bank_transfer', 'check', 'card', 'other']),
  amount: z.number().positive(),
  currency_id: z.string().uuid(),
  payment_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
  ref: z.string().optional(),
  journal_id: z.string().uuid(),
  notes: z.string().optional(),
});

const updatePaymentSchema = z.object({
  partner_id: z.string().uuid().optional(),
  payment_method: z.enum(['cash', 'bank_transfer', 'check', 'card', 'other']).optional(),
  amount: z.number().positive().optional(),
  currency_id: z.string().uuid().optional(),
  payment_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
  ref: z.string().optional().nullable(),
  journal_id: z.string().uuid().optional(),
  notes: z.string().optional().nullable(),
});

const reconcilePaymentSchema = z.object({
  invoices: z.array(z.object({
    invoice_id: z.string().uuid(),
    amount: z.number().positive(),
  })).min(1),
});

const paymentQuerySchema = z.object({
  company_id: z.string().uuid().optional(),
  partner_id: z.string().uuid().optional(),
  payment_type: z.enum(['inbound', 'outbound']).optional(),
  payment_method: z.enum(['cash', 'bank_transfer', 'check', 'card', 'other']).optional(),
  status: z.enum(['draft', 'posted', 'reconciled', '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),
});

// ========== TAX SCHEMAS ==========
const createTaxSchema = z.object({
  company_id: z.string().uuid(),
  name: z.string().min(1).max(100),
  code: z.string().min(1).max(20),
  tax_type: z.enum(['sales', 'purchase', 'all']),
  amount: z.number().min(0).max(100),
  included_in_price: z.boolean().default(false),
});

const updateTaxSchema = z.object({
  name: z.string().min(1).max(100).optional(),
  code: z.string().min(1).max(20).optional(),
  tax_type: z.enum(['sales', 'purchase', 'all']).optional(),
  amount: z.number().min(0).max(100).optional(),
  included_in_price: z.boolean().optional(),
  active: z.boolean().optional(),
});

const taxQuerySchema = z.object({
  company_id: z.string().uuid().optional(),
  tax_type: z.enum(['sales', 'purchase', 'all']).optional(),
  active: 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(20),
});

// ========== INCOTERM SCHEMAS (read-only) ==========
const incotermQuerySchema = z.object({
  is_active: 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),
});

// ========== PAYMENT METHOD SCHEMAS (read-only) ==========
const paymentMethodQuerySchema = z.object({
  payment_type: z.enum(['inbound', 'outbound']).optional(),
  is_active: 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),
});

// ========== PAYMENT TERMS SCHEMAS ==========
const createPaymentTermSchema = z.object({
  company_id: z.string().uuid(),
  name: z.string().min(1).max(100),
  code: z.string().min(1).max(20),
  terms: z.array(z.object({
    days: z.number().int().min(0),
    percent: z.number().min(0).max(100),
  })).optional(),
  active: z.boolean().default(true),
  lines: z.array(z.object({
    value: z.enum(['balance', 'percent', 'fixed']).optional(),
    value_amount: z.number().optional(),
    nb_days: z.number().int().min(0).optional(),
    delay_type: z.string().optional(),
    day_of_the_month: z.number().int().min(1).max(31).nullable().optional(),
    sequence: z.number().int().optional(),
  })).optional(),
});

const updatePaymentTermSchema = z.object({
  name: z.string().min(1).max(100).optional(),
  code: z.string().min(1).max(20).optional(),
  terms: z.array(z.object({
    days: z.number().int().min(0),
    percent: z.number().min(0).max(100),
  })).optional(),
  active: z.boolean().optional(),
});

const paymentTermQuerySchema = z.object({
  company_id: z.string().uuid().optional(),
  active: 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),
});

const paymentTermLineSchema = z.object({
  value: z.enum(['balance', 'percent', 'fixed']).optional(),
  value_amount: z.number().optional(),
  nb_days: z.number().int().min(0).optional(),
  delay_type: z.string().optional(),
  day_of_the_month: z.number().int().min(1).max(31).nullable().optional(),
  sequence: z.number().int().optional(),
});

// ========== RECONCILE MODEL SCHEMAS ==========
const createReconcileModelSchema = z.object({
  name: z.string().min(1).max(255),
  company_id: z.string().uuid().optional().nullable(),
  sequence: z.number().int().optional(),
  rule_type: z.enum(['writeoff_button', 'writeoff_suggestion', 'invoice_matching']).optional(),
  auto_reconcile: z.boolean().optional(),
  match_nature: z.enum(['amount_received', 'amount_paid', 'both']).optional(),
  match_amount: z.enum(['lower', 'greater', 'between', 'any']).optional(),
  match_amount_min: z.number().optional().nullable(),
  match_amount_max: z.number().optional().nullable(),
  match_label: z.string().optional().nullable(),
  match_label_param: z.string().optional().nullable(),
  match_partner: z.boolean().optional(),
  match_partner_ids: z.array(z.string().uuid()).optional().nullable(),
  is_active: z.boolean().default(true),
  lines: z.array(z.object({
    account_id: z.string().uuid(),
    journal_id: z.string().uuid().optional().nullable(),
    label: z.string().optional().nullable(),
    amount_type: z.enum(['percentage', 'fixed', 'regex']).optional(),
    amount_value: z.number().optional(),
    tax_ids: z.array(z.string().uuid()).optional().nullable(),
    analytic_account_id: z.string().uuid().optional().nullable(),
    sequence: z.number().int().optional(),
  })).optional(),
});

const updateReconcileModelSchema = z.object({
  name: z.string().min(1).max(255).optional(),
  sequence: z.number().int().optional(),
  rule_type: z.enum(['writeoff_button', 'writeoff_suggestion', 'invoice_matching']).optional(),
  auto_reconcile: z.boolean().optional(),
  match_nature: z.enum(['amount_received', 'amount_paid', 'both']).optional(),
  match_amount: z.enum(['lower', 'greater', 'between', 'any']).optional(),
  match_amount_min: z.number().optional().nullable(),
  match_amount_max: z.number().optional().nullable(),
  match_label: z.string().optional().nullable(),
  match_label_param: z.string().optional().nullable(),
  match_partner: z.boolean().optional(),
  match_partner_ids: z.array(z.string().uuid()).optional().nullable(),
  is_active: z.boolean().optional(),
});

const reconcileModelQuerySchema = z.object({
  company_id: z.string().uuid().optional(),
  rule_type: z.enum(['writeoff_button', 'writeoff_suggestion', 'invoice_matching']).optional(),
  is_active: 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),
});

const reconcileModelLineSchema = z.object({
  account_id: z.string().uuid(),
  journal_id: z.string().uuid().optional().nullable(),
  label: z.string().optional().nullable(),
  amount_type: z.enum(['percentage', 'fixed', 'regex']).optional(),
  amount_value: z.number().optional(),
  tax_ids: z.array(z.string().uuid()).optional().nullable(),
  analytic_account_id: z.string().uuid().optional().nullable(),
  sequence: z.number().int().optional(),
});

class FinancialController {
  // ========== ACCOUNT TYPES ==========
  async getAccountTypes(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const accountTypes = await accountsService.findAllAccountTypes();
      res.json({ success: true, data: accountTypes });
    } catch (error) {
      next(error);
    }
  }

  // ========== ACCOUNTS ==========
  async getAccounts(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = accountQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }
      const filters = toCamelCase<AccountFilters>(queryResult.data as Record<string, unknown>);
      const result = await accountsService.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 getAccount(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const account = await accountsService.findById(req.params.id, req.tenantId!);
      res.json({ success: true, data: account });
    } catch (error) {
      next(error);
    }
  }

  async createAccount(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createAccountSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de cuenta inválidos', parseResult.error.errors);
      }
      const dto = toCamelCase<CreateAccountDto>(parseResult.data as Record<string, unknown>);
      const account = await accountsService.create(dto, req.tenantId!, req.user!.userId);
      res.status(201).json({ success: true, data: account, message: 'Cuenta creada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async updateAccount(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateAccountSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de cuenta inválidos', parseResult.error.errors);
      }
      const dto = toCamelCase<UpdateAccountDto>(parseResult.data as Record<string, unknown>);
      const account = await accountsService.update(req.params.id, dto, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: account, message: 'Cuenta actualizada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

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

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

  // ========== JOURNALS ==========
  async getJournals(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = journalQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }
      const filters = toCamelCase<JournalFilters>(queryResult.data as Record<string, unknown>);
      const result = await journalsService.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 getJournal(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const journal = await journalsService.findById(req.params.id, req.tenantId!);
      res.json({ success: true, data: journal });
    } catch (error) {
      next(error);
    }
  }

  async createJournal(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createJournalSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de diario inválidos', parseResult.error.errors);
      }
      const dto = toCamelCase<CreateJournalDto>(parseResult.data as Record<string, unknown>);
      const journal = await journalsService.create(dto, req.tenantId!, req.user!.userId);
      res.status(201).json({ success: true, data: journal, message: 'Diario creado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async updateJournal(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateJournalSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de diario inválidos', parseResult.error.errors);
      }
      const dto = toCamelCase<UpdateJournalDto>(parseResult.data as Record<string, unknown>);
      const journal = await journalsService.update(req.params.id, dto, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: journal, message: 'Diario actualizado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

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

  // ========== JOURNAL ENTRIES ==========
  async getJournalEntries(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = journalEntryQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }
      const filters = toCamelCase<JournalEntryFilters>(queryResult.data as Record<string, unknown>);
      const result = await journalEntriesService.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 getJournalEntry(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const entry = await journalEntriesService.findById(req.params.id, req.tenantId!);
      res.json({ success: true, data: entry });
    } catch (error) {
      next(error);
    }
  }

  async createJournalEntry(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createJournalEntrySchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de póliza inválidos', parseResult.error.errors);
      }
      const dto = toCamelCaseDeep<CreateJournalEntryDto>(parseResult.data);
      const entry = await journalEntriesService.create(dto, req.tenantId!, req.user!.userId);
      res.status(201).json({ success: true, data: entry, message: 'Póliza creada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async updateJournalEntry(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateJournalEntrySchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de póliza inválidos', parseResult.error.errors);
      }
      const dto = toCamelCaseDeep<UpdateJournalEntryDto>(parseResult.data);
      const entry = await journalEntriesService.update(req.params.id, dto, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: entry, message: 'Póliza actualizada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async postJournalEntry(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const entry = await journalEntriesService.post(req.params.id, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: entry, message: 'Póliza publicada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

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

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

  // ========== INVOICES ==========
  async getInvoices(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = invoiceQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }
      const filters = toCamelCase<InvoiceFilters>(queryResult.data as Record<string, unknown>);
      const result = await invoicesService.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 getInvoice(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const invoice = await invoicesService.findById(req.params.id, req.tenantId!);
      res.json({ success: true, data: invoice });
    } catch (error) {
      next(error);
    }
  }

  async createInvoice(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createInvoiceSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de factura inválidos', parseResult.error.errors);
      }
      const dto = toCamelCase<CreateInvoiceDto>(parseResult.data as Record<string, unknown>);
      const invoice = await invoicesService.create(dto, req.tenantId!, req.user!.userId);
      res.status(201).json({ success: true, data: invoice, message: 'Factura creada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async updateInvoice(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateInvoiceSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de factura inválidos', parseResult.error.errors);
      }
      const dto = toCamelCase<UpdateInvoiceDto>(parseResult.data as Record<string, unknown>);
      const invoice = await invoicesService.update(req.params.id, dto, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: invoice, message: 'Factura actualizada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async validateInvoice(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const invoice = await invoicesService.validate(req.params.id, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: invoice, message: 'Factura validada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

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

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

  // ========== INVOICE LINES ==========
  async addInvoiceLine(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createInvoiceLineSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de línea inválidos', parseResult.error.errors);
      }
      const dto = toCamelCase<CreateInvoiceLineDto>(parseResult.data as Record<string, unknown>);
      const line = await invoicesService.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 updateInvoiceLine(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateInvoiceLineSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de línea inválidos', parseResult.error.errors);
      }
      const dto = toCamelCase<UpdateInvoiceLineDto>(parseResult.data as Record<string, unknown>);
      const line = await invoicesService.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 removeInvoiceLine(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      await invoicesService.removeLine(req.params.id, req.params.lineId, req.tenantId!);
      res.json({ success: true, message: 'Línea eliminada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

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

  async createPayment(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createPaymentSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de pago inválidos', parseResult.error.errors);
      }
      const dto: CreatePaymentDto = toCamelCase<CreatePaymentDto>(parseResult.data);
      const payment = await paymentsService.create(dto, req.tenantId!, req.user!.userId);
      res.status(201).json({ success: true, data: payment, message: 'Pago creado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async updatePayment(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updatePaymentSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de pago inválidos', parseResult.error.errors);
      }
      const dto: UpdatePaymentDto = parseResult.data;
      const payment = await paymentsService.update(req.params.id, dto, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: payment, message: 'Pago actualizado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async postPayment(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const payment = await paymentsService.post(req.params.id, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: payment, message: 'Pago publicado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async reconcilePayment(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = reconcilePaymentSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de conciliación inválidos', parseResult.error.errors);
      }
      const dto: ReconcileDto = toCamelCaseDeep<ReconcileDto>(parseResult.data);
      const payment = await paymentsService.reconcile(req.params.id, dto, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: payment, message: 'Pago conciliado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async cancelPayment(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const payment = await paymentsService.cancel(req.params.id, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: payment, message: 'Pago cancelado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

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

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

  async createTax(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createTaxSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de impuesto inválidos', parseResult.error.errors);
      }
      const dto: CreateTaxDto = toCamelCase<CreateTaxDto>(parseResult.data);
      const tax = await taxesService.create(dto, req.tenantId!, req.user!.userId);
      res.status(201).json({ success: true, data: tax, message: 'Impuesto creado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async updateTax(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateTaxSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de impuesto inválidos', parseResult.error.errors);
      }
      const dto: UpdateTaxDto = parseResult.data;
      const tax = await taxesService.update(req.params.id, dto, req.tenantId!, req.user!.userId);
      res.json({ success: true, data: tax, message: 'Impuesto actualizado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

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

  // ========== INCOTERMS (read-only) ==========
  async getIncoterms(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = incotermQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }
      const filters: IncotermFilters = toCamelCase<IncotermFilters>(queryResult.data as Record<string, unknown>);
      const result = await incotermsService.findAll(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 getIncoterm(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const incoterm = await incotermsService.findById(req.params.id);
      res.json({ success: true, data: incoterm });
    } catch (error) {
      next(error);
    }
  }

  // ========== PAYMENT METHODS (read-only) ==========
  async getPaymentMethods(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = paymentMethodQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }
      const filters: PaymentMethodFilters = toCamelCase<PaymentMethodFilters>(queryResult.data as Record<string, unknown>);
      const result = await paymentMethodsService.findAll(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 getPaymentMethod(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const paymentMethod = await paymentMethodsService.findById(req.params.id);
      res.json({ success: true, data: paymentMethod });
    } catch (error) {
      next(error);
    }
  }

  // ========== PAYMENT TERMS ==========
  async getPaymentTerms(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = paymentTermQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }
      const { company_id, active, search, page, limit } = queryResult.data;
      const result = await paymentTermsService.findAll(req.tenantId!, {
        companyId: company_id,
        active,
        search,
        page,
        limit,
      });
      res.json({
        success: true,
        data: result.data,
        meta: { total: result.total, page: result.page, limit: result.limit, totalPages: Math.ceil(result.total / result.limit) },
      });
    } 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!);
      if (!paymentTerm) {
        res.status(404).json({ success: false, error: 'Término de pago no encontrado' });
        return;
      }
      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 = {
        companyId: parseResult.data.company_id,
        name: parseResult.data.name,
        code: parseResult.data.code,
        terms: parseResult.data.terms,
        active: parseResult.data.active,
        lines: parseResult.data.lines?.map(l => ({
          value: l.value,
          valueAmount: l.value_amount,
          nbDays: l.nb_days,
          delayType: l.delay_type,
          dayOfTheMonth: l.day_of_the_month,
          sequence: l.sequence,
        })),
      };
      const paymentTerm = await paymentTermsService.create(req.tenantId!, dto, req.user!.userId);
      res.status(201).json({ success: true, data: paymentTerm });
    } 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 = {
        name: parseResult.data.name,
        code: parseResult.data.code,
        terms: parseResult.data.terms,
        active: parseResult.data.active,
      };
      const paymentTerm = await paymentTermsService.update(req.params.id, req.tenantId!, dto, req.user!.userId);
      if (!paymentTerm) {
        res.status(404).json({ success: false, error: 'Término de pago no encontrado' });
        return;
      }
      res.json({ success: true, data: paymentTerm });
    } catch (error) {
      next(error);
    }
  }

  async deletePaymentTerm(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const deleted = await paymentTermsService.delete(req.params.id, req.tenantId!);
      if (!deleted) {
        res.status(404).json({ success: false, error: 'Término de pago no encontrado' });
        return;
      }
      res.json({ success: true, message: 'Término de pago eliminado' });
    } catch (error) {
      next(error);
    }
  }

  // Payment Term Lines
  async getPaymentTermLines(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const lines = await paymentTermsService.getLines(req.params.id, req.tenantId!);
      res.json({ success: true, data: lines });
    } catch (error) {
      next(error);
    }
  }

  async addPaymentTermLine(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = paymentTermLineSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de línea inválidos', parseResult.error.errors);
      }
      const dto: PaymentTermLineDto = {
        value: parseResult.data.value,
        valueAmount: parseResult.data.value_amount,
        nbDays: parseResult.data.nb_days,
        delayType: parseResult.data.delay_type,
        dayOfTheMonth: parseResult.data.day_of_the_month,
        sequence: parseResult.data.sequence,
      };
      const line = await paymentTermsService.addLine(req.params.id, req.tenantId!, dto);
      if (!line) {
        res.status(404).json({ success: false, error: 'Término de pago no encontrado' });
        return;
      }
      res.status(201).json({ success: true, data: line });
    } catch (error) {
      next(error);
    }
  }

  async updatePaymentTermLine(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = paymentTermLineSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de línea inválidos', parseResult.error.errors);
      }
      const dto: PaymentTermLineDto = {
        value: parseResult.data.value,
        valueAmount: parseResult.data.value_amount,
        nbDays: parseResult.data.nb_days,
        delayType: parseResult.data.delay_type,
        dayOfTheMonth: parseResult.data.day_of_the_month,
        sequence: parseResult.data.sequence,
      };
      const line = await paymentTermsService.updateLine(req.params.id, req.params.lineId, req.tenantId!, dto);
      if (!line) {
        res.status(404).json({ success: false, error: 'Línea no encontrada' });
        return;
      }
      res.json({ success: true, data: line });
    } catch (error) {
      next(error);
    }
  }

  async removePaymentTermLine(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const removed = await paymentTermsService.removeLine(req.params.id, req.params.lineId, req.tenantId!);
      if (!removed) {
        res.status(404).json({ success: false, error: 'Línea no encontrada' });
        return;
      }
      res.json({ success: true, message: 'Línea eliminada' });
    } catch (error) {
      next(error);
    }
  }

  // ========== RECONCILE MODELS ==========
  async getReconcileModels(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const queryResult = reconcileModelQuerySchema.safeParse(req.query);
      if (!queryResult.success) {
        throw new ValidationError('Parámetros de consulta inválidos', queryResult.error.errors);
      }
      const filters: ReconcileModelFilters = toCamelCase<ReconcileModelFilters>(queryResult.data as Record<string, unknown>);
      const result = await reconcileModelsService.findAll(req.tenantId!, filters);
      res.json({
        success: true,
        data: result.data,
        meta: { total: result.total, page: result.page, limit: result.limit, totalPages: Math.ceil(result.total / result.limit) },
      });
    } catch (error) {
      next(error);
    }
  }

  async getReconcileModel(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const model = await reconcileModelsService.findById(req.params.id, req.tenantId!);
      if (!model) {
        res.status(404).json({ success: false, error: 'Modelo de conciliación no encontrado' });
        return;
      }
      res.json({ success: true, data: model });
    } catch (error) {
      next(error);
    }
  }

  async createReconcileModel(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = createReconcileModelSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de modelo de conciliación inválidos', parseResult.error.errors);
      }
      const dto: CreateReconcileModelDto = {
        name: parseResult.data.name,
        companyId: parseResult.data.company_id,
        sequence: parseResult.data.sequence,
        ruleType: parseResult.data.rule_type,
        autoReconcile: parseResult.data.auto_reconcile,
        matchNature: parseResult.data.match_nature,
        matchAmount: parseResult.data.match_amount,
        matchAmountMin: parseResult.data.match_amount_min,
        matchAmountMax: parseResult.data.match_amount_max,
        matchLabel: parseResult.data.match_label,
        matchLabelParam: parseResult.data.match_label_param,
        matchPartner: parseResult.data.match_partner,
        matchPartnerIds: parseResult.data.match_partner_ids,
        isActive: parseResult.data.is_active,
        lines: parseResult.data.lines?.map(l => ({
          accountId: l.account_id,
          journalId: l.journal_id,
          label: l.label,
          amountType: l.amount_type,
          amountValue: l.amount_value,
          taxIds: l.tax_ids,
          analyticAccountId: l.analytic_account_id,
          sequence: l.sequence,
        })),
      };
      const model = await reconcileModelsService.create(req.tenantId!, dto);
      res.status(201).json({ success: true, data: model, message: 'Modelo de conciliación creado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async updateReconcileModel(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = updateReconcileModelSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de modelo de conciliación inválidos', parseResult.error.errors);
      }
      const dto: UpdateReconcileModelDto = {
        name: parseResult.data.name,
        sequence: parseResult.data.sequence,
        ruleType: parseResult.data.rule_type,
        autoReconcile: parseResult.data.auto_reconcile,
        matchNature: parseResult.data.match_nature,
        matchAmount: parseResult.data.match_amount,
        matchAmountMin: parseResult.data.match_amount_min,
        matchAmountMax: parseResult.data.match_amount_max,
        matchLabel: parseResult.data.match_label,
        matchLabelParam: parseResult.data.match_label_param,
        matchPartner: parseResult.data.match_partner,
        matchPartnerIds: parseResult.data.match_partner_ids,
        isActive: parseResult.data.is_active,
      };
      const model = await reconcileModelsService.update(req.params.id, req.tenantId!, dto);
      if (!model) {
        res.status(404).json({ success: false, error: 'Modelo de conciliación no encontrado' });
        return;
      }
      res.json({ success: true, data: model, message: 'Modelo de conciliación actualizado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async deleteReconcileModel(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const deleted = await reconcileModelsService.delete(req.params.id, req.tenantId!);
      if (!deleted) {
        res.status(404).json({ success: false, error: 'Modelo de conciliación no encontrado' });
        return;
      }
      res.json({ success: true, message: 'Modelo de conciliación eliminado exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  // Reconcile Model Lines
  async getReconcileModelLines(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const lines = await reconcileModelsService.getLines(req.params.id, req.tenantId!);
      res.json({ success: true, data: lines });
    } catch (error) {
      next(error);
    }
  }

  async addReconcileModelLine(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = reconcileModelLineSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de línea inválidos', parseResult.error.errors);
      }
      const dto: ReconcileModelLineDto = {
        accountId: parseResult.data.account_id,
        journalId: parseResult.data.journal_id,
        label: parseResult.data.label,
        amountType: parseResult.data.amount_type,
        amountValue: parseResult.data.amount_value,
        taxIds: parseResult.data.tax_ids,
        analyticAccountId: parseResult.data.analytic_account_id,
        sequence: parseResult.data.sequence,
      };
      const line = await reconcileModelsService.addLine(req.params.id, req.tenantId!, dto);
      if (!line) {
        res.status(404).json({ success: false, error: 'Modelo de conciliación no encontrado' });
        return;
      }
      res.status(201).json({ success: true, data: line, message: 'Línea agregada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async updateReconcileModelLine(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const parseResult = reconcileModelLineSchema.safeParse(req.body);
      if (!parseResult.success) {
        throw new ValidationError('Datos de línea inválidos', parseResult.error.errors);
      }
      const dto: ReconcileModelLineDto = {
        accountId: parseResult.data.account_id,
        journalId: parseResult.data.journal_id,
        label: parseResult.data.label,
        amountType: parseResult.data.amount_type,
        amountValue: parseResult.data.amount_value,
        taxIds: parseResult.data.tax_ids,
        analyticAccountId: parseResult.data.analytic_account_id,
        sequence: parseResult.data.sequence,
      };
      const line = await reconcileModelsService.updateLine(req.params.id, req.params.lineId, req.tenantId!, dto);
      if (!line) {
        res.status(404).json({ success: false, error: 'Línea no encontrada' });
        return;
      }
      res.json({ success: true, data: line, message: 'Línea actualizada exitosamente' });
    } catch (error) {
      next(error);
    }
  }

  async removeReconcileModelLine(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise<void> {
    try {
      const removed = await reconcileModelsService.removeLine(req.params.id, req.params.lineId, req.tenantId!);
      if (!removed) {
        res.status(404).json({ success: false, error: 'Línea no encontrada' });
        return;
      }
      res.json({ success: true, message: 'Línea eliminada exitosamente' });
    } catch (error) {
      next(error);
    }
  }
}

export const financialController = new FinancialController();
