/**
 * VendorService - Gestion de Proveedores/Contratistas
 *
 * Registro centralizado de proveedores y contratistas que participan en licitaciones.
 * Gestiona informacion de proveedores, calificaciones y historico de participacion.
 *
 * @module Bidding (MAI-018)
 */

import { Repository } from 'typeorm';
import { ServiceContext, PaginatedResult } from '../../../shared/services/base.service';
import { Vendor, VendorCertification, VendorPerformanceEntry } from '../entities/vendor.entity';

// DTOs
export interface CreateVendorDto {
  companyName: string;
  rfc?: string;
  specialties?: string[];
  rating?: number;
  certifications?: VendorCertification[];
  contactName?: string;
  contactEmail?: string;
  contactPhone?: string;
}

export interface UpdateVendorDto extends Partial<CreateVendorDto> {
  isActive?: boolean;
  documentationValid?: boolean;
  performanceHistory?: VendorPerformanceEntry[];
}

export interface VendorFilters {
  search?: string;
  specialty?: string;
  isActive?: boolean;
  minRating?: number;
  page?: number;
  limit?: number;
}

export interface VendorRecord {
  id: string;
  code: string;
  businessName: string;
  rfc?: string;
  contactName?: string;
  contactEmail?: string;
  contactPhone?: string;
  specialties?: string[];
  rating?: number;
  totalParticipations: number;
  totalWins: number;
  isActive: boolean;
  createdAt: Date;
  updatedAt: Date;
}

export interface VendorPerformanceHistory {
  vendor: VendorRecord;
  proposals: {
    tenderId: string;
    tenderTitle: string;
    submittedAt: Date;
    status: string;
    proposedAmount?: string;
    technicalScore?: number;
    economicScore?: number;
    totalScore?: number;
    isWinner: boolean;
  }[];
  statistics: {
    totalParticipations: number;
    wins: number;
    losses: number;
    winRate: number;
    avgTechnicalScore: number;
    avgEconomicScore: number;
    avgTotalScore: number;
    totalProposedValue: number;
    totalWonValue: number;
  };
}

export class VendorService {
  constructor(private readonly repository: Repository<Vendor>) {}

  /**
   * Genera codigo automatico para vendor: VND-NNN
   */
  private async generateCode(ctx: ServiceContext): Promise<string> {
    const prefix = 'VND-';

    const lastVendor = await this.repository
      .createQueryBuilder('v')
      .where('v.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('v.code LIKE :prefix', { prefix: `${prefix}%` })
      .orderBy('v.code', 'DESC')
      .getOne();

    let sequence = 1;
    if (lastVendor) {
      const lastSequence = parseInt(lastVendor.code.replace(prefix, ''), 10);
      if (!isNaN(lastSequence)) {
        sequence = lastSequence + 1;
      }
    }

    return `${prefix}${sequence.toString().padStart(3, '0')}`;
  }

  /**
   * Validar formato RFC mexicano
   */
  private validateRfc(rfc: string): boolean {
    const rfcPattern = /^[A-ZÑ&]{3,4}[0-9]{6}[A-Z0-9]{3}$/i;
    return rfcPattern.test(rfc.replace(/\s/g, '').toUpperCase());
  }

  /**
   * Buscar vendors con filtros y paginacion
   */
  async findAll(
    ctx: ServiceContext,
    filters: VendorFilters = {}
  ): Promise<PaginatedResult<Vendor>> {
    const page = filters.page || 1;
    const limit = filters.limit || 20;

    const qb = this.repository
      .createQueryBuilder('v')
      .where('v.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('v.deleted_at IS NULL');

    if (filters.search) {
      qb.andWhere(
        '(v.business_name ILIKE :search OR v.rfc ILIKE :search OR v.code ILIKE :search)',
        { search: `%${filters.search}%` }
      );
    }
    if (filters.specialty) {
      qb.andWhere(':specialty = ANY(v.specialties)', { specialty: filters.specialty });
    }
    if (filters.isActive !== undefined) {
      qb.andWhere('v.is_active = :isActive', { isActive: filters.isActive });
    }
    if (filters.minRating !== undefined) {
      qb.andWhere('v.rating >= :minRating', { minRating: filters.minRating });
    }

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

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

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

  /**
   * Buscar por ID
   */
  async findById(ctx: ServiceContext, id: string): Promise<Vendor | null> {
    return this.repository.findOne({
      where: { id, tenantId: ctx.tenantId, deletedAt: undefined },
      relations: ['proposals'],
    });
  }

  /**
   * Buscar por codigo de vendor
   */
  async findByCode(ctx: ServiceContext, code: string): Promise<Vendor | null> {
    return this.repository.findOne({
      where: {
        code,
        tenantId: ctx.tenantId,
        deletedAt: undefined,
      },
    });
  }

  /**
   * Buscar por RFC
   */
  async findByRfc(ctx: ServiceContext, rfc: string): Promise<Vendor | null> {
    const normalizedRfc = rfc.replace(/\s/g, '').toUpperCase();

    return this.repository.findOne({
      where: {
        rfc: normalizedRfc,
        tenantId: ctx.tenantId,
        deletedAt: undefined,
      },
    });
  }

  /**
   * Crear vendor
   * @throws Error si el RFC ya existe o tiene formato invalido
   */
  async create(ctx: ServiceContext, dto: CreateVendorDto): Promise<Vendor> {
    // Validar formato RFC si se proporciona
    if (dto.rfc && !this.validateRfc(dto.rfc)) {
      throw new Error('Invalid RFC format. Must be 12 or 13 alphanumeric characters.');
    }

    const normalizedRfc = dto.rfc?.replace(/\s/g, '').toUpperCase();

    // Verificar unicidad de RFC
    if (normalizedRfc) {
      const existing = await this.findByRfc(ctx, normalizedRfc);
      if (existing) {
        throw new Error(`Vendor with RFC ${normalizedRfc} already exists`);
      }
    }

    const vendorCode = await this.generateCode(ctx);

    const vendor = this.repository.create({
      tenantId: ctx.tenantId,
      code: vendorCode,
      businessName: dto.companyName,
      rfc: normalizedRfc,
      specialties: dto.specialties,
      rating: dto.rating,
      certifications: dto.certifications,
      contactName: dto.contactName,
      contactEmail: dto.contactEmail,
      contactPhone: dto.contactPhone,
      isActive: true,
      documentationValid: false,
      createdById: ctx.userId,
      updatedById: ctx.userId,
    });

    return this.repository.save(vendor);
  }

  /**
   * Actualizar vendor
   */
  async update(ctx: ServiceContext, id: string, dto: UpdateVendorDto): Promise<Vendor | null> {
    const vendor = await this.findById(ctx, id);
    if (!vendor) return null;

    if (dto.companyName !== undefined) vendor.businessName = dto.companyName;
    if (dto.rfc !== undefined) vendor.rfc = dto.rfc?.replace(/\s/g, '').toUpperCase();
    if (dto.specialties !== undefined) vendor.specialties = dto.specialties;
    if (dto.rating !== undefined) vendor.rating = dto.rating;
    if (dto.certifications !== undefined) vendor.certifications = dto.certifications;
    if (dto.performanceHistory !== undefined) vendor.performanceHistory = dto.performanceHistory;
    if (dto.contactName !== undefined) vendor.contactName = dto.contactName;
    if (dto.contactEmail !== undefined) vendor.contactEmail = dto.contactEmail;
    if (dto.contactPhone !== undefined) vendor.contactPhone = dto.contactPhone;
    if (dto.isActive !== undefined) vendor.isActive = dto.isActive;
    if (dto.documentationValid !== undefined) vendor.documentationValid = dto.documentationValid;
    if (ctx.userId) {
      vendor.updatedById = ctx.userId;
    }

    return this.repository.save(vendor);
  }

  /**
   * Soft delete - marca como inactivo
   */
  async softDelete(ctx: ServiceContext, id: string): Promise<boolean> {
    const result = await this.repository.update(
      { id, tenantId: ctx.tenantId },
      { deletedAt: new Date(), isActive: false, updatedById: ctx.userId }
    );
    return (result.affected || 0) > 0;
  }

  /**
   * Actualizar rating de vendor
   */
  async updateRating(ctx: ServiceContext, id: string, newRating: number): Promise<Vendor> {
    const vendor = await this.findById(ctx, id);
    if (!vendor) {
      throw new Error('Vendor not found');
    }

    if (newRating < 1 || newRating > 5) {
      throw new Error('Rating must be between 1 and 5');
    }

    vendor.rating = newRating;
    if (ctx.userId) {
      vendor.updatedById = ctx.userId;
    }

    return this.repository.save(vendor);
  }

  /**
   * Obtener historial de rendimiento del vendor
   */
  async getPerformanceHistory(ctx: ServiceContext, id: string): Promise<VendorPerformanceHistory> {
    const vendor = await this.repository.findOne({
      where: { id, tenantId: ctx.tenantId, deletedAt: undefined },
      relations: ['proposals', 'proposals.tender'],
    });

    if (!vendor) {
      throw new Error('Vendor not found');
    }

    const proposals = (vendor.proposals || []).map(p => ({
      tenderId: p.tenderId,
      tenderTitle: p.tender?.title || 'Unknown',
      submittedAt: p.submittedAt,
      status: p.status,
      proposedAmount: p.proposedAmount,
      technicalScore: p.technicalScore,
      economicScore: p.economicScore,
      totalScore: p.totalScore,
      isWinner: p.status === 'winner',
    }));

    const wins = proposals.filter(p => p.isWinner).length;
    const totalParticipations = proposals.length;

    const scoredProposals = proposals.filter(p => p.totalScore !== undefined && p.totalScore !== null);
    const avgTechnicalScore = scoredProposals.length > 0
      ? scoredProposals.reduce((sum, p) => sum + (p.technicalScore || 0), 0) / scoredProposals.length
      : 0;
    const avgEconomicScore = scoredProposals.length > 0
      ? scoredProposals.reduce((sum, p) => sum + (p.economicScore || 0), 0) / scoredProposals.length
      : 0;
    const avgTotalScore = scoredProposals.length > 0
      ? scoredProposals.reduce((sum, p) => sum + (p.totalScore || 0), 0) / scoredProposals.length
      : 0;

    const totalProposedValue = proposals.reduce((sum, p) => sum + (parseFloat(p.proposedAmount || '0') || 0), 0);
    const totalWonValue = proposals
      .filter(p => p.isWinner)
      .reduce((sum, p) => sum + (parseFloat(p.proposedAmount || '0') || 0), 0);

    const vendorRecord: VendorRecord = {
      id: vendor.id,
      code: vendor.code,
      businessName: vendor.businessName,
      rfc: vendor.rfc,
      contactName: vendor.contactName,
      contactEmail: vendor.contactEmail,
      contactPhone: vendor.contactPhone,
      specialties: vendor.specialties,
      rating: vendor.rating,
      totalParticipations,
      totalWins: wins,
      isActive: vendor.isActive,
      createdAt: vendor.createdAt,
      updatedAt: vendor.updatedAt,
    };

    return {
      vendor: vendorRecord,
      proposals,
      statistics: {
        totalParticipations,
        wins,
        losses: totalParticipations - wins,
        winRate: totalParticipations > 0 ? (wins / totalParticipations) * 100 : 0,
        avgTechnicalScore,
        avgEconomicScore,
        avgTotalScore,
        totalProposedValue,
        totalWonValue,
      },
    };
  }
}
