/**
 * CfdiUseService - Gestion de Usos de CFDI
 *
 * Administra los usos de CFDI del SAT para facturas electronicas.
 *
 * @module Fiscal
 */

import { Repository } from 'typeorm';
import { CfdiUse } from '../entities/cfdi-use.entity';
import { PersonType } from '../entities/fiscal-regime.entity';

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

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

export interface CreateCfdiUseDto {
  code: string;
  name: string;
  description?: string;
  appliesTo?: PersonType;
  allowedRegimes?: string[];
  isActive?: boolean;
}

export interface UpdateCfdiUseDto extends Partial<CreateCfdiUseDto> {}

export interface CfdiUseFilters {
  appliesTo?: PersonType;
  regimeCode?: string;
  isActive?: boolean;
  search?: string;
}

export class CfdiUseService {
  private repository: Repository<CfdiUse>;

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

  /**
   * Crear un nuevo uso de CFDI
   */
  async create(_ctx: ServiceContext, data: CreateCfdiUseDto): Promise<CfdiUse> {
    const existing = await this.findByCode(data.code);
    if (existing) {
      throw new Error(`CFDI use 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<CfdiUse | null> {
    return this.repository.findOne({
      where: { id },
    });
  }

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

  /**
   * Actualizar uso de CFDI
   */
  async update(_ctx: ServiceContext, id: string, data: UpdateCfdiUseDto): Promise<CfdiUse | 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(`CFDI use with code ${data.code} already exists`);
      }
    }

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

  /**
   * Desactivar uso de CFDI
   */
  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 usos de CFDI con filtros
   */
  async findWithFilters(
    _ctx: ServiceContext,
    filters: CfdiUseFilters,
    page = 1,
    limit = 20
  ): Promise<PaginatedResult<CfdiUse>> {
    const qb = this.repository.createQueryBuilder('cu');

    if (filters.appliesTo) {
      qb.andWhere('(cu.applies_to = :appliesTo OR cu.applies_to = :both)', {
        appliesTo: filters.appliesTo,
        both: PersonType.BOTH,
      });
    }

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

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

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

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

    // Filter by regime code if specified (post-query filter for simple-array)
    let filteredData = data;
    if (filters.regimeCode) {
      filteredData = data.filter(
        (use) => !use.allowedRegimes || use.allowedRegimes.length === 0 || use.allowedRegimes.includes(filters.regimeCode!)
      );
    }

    return {
      data: filteredData,
      total: filters.regimeCode ? filteredData.length : total,
      page,
      limit,
      totalPages: Math.ceil((filters.regimeCode ? filteredData.length : total) / limit),
    };
  }

  /**
   * Obtener todos los usos activos
   */
  async findAllActive(_ctx: ServiceContext): Promise<CfdiUse[]> {
    return this.repository.find({
      where: { isActive: true },
      order: { code: 'ASC' },
    });
  }

  /**
   * Obtener usos por tipo de persona
   */
  async findByPersonType(_ctx: ServiceContext, personType: PersonType): Promise<CfdiUse[]> {
    const qb = this.repository
      .createQueryBuilder('cu')
      .where('cu.is_active = true')
      .andWhere('(cu.applies_to = :personType OR cu.applies_to = :both)', {
        personType,
        both: PersonType.BOTH,
      })
      .orderBy('cu.code', 'ASC');

    return qb.getMany();
  }

  /**
   * Obtener usos compatibles con un regimen fiscal
   */
  async findByRegime(_ctx: ServiceContext, regimeCode: string): Promise<CfdiUse[]> {
    const allActive = await this.repository.find({
      where: { isActive: true },
      order: { code: 'ASC' },
    });

    return allActive.filter(
      (use) => !use.allowedRegimes || use.allowedRegimes.length === 0 || use.allowedRegimes.includes(regimeCode)
    );
  }

  /**
   * Validar compatibilidad uso-regimen
   */
  async validateCompatibility(
    _ctx: ServiceContext,
    useCode: string,
    regimeCode: string
  ): Promise<{ valid: boolean; message?: string }> {
    const use = await this.findByCode(useCode);
    if (!use) {
      return { valid: false, message: `CFDI use ${useCode} not found` };
    }

    if (!use.isActive) {
      return { valid: false, message: `CFDI use ${useCode} is not active` };
    }

    if (use.allowedRegimes && use.allowedRegimes.length > 0 && !use.allowedRegimes.includes(regimeCode)) {
      return {
        valid: false,
        message: `CFDI use ${useCode} is not compatible with regime ${regimeCode}`,
      };
    }

    return { valid: true };
  }

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

    let naturalCount = 0;
    let legalCount = 0;
    let bothCount = 0;
    let activeCount = 0;
    let withRestrictionsCount = 0;

    for (const use of all) {
      if (use.isActive) activeCount++;
      if (use.allowedRegimes && use.allowedRegimes.length > 0) withRestrictionsCount++;
      switch (use.appliesTo) {
        case PersonType.NATURAL:
          naturalCount++;
          break;
        case PersonType.LEGAL:
          legalCount++;
          break;
        case PersonType.BOTH:
          bothCount++;
          break;
      }
    }

    return {
      total: all.length,
      active: activeCount,
      inactive: all.length - activeCount,
      withRegimeRestrictions: withRestrictionsCount,
      byPersonType: {
        natural: naturalCount,
        legal: legalCount,
        both: bothCount,
      },
    };
  }
}

export interface CfdiUseStats {
  total: number;
  active: number;
  inactive: number;
  withRegimeRestrictions: number;
  byPersonType: {
    natural: number;
    legal: number;
    both: number;
  };
}
