/**
 * TaxCategoryService - Gestion de Categorias de Impuestos
 *
 * Administra las categorias de impuestos del SAT (IVA, ISR, IEPS, etc).
 *
 * @module Fiscal
 */

import { Repository } from 'typeorm';
import { TaxCategory, TaxNature } from '../entities/tax-category.entity';

interface ServiceContext {
  tenantId: string;
  userId?: string;
}

interface PaginatedResult<T> {
  data: T[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
}

export interface CreateTaxCategoryDto {
  code: string;
  name: string;
  description?: string;
  taxNature?: TaxNature;
  satCode?: string;
  isActive?: boolean;
}

export interface UpdateTaxCategoryDto extends Partial<CreateTaxCategoryDto> {}

export interface TaxCategoryFilters {
  taxNature?: TaxNature;
  satCode?: string;
  isActive?: boolean;
  search?: string;
}

export class TaxCategoryService {
  private repository: Repository<TaxCategory>;

  constructor(repository: Repository<TaxCategory>) {
    this.repository = repository;
  }

  /**
   * Crear una nueva categoria de impuestos
   */
  async create(_ctx: ServiceContext, data: CreateTaxCategoryDto): Promise<TaxCategory> {
    const existing = await this.findByCode(data.code);
    if (existing) {
      throw new Error(`Tax category with code ${data.code} already exists`);
    }

    const entity = this.repository.create({
      ...data,
      isActive: data.isActive ?? true,
    });
    return this.repository.save(entity);
  }

  /**
   * Buscar por ID
   */
  async findById(_ctx: ServiceContext, id: string): Promise<TaxCategory | null> {
    return this.repository.findOne({
      where: { id },
    });
  }

  /**
   * Buscar por codigo
   */
  async findByCode(code: string): Promise<TaxCategory | null> {
    return this.repository.findOne({
      where: { code },
    });
  }

  /**
   * Buscar por codigo SAT
   */
  async findBySatCode(_ctx: ServiceContext, satCode: string): Promise<TaxCategory | null> {
    return this.repository.findOne({
      where: { satCode, isActive: true },
    });
  }

  /**
   * Actualizar categoria de impuestos
   */
  async update(_ctx: ServiceContext, id: string, data: UpdateTaxCategoryDto): Promise<TaxCategory | null> {
    const entity = await this.findById(_ctx, id);
    if (!entity) return null;

    if (data.code && data.code !== entity.code) {
      const existing = await this.findByCode(data.code);
      if (existing) {
        throw new Error(`Tax category with code ${data.code} already exists`);
      }
    }

    Object.assign(entity, data);
    return this.repository.save(entity);
  }

  /**
   * Desactivar categoria de impuestos
   */
  async deactivate(_ctx: ServiceContext, id: string): Promise<boolean> {
    const entity = await this.findById(_ctx, id);
    if (!entity) return false;
    entity.isActive = false;
    await this.repository.save(entity);
    return true;
  }

  /**
   * Buscar categorias con filtros
   */
  async findWithFilters(
    _ctx: ServiceContext,
    filters: TaxCategoryFilters,
    page = 1,
    limit = 20
  ): Promise<PaginatedResult<TaxCategory>> {
    const qb = this.repository.createQueryBuilder('tc');

    if (filters.taxNature) {
      qb.andWhere('(tc.tax_nature = :taxNature OR tc.tax_nature = :both)', {
        taxNature: filters.taxNature,
        both: TaxNature.BOTH,
      });
    }

    if (filters.satCode) {
      qb.andWhere('tc.sat_code = :satCode', { satCode: filters.satCode });
    }

    if (filters.isActive !== undefined) {
      qb.andWhere('tc.is_active = :isActive', { isActive: filters.isActive });
    }

    if (filters.search) {
      qb.andWhere('(tc.name ILIKE :search OR tc.code ILIKE :search OR tc.description ILIKE :search)', {
        search: `%${filters.search}%`,
      });
    }

    const skip = (page - 1) * limit;
    qb.orderBy('tc.code', 'ASC').skip(skip).take(limit);

    const [data, total] = await qb.getManyAndCount();

    return {
      data,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  /**
   * Obtener todas las categorias activas
   */
  async findAllActive(_ctx: ServiceContext): Promise<TaxCategory[]> {
    return this.repository.find({
      where: { isActive: true },
      order: { code: 'ASC' },
    });
  }

  /**
   * Obtener categorias por naturaleza
   */
  async findByNature(_ctx: ServiceContext, nature: TaxNature): Promise<TaxCategory[]> {
    const qb = this.repository
      .createQueryBuilder('tc')
      .where('tc.is_active = true')
      .andWhere('(tc.tax_nature = :nature OR tc.tax_nature = :both)', {
        nature,
        both: TaxNature.BOTH,
      })
      .orderBy('tc.code', 'ASC');

    return qb.getMany();
  }

  /**
   * Obtener categorias de impuestos trasladados
   */
  async findTaxes(_ctx: ServiceContext): Promise<TaxCategory[]> {
    return this.findByNature(_ctx, TaxNature.TAX);
  }

  /**
   * Obtener categorias de retenciones
   */
  async findWithholdings(_ctx: ServiceContext): Promise<TaxCategory[]> {
    return this.findByNature(_ctx, TaxNature.WITHHOLDING);
  }

  /**
   * Obtener estadisticas
   */
  async getStats(_ctx: ServiceContext): Promise<TaxCategoryStats> {
    const all = await this.repository.find();

    let taxCount = 0;
    let withholdingCount = 0;
    let bothCount = 0;
    let activeCount = 0;
    let withSatCodeCount = 0;

    for (const category of all) {
      if (category.isActive) activeCount++;
      if (category.satCode) withSatCodeCount++;
      switch (category.taxNature) {
        case TaxNature.TAX:
          taxCount++;
          break;
        case TaxNature.WITHHOLDING:
          withholdingCount++;
          break;
        case TaxNature.BOTH:
          bothCount++;
          break;
      }
    }

    return {
      total: all.length,
      active: activeCount,
      inactive: all.length - activeCount,
      withSatCode: withSatCodeCount,
      byNature: {
        tax: taxCount,
        withholding: withholdingCount,
        both: bothCount,
      },
    };
  }
}

export interface TaxCategoryStats {
  total: number;
  active: number;
  inactive: number;
  withSatCode: number;
  byNature: {
    tax: number;
    withholding: number;
    both: number;
  };
}
