/**
 * BidService - Gestión de Licitaciones
 *
 * CRUD y lógica de negocio para licitaciones/propuestas.
 *
 * @module Bidding
 */

import { Repository, In, Between } from 'typeorm';
import { ServiceContext, PaginatedResult } from '../../../shared/services/base.service';
import { Bid, BidType, BidStatus, BidStage } from '../entities/bid.entity';

export interface CreateBidDto {
  opportunityId: string;
  code: string;
  name: string;
  description?: string;
  bidType: BidType;
  tenderNumber?: string;
  tenderName?: string;
  contractingEntity?: string;
  publicationDate?: Date;
  siteVisitDate?: Date;
  clarificationDeadline?: Date;
  submissionDeadline: Date;
  openingDate?: Date;
  baseBudget?: number;
  currency?: string;
  technicalWeight?: number;
  economicWeight?: number;
  bidBondAmount?: number;
  bidManagerId?: string;
  notes?: string;
  metadata?: Record<string, any>;
}

export interface UpdateBidDto extends Partial<CreateBidDto> {
  status?: BidStatus;
  stage?: BidStage;
  ourProposalAmount?: number;
  technicalScore?: number;
  economicScore?: number;
  finalScore?: number;
  rankingPosition?: number;
  bidBondNumber?: string;
  bidBondExpiry?: Date;
  awardDate?: Date;
  contractSigningDate?: Date;
  winnerName?: string;
  winningAmount?: number;
  rejectionReason?: string;
  lessonsLearned?: string;
  completionPercentage?: number;
  checklist?: Record<string, any>;
}

export interface BidFilters {
  status?: BidStatus | BidStatus[];
  bidType?: BidType;
  stage?: BidStage;
  opportunityId?: string;
  bidManagerId?: string;
  contractingEntity?: string;
  deadlineFrom?: Date;
  deadlineTo?: Date;
  minBudget?: number;
  maxBudget?: number;
  search?: string;
}

export class BidService {
  constructor(private readonly repository: Repository<Bid>) {}

  /**
   * Crear licitación
   */
  async create(ctx: ServiceContext, data: CreateBidDto): Promise<Bid> {
    const bid = this.repository.create({
      tenantId: ctx.tenantId,
      ...data,
      status: 'draft',
      stage: 'initial',
      completionPercentage: 0,
      createdBy: ctx.userId,
      updatedBy: ctx.userId,
    });

    return this.repository.save(bid);
  }

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

  /**
   * Buscar con filtros
   */
  async findWithFilters(
    ctx: ServiceContext,
    filters: BidFilters,
    page = 1,
    limit = 20
  ): Promise<PaginatedResult<Bid>> {
    const qb = this.repository
      .createQueryBuilder('b')
      .leftJoinAndSelect('b.opportunity', 'o')
      .leftJoinAndSelect('b.bidManager', 'm')
      .where('b.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('b.deleted_at IS NULL');

    if (filters.status) {
      if (Array.isArray(filters.status)) {
        qb.andWhere('b.status IN (:...statuses)', { statuses: filters.status });
      } else {
        qb.andWhere('b.status = :status', { status: filters.status });
      }
    }
    if (filters.bidType) {
      qb.andWhere('b.bid_type = :bidType', { bidType: filters.bidType });
    }
    if (filters.stage) {
      qb.andWhere('b.stage = :stage', { stage: filters.stage });
    }
    if (filters.opportunityId) {
      qb.andWhere('b.opportunity_id = :opportunityId', { opportunityId: filters.opportunityId });
    }
    if (filters.bidManagerId) {
      qb.andWhere('b.bid_manager_id = :bidManagerId', { bidManagerId: filters.bidManagerId });
    }
    if (filters.contractingEntity) {
      qb.andWhere('b.contracting_entity ILIKE :entity', { entity: `%${filters.contractingEntity}%` });
    }
    if (filters.deadlineFrom) {
      qb.andWhere('b.submission_deadline >= :deadlineFrom', { deadlineFrom: filters.deadlineFrom });
    }
    if (filters.deadlineTo) {
      qb.andWhere('b.submission_deadline <= :deadlineTo', { deadlineTo: filters.deadlineTo });
    }
    if (filters.minBudget !== undefined) {
      qb.andWhere('b.base_budget >= :minBudget', { minBudget: filters.minBudget });
    }
    if (filters.maxBudget !== undefined) {
      qb.andWhere('b.base_budget <= :maxBudget', { maxBudget: filters.maxBudget });
    }
    if (filters.search) {
      qb.andWhere(
        '(b.name ILIKE :search OR b.code ILIKE :search OR b.tender_number ILIKE :search)',
        { search: `%${filters.search}%` }
      );
    }

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

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

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

  /**
   * Actualizar licitación
   */
  async update(ctx: ServiceContext, id: string, data: UpdateBidDto): Promise<Bid | null> {
    const bid = await this.findById(ctx, id);
    if (!bid) return null;

    // Calcular puntuación final si hay scores
    let finalScore = data.finalScore ?? bid.finalScore;
    const techScore = data.technicalScore ?? bid.technicalScore;
    const econScore = data.economicScore ?? bid.economicScore;
    const techWeight = data.technicalWeight ?? bid.technicalWeight;
    const econWeight = data.economicWeight ?? bid.economicWeight;

    if (techScore !== undefined && econScore !== undefined) {
      finalScore = (techScore * techWeight / 100) + (econScore * econWeight / 100);
    }

    Object.assign(bid, {
      ...data,
      finalScore,
      updatedBy: ctx.userId,
    });

    return this.repository.save(bid);
  }

  /**
   * Cambiar estado
   */
  async changeStatus(ctx: ServiceContext, id: string, status: BidStatus): Promise<Bid | null> {
    const bid = await this.findById(ctx, id);
    if (!bid) return null;

    bid.status = status;
    bid.updatedBy = ctx.userId;

    return this.repository.save(bid);
  }

  /**
   * Cambiar etapa
   */
  async changeStage(ctx: ServiceContext, id: string, stage: BidStage): Promise<Bid | null> {
    const bid = await this.findById(ctx, id);
    if (!bid) return null;

    bid.stage = stage;
    bid.updatedBy = ctx.userId;

    return this.repository.save(bid);
  }

  /**
   * Marcar como presentada
   */
  async submit(ctx: ServiceContext, id: string, proposalAmount: number): Promise<Bid | null> {
    const bid = await this.findById(ctx, id);
    if (!bid) return null;

    bid.status = 'submitted';
    bid.stage = 'post_submission';
    bid.ourProposalAmount = proposalAmount;
    bid.updatedBy = ctx.userId;

    return this.repository.save(bid);
  }

  /**
   * Registrar resultado
   */
  async recordResult(
    ctx: ServiceContext,
    id: string,
    won: boolean,
    details: {
      winnerName?: string;
      winningAmount?: number;
      rankingPosition?: number;
      rejectionReason?: string;
      lessonsLearned?: string;
    }
  ): Promise<Bid | null> {
    const bid = await this.findById(ctx, id);
    if (!bid) return null;

    bid.status = won ? 'awarded' : 'rejected';
    bid.awardDate = new Date();
    Object.assign(bid, details);
    bid.updatedBy = ctx.userId;

    return this.repository.save(bid);
  }

  /**
   * Convertir a proyecto
   */
  async convertToProject(ctx: ServiceContext, id: string, projectId: string): Promise<Bid | null> {
    const bid = await this.findById(ctx, id);
    if (!bid || bid.status !== 'awarded') return null;

    bid.convertedToProjectId = projectId;
    bid.convertedAt = new Date();
    bid.updatedBy = ctx.userId;

    return this.repository.save(bid);
  }

  /**
   * Obtener próximas fechas límite
   */
  async getUpcomingDeadlines(ctx: ServiceContext, days = 7): Promise<Bid[]> {
    const now = new Date();
    const future = new Date();
    future.setDate(future.getDate() + days);

    return this.repository.find({
      where: {
        tenantId: ctx.tenantId,
        deletedAt: undefined,
        status: In(['draft', 'preparation', 'review', 'approved']),
        submissionDeadline: Between(now, future),
      },
      relations: ['opportunity', 'bidManager'],
      order: { submissionDeadline: 'ASC' },
    });
  }

  /**
   * Obtener estadísticas
   */
  async getStats(ctx: ServiceContext, year?: number): Promise<BidStats> {
    const currentYear = year || new Date().getFullYear();
    const startDate = new Date(currentYear, 0, 1);
    const endDate = new Date(currentYear, 11, 31);

    const total = await this.repository
      .createQueryBuilder('b')
      .where('b.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('b.deleted_at IS NULL')
      .andWhere('b.created_at BETWEEN :startDate AND :endDate', { startDate, endDate })
      .getCount();

    const byStatus = await this.repository
      .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.created_at BETWEEN :startDate AND :endDate', { startDate, endDate })
      .groupBy('b.status')
      .getRawMany();

    const byType = await this.repository
      .createQueryBuilder('b')
      .select('b.bid_type', 'bidType')
      .addSelect('COUNT(*)', 'count')
      .where('b.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('b.deleted_at IS NULL')
      .andWhere('b.created_at BETWEEN :startDate AND :endDate', { startDate, endDate })
      .groupBy('b.bid_type')
      .getRawMany();

    const valueStats = await this.repository
      .createQueryBuilder('b')
      .select('SUM(b.base_budget)', 'totalBudget')
      .addSelect('SUM(b.our_proposal_amount)', 'totalProposed')
      .addSelect('SUM(CASE WHEN b.status = \'awarded\' THEN b.winning_amount ELSE 0 END)', 'totalWon')
      .where('b.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('b.deleted_at IS NULL')
      .andWhere('b.created_at BETWEEN :startDate AND :endDate', { startDate, endDate })
      .getRawOne();

    const awardedCount = byStatus.find((s) => s.status === 'awarded')?.count || 0;
    const rejectedCount = byStatus.find((s) => s.status === 'rejected')?.count || 0;
    const closedCount = parseInt(awardedCount) + parseInt(rejectedCount);

    return {
      year: currentYear,
      total,
      byStatus: byStatus.map((r) => ({ status: r.status, count: parseInt(r.count) })),
      byType: byType.map((r) => ({ bidType: r.bidType, count: parseInt(r.count) })),
      totalBudget: parseFloat(valueStats?.totalBudget) || 0,
      totalProposed: parseFloat(valueStats?.totalProposed) || 0,
      totalWon: parseFloat(valueStats?.totalWon) || 0,
      winRate: closedCount > 0 ? (parseInt(awardedCount) / closedCount) * 100 : 0,
    };
  }

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

export interface BidStats {
  year: number;
  total: number;
  byStatus: { status: BidStatus; count: number }[];
  byType: { bidType: BidType; count: number }[];
  totalBudget: number;
  totalProposed: number;
  totalWon: number;
  winRate: number;
}
