/**
 * SubcontractorService - Servicio de gestión de subcontratistas
 *
 * Catálogo de subcontratistas con evaluaciones.
 *
 * @module Contracts
 */

import { Repository, FindOptionsWhere } from 'typeorm';
import { Subcontractor, SubcontractorStatus, SubcontractorSpecialty } from '../entities/subcontractor.entity';

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

export interface CreateSubcontractorDto {
  businessName: string;
  tradeName?: string;
  rfc: string;
  address?: string;
  phone?: string;
  email?: string;
  contactName?: string;
  contactPhone?: string;
  primarySpecialty: SubcontractorSpecialty;
  secondarySpecialties?: string[];
  bankName?: string;
  bankAccount?: string;
  clabe?: string;
  notes?: string;
}

export interface UpdateSubcontractorDto {
  tradeName?: string;
  address?: string;
  phone?: string;
  email?: string;
  contactName?: string;
  contactPhone?: string;
  secondarySpecialties?: string[];
  bankName?: string;
  bankAccount?: string;
  clabe?: string;
  notes?: string;
}

export interface SubcontractorFilters {
  specialty?: SubcontractorSpecialty;
  status?: SubcontractorStatus;
  search?: string;
  minRating?: number;
}

export class SubcontractorService {
  constructor(private readonly subcontractorRepository: Repository<Subcontractor>) {}

  private generateCode(): string {
    const now = new Date();
    const year = now.getFullYear().toString().slice(-2);
    const random = Math.random().toString(36).substring(2, 6).toUpperCase();
    return `SC-${year}-${random}`;
  }

  async findWithFilters(
    ctx: ServiceContext,
    filters: SubcontractorFilters = {},
    page: number = 1,
    limit: number = 20
  ): Promise<{ data: Subcontractor[]; total: number; page: number; limit: number }> {
    const skip = (page - 1) * limit;

    const queryBuilder = this.subcontractorRepository
      .createQueryBuilder('sc')
      .leftJoinAndSelect('sc.createdBy', 'createdBy')
      .where('sc.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('sc.deleted_at IS NULL');

    if (filters.specialty) {
      queryBuilder.andWhere('sc.primary_specialty = :specialty', { specialty: filters.specialty });
    }

    if (filters.status) {
      queryBuilder.andWhere('sc.status = :status', { status: filters.status });
    }

    if (filters.search) {
      queryBuilder.andWhere(
        '(sc.business_name ILIKE :search OR sc.trade_name ILIKE :search OR sc.rfc ILIKE :search)',
        { search: `%${filters.search}%` }
      );
    }

    if (filters.minRating !== undefined) {
      queryBuilder.andWhere('sc.average_rating >= :minRating', { minRating: filters.minRating });
    }

    queryBuilder
      .orderBy('sc.business_name', 'ASC')
      .skip(skip)
      .take(limit);

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

    return { data, total, page, limit };
  }

  async findById(ctx: ServiceContext, id: string): Promise<Subcontractor | null> {
    return this.subcontractorRepository.findOne({
      where: {
        id,
        tenantId: ctx.tenantId,
        deletedAt: null,
      } as unknown as FindOptionsWhere<Subcontractor>,
    });
  }

  async findByRfc(ctx: ServiceContext, rfc: string): Promise<Subcontractor | null> {
    return this.subcontractorRepository.findOne({
      where: {
        rfc: rfc.toUpperCase(),
        tenantId: ctx.tenantId,
        deletedAt: null,
      } as unknown as FindOptionsWhere<Subcontractor>,
    });
  }

  async create(ctx: ServiceContext, dto: CreateSubcontractorDto): Promise<Subcontractor> {
    // Check for existing RFC
    const existing = await this.findByRfc(ctx, dto.rfc);
    if (existing) {
      throw new Error('A subcontractor with this RFC already exists');
    }

    const subcontractor = this.subcontractorRepository.create({
      tenantId: ctx.tenantId,
      createdById: ctx.userId,
      code: this.generateCode(),
      businessName: dto.businessName,
      tradeName: dto.tradeName,
      rfc: dto.rfc.toUpperCase(),
      address: dto.address,
      phone: dto.phone,
      email: dto.email,
      contactName: dto.contactName,
      contactPhone: dto.contactPhone,
      primarySpecialty: dto.primarySpecialty,
      secondarySpecialties: dto.secondarySpecialties,
      bankName: dto.bankName,
      bankAccount: dto.bankAccount,
      clabe: dto.clabe,
      notes: dto.notes,
      status: 'active',
    });

    return this.subcontractorRepository.save(subcontractor);
  }

  async update(ctx: ServiceContext, id: string, dto: UpdateSubcontractorDto): Promise<Subcontractor | null> {
    const subcontractor = await this.findById(ctx, id);
    if (!subcontractor) {
      return null;
    }

    Object.assign(subcontractor, {
      ...dto,
      updatedById: ctx.userId || '',
    });

    return this.subcontractorRepository.save(subcontractor);
  }

  async updateRating(ctx: ServiceContext, id: string, rating: number): Promise<Subcontractor | null> {
    const subcontractor = await this.findById(ctx, id);
    if (!subcontractor) {
      return null;
    }

    // Calculate new average rating
    const totalRatings = subcontractor.completedContracts;
    const currentTotal = subcontractor.averageRating * totalRatings;
    const newTotal = currentTotal + rating;
    subcontractor.averageRating = newTotal / (totalRatings + 1);
    subcontractor.updatedById = ctx.userId || '';

    return this.subcontractorRepository.save(subcontractor);
  }

  async incrementContracts(ctx: ServiceContext, id: string, completed: boolean = false): Promise<Subcontractor | null> {
    const subcontractor = await this.findById(ctx, id);
    if (!subcontractor) {
      return null;
    }

    subcontractor.totalContracts += 1;
    if (completed) {
      subcontractor.completedContracts += 1;
    }
    subcontractor.updatedById = ctx.userId || '';

    return this.subcontractorRepository.save(subcontractor);
  }

  async incrementIncidents(ctx: ServiceContext, id: string): Promise<Subcontractor | null> {
    const subcontractor = await this.findById(ctx, id);
    if (!subcontractor) {
      return null;
    }

    subcontractor.totalIncidents += 1;
    subcontractor.updatedById = ctx.userId || '';

    return this.subcontractorRepository.save(subcontractor);
  }

  async deactivate(ctx: ServiceContext, id: string): Promise<Subcontractor | null> {
    const subcontractor = await this.findById(ctx, id);
    if (!subcontractor) {
      return null;
    }

    subcontractor.status = 'inactive';
    subcontractor.updatedById = ctx.userId || '';

    return this.subcontractorRepository.save(subcontractor);
  }

  async reactivate(ctx: ServiceContext, id: string): Promise<Subcontractor | null> {
    const subcontractor = await this.findById(ctx, id);
    if (!subcontractor) {
      return null;
    }

    if (subcontractor.status === 'blacklisted') {
      throw new Error('Cannot reactivate a blacklisted subcontractor');
    }

    subcontractor.status = 'active';
    subcontractor.updatedById = ctx.userId || '';

    return this.subcontractorRepository.save(subcontractor);
  }

  async blacklist(ctx: ServiceContext, id: string, reason: string): Promise<Subcontractor | null> {
    const subcontractor = await this.findById(ctx, id);
    if (!subcontractor) {
      return null;
    }

    subcontractor.status = 'blacklisted';
    subcontractor.notes = `${subcontractor.notes || ''}\n[BLACKLISTED] ${reason}`;
    subcontractor.updatedById = ctx.userId || '';

    return this.subcontractorRepository.save(subcontractor);
  }

  async softDelete(ctx: ServiceContext, id: string): Promise<boolean> {
    const subcontractor = await this.findById(ctx, id);
    if (!subcontractor) {
      return false;
    }

    await this.subcontractorRepository.update(
      { id, tenantId: ctx.tenantId } as FindOptionsWhere<Subcontractor>,
      { deletedAt: new Date(), deletedById: ctx.userId || '' }
    );

    return true;
  }

  async getBySpecialty(ctx: ServiceContext, specialty: SubcontractorSpecialty): Promise<Subcontractor[]> {
    return this.subcontractorRepository.find({
      where: {
        tenantId: ctx.tenantId,
        primarySpecialty: specialty,
        status: 'active' as SubcontractorStatus,
        deletedAt: null,
      } as unknown as FindOptionsWhere<Subcontractor>,
      order: { averageRating: 'DESC' },
    });
  }

  async getTopRated(ctx: ServiceContext, limit: number = 10): Promise<Subcontractor[]> {
    return this.subcontractorRepository.find({
      where: {
        tenantId: ctx.tenantId,
        status: 'active' as SubcontractorStatus,
        deletedAt: null,
      } as unknown as FindOptionsWhere<Subcontractor>,
      order: { averageRating: 'DESC', completedContracts: 'DESC' },
      take: limit,
    });
  }

  async getStatistics(ctx: ServiceContext): Promise<{
    total: number;
    active: number;
    inactive: number;
    blacklisted: number;
    averageRating: number;
  }> {
    const subcontractors = await this.subcontractorRepository.find({
      where: {
        tenantId: ctx.tenantId,
        deletedAt: null,
      } as unknown as FindOptionsWhere<Subcontractor>,
    });

    const active = subcontractors.filter(s => s.status === 'active');
    const totalRating = active.reduce((sum, s) => sum + Number(s.averageRating), 0);

    return {
      total: subcontractors.length,
      active: active.length,
      inactive: subcontractors.filter(s => s.status === 'inactive').length,
      blacklisted: subcontractors.filter(s => s.status === 'blacklisted').length,
      averageRating: active.length > 0 ? totalRating / active.length : 0,
    };
  }
}
