/**
 * KpiService - Gestión de KPIs y Analytics
 *
 * Administra snapshots de KPIs, cálculos y tendencias.
 *
 * @module Reports
 */

import { Repository, DataSource, LessThanOrEqual } from 'typeorm';
import { KpiSnapshot, KpiCategory, KpiPeriodType, TrendDirection } from '../entities/kpi-snapshot.entity';

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

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

export interface CreateKpiSnapshotDto {
  kpiCode: string;
  kpiName: string;
  category: KpiCategory;
  snapshotDate: Date;
  periodType?: KpiPeriodType;
  periodStart?: Date;
  periodEnd?: Date;
  fraccionamientoId?: string;
  value: number;
  previousValue?: number;
  targetValue?: number;
  minValue?: number;
  maxValue?: number;
  unit?: string;
  breakdown?: Record<string, any>;
  metadata?: Record<string, any>;
}

export interface KpiFilters {
  kpiCode?: string;
  category?: KpiCategory;
  periodType?: KpiPeriodType;
  fraccionamientoId?: string;
  dateFrom?: Date;
  dateTo?: Date;
}

export interface KpiTrendData {
  kpiCode: string;
  kpiName: string;
  category: KpiCategory;
  currentValue: number;
  previousValue: number | null;
  targetValue: number | null;
  changePercentage: number | null;
  trend: TrendDirection;
  isOnTarget: boolean;
  statusColor: string;
  unit: string | null;
  history: { date: Date; value: number }[];
}

export interface KpiSummary {
  category: KpiCategory;
  kpis: {
    code: string;
    name: string;
    value: number;
    target: number | null;
    trend: TrendDirection;
    statusColor: string;
    unit: string | null;
  }[];
}

export class KpiService {
  private repository: Repository<KpiSnapshot>;

  constructor(dataSource: DataSource) {
    this.repository = dataSource.getRepository(KpiSnapshot);
  }

  /**
   * Buscar por ID
   */
  async findById(ctx: ServiceContext, id: string): Promise<KpiSnapshot | null> {
    return this.repository.findOne({
      where: {
        id,
        tenantId: ctx.tenantId,
      } as any,
    });
  }

  /**
   * Crear entidad
   */
  async create(ctx: ServiceContext, data: Partial<KpiSnapshot>): Promise<KpiSnapshot> {
    const entity = this.repository.create({
      ...data,
      tenantId: ctx.tenantId,
    });
    return this.repository.save(entity);
  }

  /**
   * Buscar snapshots con filtros
   */
  async findWithFilters(
    ctx: ServiceContext,
    filters: KpiFilters,
    page = 1,
    limit = 20
  ): Promise<PaginatedResult<KpiSnapshot>> {
    const qb = this.repository
      .createQueryBuilder('k')
      .where('k.tenant_id = :tenantId', { tenantId: ctx.tenantId });

    if (filters.kpiCode) {
      qb.andWhere('k.kpi_code = :kpiCode', { kpiCode: filters.kpiCode });
    }
    if (filters.category) {
      qb.andWhere('k.category = :category', { category: filters.category });
    }
    if (filters.periodType) {
      qb.andWhere('k.period_type = :periodType', { periodType: filters.periodType });
    }
    if (filters.fraccionamientoId) {
      qb.andWhere('k.fraccionamiento_id = :fraccionamientoId', { fraccionamientoId: filters.fraccionamientoId });
    }
    if (filters.dateFrom) {
      qb.andWhere('k.snapshot_date >= :dateFrom', { dateFrom: filters.dateFrom });
    }
    if (filters.dateTo) {
      qb.andWhere('k.snapshot_date <= :dateTo', { dateTo: filters.dateTo });
    }

    const skip = (page - 1) * limit;
    qb.orderBy('k.snapshot_date', 'DESC').skip(skip).take(limit);

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

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

  /**
   * Crear snapshot de KPI
   */
  async createSnapshot(ctx: ServiceContext, data: CreateKpiSnapshotDto): Promise<KpiSnapshot> {
    // Calcular cambio porcentual y tendencia
    const changePercentage = data.previousValue
      ? ((data.value - data.previousValue) / Math.abs(data.previousValue)) * 100
      : null;

    let trendDirection: TrendDirection = 'stable';
    if (changePercentage !== null) {
      if (changePercentage > 1) trendDirection = 'up';
      else if (changePercentage < -1) trendDirection = 'down';
    }

    // Determinar si está en objetivo
    let isOnTarget: boolean | null = null;
    let statusColor = 'gray';
    if (data.targetValue !== undefined) {
      isOnTarget = data.value >= data.targetValue;
      if (isOnTarget) {
        statusColor = 'green';
      } else if (data.value >= data.targetValue * 0.9) {
        statusColor = 'yellow';
      } else {
        statusColor = 'red';
      }
    }

    return this.create(ctx, {
      ...data,
      changePercentage,
      trendDirection,
      isOnTarget,
      statusColor,
    });
  }

  /**
   * Obtener último snapshot de un KPI
   */
  async getLatestSnapshot(
    ctx: ServiceContext,
    kpiCode: string,
    fraccionamientoId?: string
  ): Promise<KpiSnapshot | null> {
    const where: any = {
      tenantId: ctx.tenantId,
      kpiCode,
    };
    if (fraccionamientoId) {
      where.fraccionamientoId = fraccionamientoId;
    }

    return this.repository.findOne({
      where,
      order: { snapshotDate: 'DESC' },
    });
  }

  /**
   * Obtener historial de un KPI
   */
  async getKpiHistory(
    ctx: ServiceContext,
    kpiCode: string,
    dateFrom: Date,
    dateTo: Date,
    fraccionamientoId?: string
  ): Promise<KpiSnapshot[]> {
    const qb = this.repository
      .createQueryBuilder('k')
      .where('k.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('k.kpi_code = :kpiCode', { kpiCode })
      .andWhere('k.snapshot_date BETWEEN :dateFrom AND :dateTo', { dateFrom, dateTo });

    if (fraccionamientoId) {
      qb.andWhere('k.fraccionamiento_id = :fraccionamientoId', { fraccionamientoId });
    }

    return qb.orderBy('k.snapshot_date', 'ASC').getMany();
  }

  /**
   * Obtener tendencia de KPI con historial
   */
  async getKpiTrend(
    ctx: ServiceContext,
    kpiCode: string,
    days = 30,
    fraccionamientoId?: string
  ): Promise<KpiTrendData | null> {
    const dateTo = new Date();
    const dateFrom = new Date();
    dateFrom.setDate(dateFrom.getDate() - days);

    const history = await this.getKpiHistory(ctx, kpiCode, dateFrom, dateTo, fraccionamientoId);
    if (!history.length) {
      return null;
    }

    const latest = history[history.length - 1];
    const previous = history.length > 1 ? history[history.length - 2] : null;

    return {
      kpiCode: latest.kpiCode,
      kpiName: latest.kpiName,
      category: latest.category,
      currentValue: Number(latest.value),
      previousValue: previous ? Number(previous.value) : null,
      targetValue: latest.targetValue ? Number(latest.targetValue) : null,
      changePercentage: latest.changePercentage ? Number(latest.changePercentage) : null,
      trend: latest.trendDirection || 'stable',
      isOnTarget: latest.isOnTarget || false,
      statusColor: latest.statusColor || 'gray',
      unit: latest.unit,
      history: history.map((h) => ({
        date: h.snapshotDate,
        value: Number(h.value),
      })),
    };
  }

  /**
   * Obtener resumen de KPIs por categoría
   */
  async getSummaryByCategory(
    ctx: ServiceContext,
    category: KpiCategory,
    fraccionamientoId?: string
  ): Promise<KpiSummary> {
    // Obtener los KPIs únicos de la categoría
    const qb = this.repository
      .createQueryBuilder('k')
      .select('DISTINCT k.kpi_code', 'kpiCode')
      .addSelect('k.kpi_name', 'kpiName')
      .where('k.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('k.category = :category', { category });

    if (fraccionamientoId) {
      qb.andWhere('k.fraccionamiento_id = :fraccionamientoId', { fraccionamientoId });
    }

    const kpiCodes = await qb.getRawMany();

    const kpis = [];
    for (const kpi of kpiCodes) {
      const latest = await this.getLatestSnapshot(ctx, kpi.kpiCode, fraccionamientoId);
      if (latest) {
        kpis.push({
          code: latest.kpiCode,
          name: latest.kpiName,
          value: Number(latest.value),
          target: latest.targetValue ? Number(latest.targetValue) : null,
          trend: latest.trendDirection || 'stable',
          statusColor: latest.statusColor || 'gray',
          unit: latest.unit,
        });
      }
    }

    return {
      category,
      kpis,
    };
  }

  /**
   * Obtener dashboard de KPIs
   */
  async getDashboardKpis(
    ctx: ServiceContext,
    fraccionamientoId?: string
  ): Promise<{ [category: string]: KpiSummary }> {
    const categories: KpiCategory[] = [
      'financial',
      'progress',
      'quality',
      'hse',
      'hr',
      'inventory',
      'operational',
    ];

    const result: { [category: string]: KpiSummary } = {};

    for (const category of categories) {
      const summary = await this.getSummaryByCategory(ctx, category, fraccionamientoId);
      if (summary.kpis.length > 0) {
        result[category] = summary;
      }
    }

    return result;
  }

  /**
   * Comparar KPIs entre proyectos
   */
  async compareProjects(
    ctx: ServiceContext,
    kpiCode: string,
    fraccionamientoIds: string[],
    snapshotDate?: Date
  ): Promise<{ fraccionamientoId: string; value: number; target: number | null }[]> {
    const date = snapshotDate || new Date();

    const results = [];
    for (const fraccionamientoId of fraccionamientoIds) {
      const snapshot = await this.repository.findOne({
        where: {
          tenantId: ctx.tenantId,
          kpiCode,
          fraccionamientoId,
          snapshotDate: LessThanOrEqual(date),
        } as any,
        order: { snapshotDate: 'DESC' },
      });

      if (snapshot) {
        results.push({
          fraccionamientoId,
          value: Number(snapshot.value),
          target: snapshot.targetValue ? Number(snapshot.targetValue) : null,
        });
      }
    }

    return results;
  }

  /**
   * Calcular y guardar snapshot masivo (para jobs)
   */
  async bulkCreateSnapshots(
    ctx: ServiceContext,
    snapshots: CreateKpiSnapshotDto[]
  ): Promise<{ created: number; errors: number }> {
    let created = 0;
    let errors = 0;

    for (const data of snapshots) {
      try {
        await this.createSnapshot(ctx, data);
        created++;
      } catch {
        errors++;
      }
    }

    return { created, errors };
  }

  /**
   * Estadísticas de KPIs
   */
  async getStats(ctx: ServiceContext): Promise<KpiStats> {
    const qb = this.repository
      .createQueryBuilder('k')
      .select('k.category', 'category')
      .addSelect('COUNT(DISTINCT k.kpi_code)', 'uniqueKpis')
      .addSelect('COUNT(*)', 'totalSnapshots')
      .where('k.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .groupBy('k.category');

    const categoryStats = await qb.getRawMany();

    const onTarget = await this.repository.count({
      where: {
        tenantId: ctx.tenantId,
        isOnTarget: true,
      } as any,
    });

    const offTarget = await this.repository.count({
      where: {
        tenantId: ctx.tenantId,
        isOnTarget: false,
      } as any,
    });

    return {
      byCategory: categoryStats.map((s: any) => ({
        category: s.category as KpiCategory,
        uniqueKpis: parseInt(s.uniqueKpis),
        totalSnapshots: parseInt(s.totalSnapshots),
      })),
      onTargetCount: onTarget,
      offTargetCount: offTarget,
      healthPercentage: onTarget + offTarget > 0 ? (onTarget / (onTarget + offTarget)) * 100 : 0,
    };
  }
}

export interface KpiStats {
  byCategory: {
    category: KpiCategory;
    uniqueKpis: number;
    totalSnapshots: number;
  }[];
  onTargetCount: number;
  offTargetCount: number;
  healthPercentage: number;
}
