/**
 * BidAnalyticsService - Análisis y Reportes de Licitaciones
 *
 * Estadísticas, tendencias y análisis de competitividad.
 *
 * @module Bidding
 */

import { Repository } from 'typeorm';
import { ServiceContext } from '../../../shared/services/base.service';
import { Bid, BidStatus, BidType } from '../entities/bid.entity';
import { Opportunity, OpportunitySource, OpportunityStatus } from '../entities/opportunity.entity';
import { BidCompetitor } from '../entities/bid-competitor.entity';

export class BidAnalyticsService {
  constructor(
    private readonly bidRepository: Repository<Bid>,
    private readonly opportunityRepository: Repository<Opportunity>,
    private readonly competitorRepository: Repository<BidCompetitor>
  ) {}

  /**
   * Dashboard general de licitaciones
   */
  async getDashboard(ctx: ServiceContext): Promise<BidDashboard> {
    const now = new Date();
    const thirtyDaysAgo = new Date();
    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

    // Oportunidades activas
    const activeOpportunities = await this.opportunityRepository.count({
      where: {
        tenantId: ctx.tenantId,
        deletedAt: undefined,
        status: 'pursuing' as OpportunityStatus,
      },
    });

    // Licitaciones activas
    const activeBids = await this.bidRepository.count({
      where: {
        tenantId: ctx.tenantId,
        deletedAt: undefined,
        status: 'preparation' as BidStatus,
      },
    });

    // Valor del pipeline
    const pipelineValue = await this.opportunityRepository
      .createQueryBuilder('o')
      .select('SUM(o.weighted_value)', 'value')
      .where('o.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('o.deleted_at IS NULL')
      .andWhere('o.status NOT IN (:...closedStatuses)', { closedStatuses: ['won', 'lost', 'cancelled'] })
      .getRawOne();

    // Próximas fechas límite
    const upcomingDeadlines = await this.bidRepository
      .createQueryBuilder('b')
      .where('b.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('b.deleted_at IS NULL')
      .andWhere('b.status IN (:...activeStatuses)', { activeStatuses: ['draft', 'preparation', 'review', 'approved'] })
      .andWhere('b.submission_deadline >= :now', { now })
      .orderBy('b.submission_deadline', 'ASC')
      .take(5)
      .getMany();

    // Win rate últimos 12 meses
    const yearAgo = new Date();
    yearAgo.setFullYear(yearAgo.getFullYear() - 1);

    const winRateStats = await this.bidRepository
      .createQueryBuilder('b')
      .select('b.status', 'status')
      .addSelect('COUNT(*)', 'count')
      .where('b.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('b.deleted_at IS NULL')
      .andWhere('b.status IN (:...closedStatuses)', { closedStatuses: ['awarded', 'rejected'] })
      .andWhere('b.award_date >= :yearAgo', { yearAgo })
      .groupBy('b.status')
      .getRawMany();

    const awarded = winRateStats.find((s) => s.status === 'awarded')?.count || 0;
    const rejected = winRateStats.find((s) => s.status === 'rejected')?.count || 0;
    const totalClosed = parseInt(awarded) + parseInt(rejected);
    const winRate = totalClosed > 0 ? (parseInt(awarded) / totalClosed) * 100 : 0;

    // Valor ganado este año
    const startOfYear = new Date(now.getFullYear(), 0, 1);
    const wonValue = await this.bidRepository
      .createQueryBuilder('b')
      .select('SUM(b.winning_amount)', 'value')
      .where('b.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('b.deleted_at IS NULL')
      .andWhere('b.status = :status', { status: 'awarded' })
      .andWhere('b.award_date >= :startOfYear', { startOfYear })
      .getRawOne();

    return {
      activeOpportunities,
      activeBids,
      pipelineValue: parseFloat(pipelineValue?.value) || 0,
      upcomingDeadlines: upcomingDeadlines.map((b) => ({
        id: b.id,
        name: b.name,
        deadline: b.submissionDeadline,
        status: b.status,
      })),
      winRate,
      wonValueYTD: parseFloat(wonValue?.value) || 0,
    };
  }

  /**
   * Análisis de pipeline por fuente
   */
  async getPipelineBySource(ctx: ServiceContext): Promise<PipelineBySource[]> {
    const result = await this.opportunityRepository
      .createQueryBuilder('o')
      .select('o.source', 'source')
      .addSelect('COUNT(*)', 'count')
      .addSelect('SUM(o.estimated_value)', 'totalValue')
      .addSelect('SUM(o.weighted_value)', 'weightedValue')
      .where('o.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('o.deleted_at IS NULL')
      .andWhere('o.status NOT IN (:...closedStatuses)', { closedStatuses: ['won', 'lost', 'cancelled'] })
      .groupBy('o.source')
      .orderBy('SUM(o.weighted_value)', 'DESC')
      .getRawMany();

    return result.map((r) => ({
      source: r.source as OpportunitySource,
      count: parseInt(r.count),
      totalValue: parseFloat(r.totalValue) || 0,
      weightedValue: parseFloat(r.weightedValue) || 0,
    }));
  }

  /**
   * Análisis de win rate por tipo de licitación
   */
  async getWinRateByType(ctx: ServiceContext, months = 12): Promise<WinRateByType[]> {
    const fromDate = new Date();
    fromDate.setMonth(fromDate.getMonth() - months);

    const result = await this.bidRepository
      .createQueryBuilder('b')
      .select('b.bid_type', 'bidType')
      .addSelect('COUNT(*) FILTER (WHERE b.status = \'awarded\')', 'won')
      .addSelect('COUNT(*) FILTER (WHERE b.status IN (\'awarded\', \'rejected\'))', 'total')
      .addSelect('SUM(CASE WHEN b.status = \'awarded\' THEN b.winning_amount ELSE 0 END)', 'wonValue')
      .where('b.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('b.deleted_at IS NULL')
      .andWhere('b.award_date >= :fromDate', { fromDate })
      .groupBy('b.bid_type')
      .getRawMany();

    return result.map((r) => ({
      bidType: r.bidType as BidType,
      won: parseInt(r.won) || 0,
      total: parseInt(r.total) || 0,
      winRate: parseInt(r.total) > 0 ? (parseInt(r.won) / parseInt(r.total)) * 100 : 0,
      wonValue: parseFloat(r.wonValue) || 0,
    }));
  }

  /**
   * Tendencia mensual de oportunidades
   */
  async getMonthlyTrend(ctx: ServiceContext, months = 12): Promise<MonthlyTrend[]> {
    const fromDate = new Date();
    fromDate.setMonth(fromDate.getMonth() - months);

    const result = await this.opportunityRepository
      .createQueryBuilder('o')
      .select("TO_CHAR(o.identification_date, 'YYYY-MM')", 'month')
      .addSelect('COUNT(*)', 'identified')
      .addSelect('COUNT(*) FILTER (WHERE o.status = \'won\')', 'won')
      .addSelect('COUNT(*) FILTER (WHERE o.status = \'lost\')', 'lost')
      .addSelect('SUM(CASE WHEN o.status = \'won\' THEN o.estimated_value ELSE 0 END)', 'wonValue')
      .where('o.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('o.deleted_at IS NULL')
      .andWhere('o.identification_date >= :fromDate', { fromDate })
      .groupBy("TO_CHAR(o.identification_date, 'YYYY-MM')")
      .orderBy("TO_CHAR(o.identification_date, 'YYYY-MM')", 'ASC')
      .getRawMany();

    return result.map((r) => ({
      month: r.month,
      identified: parseInt(r.identified) || 0,
      won: parseInt(r.won) || 0,
      lost: parseInt(r.lost) || 0,
      wonValue: parseFloat(r.wonValue) || 0,
    }));
  }

  /**
   * Análisis de competidores
   */
  async getCompetitorAnalysis(ctx: ServiceContext): Promise<CompetitorAnalysis[]> {
    const result = await this.competitorRepository
      .createQueryBuilder('c')
      .select('c.company_name', 'companyName')
      .addSelect('COUNT(*)', 'encounters')
      .addSelect('SUM(CASE WHEN c.status = \'winner\' THEN 1 ELSE 0 END)', 'theirWins')
      .addSelect('SUM(CASE WHEN c.status = \'loser\' THEN 1 ELSE 0 END)', 'ourWins')
      .addSelect('AVG(c.proposed_amount)', 'avgProposedAmount')
      .where('c.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('c.deleted_at IS NULL')
      .groupBy('c.company_name')
      .having('COUNT(*) >= 2')
      .orderBy('COUNT(*)', 'DESC')
      .take(20)
      .getRawMany();

    return result.map((r) => ({
      companyName: r.companyName,
      encounters: parseInt(r.encounters) || 0,
      theirWins: parseInt(r.theirWins) || 0,
      ourWins: parseInt(r.ourWins) || 0,
      winRateAgainst: parseInt(r.encounters) > 0
        ? (parseInt(r.ourWins) / parseInt(r.encounters)) * 100
        : 0,
      avgProposedAmount: parseFloat(r.avgProposedAmount) || 0,
    }));
  }

  /**
   * Análisis de conversión del funnel
   */
  async getFunnelAnalysis(ctx: ServiceContext, months = 12): Promise<FunnelAnalysis> {
    const fromDate = new Date();
    fromDate.setMonth(fromDate.getMonth() - months);

    const baseQuery = this.opportunityRepository
      .createQueryBuilder('o')
      .where('o.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('o.deleted_at IS NULL')
      .andWhere('o.identification_date >= :fromDate', { fromDate });

    const identified = await baseQuery.clone().getCount();

    const qualified = await baseQuery.clone()
      .andWhere('o.status NOT IN (:...earlyStatuses)', { earlyStatuses: ['identified'] })
      .getCount();

    const pursuing = await baseQuery.clone()
      .andWhere('o.status IN (:...pursuitStatuses)', { pursuitStatuses: ['pursuing', 'bid_submitted', 'won', 'lost'] })
      .getCount();

    const bidSubmitted = await baseQuery.clone()
      .andWhere('o.status IN (:...submittedStatuses)', { submittedStatuses: ['bid_submitted', 'won', 'lost'] })
      .getCount();

    const won = await baseQuery.clone()
      .andWhere('o.status = :status', { status: 'won' })
      .getCount();

    return {
      identified,
      qualified,
      pursuing,
      bidSubmitted,
      won,
      conversionRates: {
        identifiedToQualified: identified > 0 ? (qualified / identified) * 100 : 0,
        qualifiedToPursuing: qualified > 0 ? (pursuing / qualified) * 100 : 0,
        pursuingToSubmitted: pursuing > 0 ? (bidSubmitted / pursuing) * 100 : 0,
        submittedToWon: bidSubmitted > 0 ? (won / bidSubmitted) * 100 : 0,
        overallConversion: identified > 0 ? (won / identified) * 100 : 0,
      },
    };
  }

  /**
   * Análisis de tiempos de ciclo
   */
  async getCycleTimeAnalysis(ctx: ServiceContext, months = 12): Promise<CycleTimeAnalysis> {
    const fromDate = new Date();
    fromDate.setMonth(fromDate.getMonth() - months);

    const result = await this.opportunityRepository
      .createQueryBuilder('o')
      .select('AVG(EXTRACT(DAY FROM (o.updated_at - o.identification_date)))', 'avgDays')
      .addSelect('MIN(EXTRACT(DAY FROM (o.updated_at - o.identification_date)))', 'minDays')
      .addSelect('MAX(EXTRACT(DAY FROM (o.updated_at - o.identification_date)))', 'maxDays')
      .where('o.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('o.deleted_at IS NULL')
      .andWhere('o.status IN (:...closedStatuses)', { closedStatuses: ['won', 'lost'] })
      .andWhere('o.identification_date >= :fromDate', { fromDate })
      .getRawOne();

    const byOutcome = await this.opportunityRepository
      .createQueryBuilder('o')
      .select('o.status', 'outcome')
      .addSelect('AVG(EXTRACT(DAY FROM (o.updated_at - o.identification_date)))', 'avgDays')
      .where('o.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('o.deleted_at IS NULL')
      .andWhere('o.status IN (:...closedStatuses)', { closedStatuses: ['won', 'lost'] })
      .andWhere('o.identification_date >= :fromDate', { fromDate })
      .groupBy('o.status')
      .getRawMany();

    return {
      overall: {
        avgDays: Math.round(parseFloat(result?.avgDays) || 0),
        minDays: Math.round(parseFloat(result?.minDays) || 0),
        maxDays: Math.round(parseFloat(result?.maxDays) || 0),
      },
      byOutcome: byOutcome.map((r) => ({
        outcome: r.outcome as 'won' | 'lost',
        avgDays: Math.round(parseFloat(r.avgDays) || 0),
      })),
    };
  }
}

// Types
export interface BidDashboard {
  activeOpportunities: number;
  activeBids: number;
  pipelineValue: number;
  upcomingDeadlines: { id: string; name: string; deadline: Date; status: BidStatus }[];
  winRate: number;
  wonValueYTD: number;
}

export interface PipelineBySource {
  source: OpportunitySource;
  count: number;
  totalValue: number;
  weightedValue: number;
}

export interface WinRateByType {
  bidType: BidType;
  won: number;
  total: number;
  winRate: number;
  wonValue: number;
}

export interface MonthlyTrend {
  month: string;
  identified: number;
  won: number;
  lost: number;
  wonValue: number;
}

export interface CompetitorAnalysis {
  companyName: string;
  encounters: number;
  theirWins: number;
  ourWins: number;
  winRateAgainst: number;
  avgProposedAmount: number;
}

export interface FunnelAnalysis {
  identified: number;
  qualified: number;
  pursuing: number;
  bidSubmitted: number;
  won: number;
  conversionRates: {
    identifiedToQualified: number;
    qualifiedToPursuing: number;
    pursuingToSubmitted: number;
    submittedToWon: number;
    overallConversion: number;
  };
}

export interface CycleTimeAnalysis {
  overall: {
    avgDays: number;
    minDays: number;
    maxDays: number;
  };
  byOutcome: {
    outcome: 'won' | 'lost';
    avgDays: number;
  }[];
}
