import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from '../../../prisma/prisma.service';
import { CredentialsService } from '../credentials/credentials.service';
import { IntegrationType, SyncStatus } from '@prisma/client';

/**
 * Health status for a single integration
 */
export interface IntegrationHealth {
  id: string;
  type: IntegrationType;
  name: string;
  isActive: boolean;
  status: 'healthy' | 'degraded' | 'unhealthy' | 'unknown';
  syncStatus: SyncStatus;
  lastSync: Date | null;
  lastError: string | null;
  latency: number | null;
  message: string;
}

/**
 * Aggregated health status for all integrations
 */
export interface AggregatedHealth {
  overall: 'healthy' | 'degraded' | 'unhealthy';
  totalIntegrations: number;
  healthyCount: number;
  degradedCount: number;
  unhealthyCount: number;
  unknownCount: number;
  integrations: IntegrationHealth[];
  lastChecked: Date;
}

/**
 * Integration status summary per type
 */
export interface IntegrationTypeSummary {
  type: IntegrationType;
  count: number;
  activeCount: number;
  healthyCount: number;
  lastSync: Date | null;
  hasErrors: boolean;
}

@Injectable()
export class StatusService {
  private readonly logger = new Logger(StatusService.name);
  private healthCache: AggregatedHealth | null = null;
  private lastHealthCheck: Date | null = null;
  private readonly cacheDurationMs = 60000; // 1 minute cache

  constructor(
    private readonly prisma: PrismaService,
    private readonly credentialsService: CredentialsService,
  ) {}

  /**
   * Gets aggregated health status for all integrations
   */
  async getAggregatedHealth(forceRefresh: boolean = false): Promise<AggregatedHealth> {
    // Return cached result if available and not expired
    if (
      !forceRefresh &&
      this.healthCache &&
      this.lastHealthCheck &&
      Date.now() - this.lastHealthCheck.getTime() < this.cacheDurationMs
    ) {
      return this.healthCache;
    }

    const credentials = await this.prisma.integrationCredential.findMany({
      where: { isActive: true },
      orderBy: [{ type: 'asc' }, { name: 'asc' }],
    });

    const integrationHealths: IntegrationHealth[] = [];

    for (const credential of credentials) {
      const health = await this.checkIntegrationHealth(credential);
      integrationHealths.push(health);
    }

    const healthyCount = integrationHealths.filter(
      (h) => h.status === 'healthy',
    ).length;
    const degradedCount = integrationHealths.filter(
      (h) => h.status === 'degraded',
    ).length;
    const unhealthyCount = integrationHealths.filter(
      (h) => h.status === 'unhealthy',
    ).length;
    const unknownCount = integrationHealths.filter(
      (h) => h.status === 'unknown',
    ).length;

    let overall: 'healthy' | 'degraded' | 'unhealthy';
    if (unhealthyCount > 0) {
      overall = 'unhealthy';
    } else if (degradedCount > 0 || unknownCount > integrationHealths.length / 2) {
      overall = 'degraded';
    } else {
      overall = 'healthy';
    }

    this.healthCache = {
      overall,
      totalIntegrations: integrationHealths.length,
      healthyCount,
      degradedCount,
      unhealthyCount,
      unknownCount,
      integrations: integrationHealths,
      lastChecked: new Date(),
    };

    this.lastHealthCheck = new Date();

    return this.healthCache;
  }

  /**
   * Gets health status for a single integration
   */
  async getIntegrationHealth(credentialId: string): Promise<IntegrationHealth> {
    const credential = await this.prisma.integrationCredential.findUnique({
      where: { id: credentialId },
    });

    if (!credential) {
      throw new Error(`Credential ${credentialId} not found`);
    }

    return this.checkIntegrationHealth(credential);
  }

  /**
   * Gets summary by integration type
   */
  async getTypeSummary(): Promise<IntegrationTypeSummary[]> {
    const credentials = await this.prisma.integrationCredential.findMany({
      orderBy: { type: 'asc' },
    });

    const summaryMap = new Map<IntegrationType, IntegrationTypeSummary>();

    for (const credential of credentials) {
      const existing = summaryMap.get(credential.type);
      const isHealthy =
        credential.isActive &&
        credential.syncStatus === SyncStatus.SUCCESS &&
        !credential.syncError;

      if (existing) {
        existing.count++;
        if (credential.isActive) existing.activeCount++;
        if (isHealthy) existing.healthyCount++;
        if (credential.lastSync && (!existing.lastSync || credential.lastSync > existing.lastSync)) {
          existing.lastSync = credential.lastSync;
        }
        if (credential.syncError) existing.hasErrors = true;
      } else {
        summaryMap.set(credential.type, {
          type: credential.type,
          count: 1,
          activeCount: credential.isActive ? 1 : 0,
          healthyCount: isHealthy ? 1 : 0,
          lastSync: credential.lastSync,
          hasErrors: !!credential.syncError,
        });
      }
    }

    return Array.from(summaryMap.values());
  }

  /**
   * Gets recent errors across all integrations
   */
  async getRecentErrors(limit: number = 10): Promise<
    Array<{
      credentialId: string;
      type: IntegrationType;
      name: string;
      error: string;
      occurredAt: Date;
    }>
  > {
    const recentFailures = await this.prisma.integrationSyncHistory.findMany({
      where: {
        status: SyncStatus.ERROR,
        error: { not: null },
      },
      include: {
        credential: {
          select: {
            type: true,
            name: true,
          },
        },
      },
      orderBy: { completedAt: 'desc' },
      take: limit,
    });

    return recentFailures.map((f) => ({
      credentialId: f.credentialId,
      type: f.credential.type,
      name: f.credential.name,
      error: f.error!,
      occurredAt: f.completedAt || f.startedAt,
    }));
  }

  /**
   * Gets sync statistics for a time period
   */
  async getSyncStatistics(
    fromDate?: Date,
    toDate?: Date,
  ): Promise<{
    totalSyncs: number;
    successfulSyncs: number;
    failedSyncs: number;
    successRate: number;
    averageDuration: number;
    totalRecordsProcessed: number;
    byType: Record<string, { success: number; failed: number }>;
  }> {
    const from = fromDate || new Date(Date.now() - 24 * 60 * 60 * 1000); // Last 24 hours
    const to = toDate || new Date();

    const history = await this.prisma.integrationSyncHistory.findMany({
      where: {
        startedAt: {
          gte: from,
          lte: to,
        },
        status: { in: [SyncStatus.SUCCESS, SyncStatus.ERROR] },
      },
      include: {
        credential: {
          select: { type: true },
        },
      },
    });

    const successfulSyncs = history.filter(
      (h) => h.status === SyncStatus.SUCCESS,
    ).length;
    const failedSyncs = history.filter((h) => h.status === SyncStatus.ERROR).length;
    const totalSyncs = history.length;

    const durations = history
      .filter((h) => h.duration !== null)
      .map((h) => h.duration!);
    const averageDuration =
      durations.length > 0
        ? durations.reduce((a, b) => a + b, 0) / durations.length
        : 0;

    const totalRecordsProcessed = history
      .filter((h) => h.recordsProcessed !== null)
      .reduce((sum, h) => sum + h.recordsProcessed!, 0);

    // Group by type
    const byType: Record<string, { success: number; failed: number }> = {};
    for (const h of history) {
      const type = h.credential.type;
      if (!byType[type]) {
        byType[type] = { success: 0, failed: 0 };
      }
      if (h.status === SyncStatus.SUCCESS) {
        byType[type].success++;
      } else {
        byType[type].failed++;
      }
    }

    return {
      totalSyncs,
      successfulSyncs,
      failedSyncs,
      successRate: totalSyncs > 0 ? (successfulSyncs / totalSyncs) * 100 : 0,
      averageDuration: Math.round(averageDuration),
      totalRecordsProcessed,
      byType,
    };
  }

  /**
   * Checks health for a single credential
   */
  private async checkIntegrationHealth(credential: any): Promise<IntegrationHealth> {
    let status: 'healthy' | 'degraded' | 'unhealthy' | 'unknown';
    let message: string;
    let latency: number | null = null;

    if (!credential.isActive) {
      status = 'unknown';
      message = 'Integration is deactivated';
    } else if (credential.syncStatus === SyncStatus.ERROR) {
      status = 'unhealthy';
      message = credential.syncError || 'Sync failed';
    } else if (credential.syncStatus === SyncStatus.SUCCESS) {
      // Check if last sync is recent enough
      const syncAge = credential.lastSync
        ? Date.now() - new Date(credential.lastSync).getTime()
        : null;

      const maxSyncAge = this.getMaxSyncAge(credential.type);

      if (!syncAge) {
        status = 'unknown';
        message = 'Never synced';
      } else if (syncAge > maxSyncAge * 2) {
        status = 'degraded';
        message = 'Sync is overdue';
      } else if (syncAge > maxSyncAge) {
        status = 'degraded';
        message = 'Sync approaching due time';
      } else {
        status = 'healthy';
        message = 'Operating normally';
      }

      // Get average latency from recent syncs
      const recentSyncs = await this.prisma.integrationSyncHistory.findMany({
        where: {
          credentialId: credential.id,
          status: SyncStatus.SUCCESS,
          duration: { not: null },
        },
        orderBy: { completedAt: 'desc' },
        take: 5,
      });

      if (recentSyncs.length > 0) {
        const durations = recentSyncs.map((s) => s.duration!);
        latency = Math.round(
          durations.reduce((a, b) => a + b, 0) / durations.length,
        );
      }
    } else if (credential.syncStatus === SyncStatus.RUNNING) {
      status = 'healthy';
      message = 'Sync in progress';
    } else {
      status = 'unknown';
      message = 'Pending initial sync';
    }

    return {
      id: credential.id,
      type: credential.type,
      name: credential.name,
      isActive: credential.isActive,
      status,
      syncStatus: credential.syncStatus,
      lastSync: credential.lastSync,
      lastError: credential.syncError,
      latency,
      message,
    };
  }

  /**
   * Gets the maximum expected sync age for an integration type
   */
  private getMaxSyncAge(type: IntegrationType): number {
    // Returns max age in milliseconds based on expected sync interval
    const intervals: Record<string, number> = {
      PLENTYONE: 15 * 60 * 1000, // 15 minutes
      ZULIP: 5 * 60 * 1000, // 5 minutes
      TODOIST: 10 * 60 * 1000, // 10 minutes
      FREESCOUT: 10 * 60 * 1000, // 10 minutes
      NEXTCLOUD: 30 * 60 * 1000, // 30 minutes
      ECODMS: 60 * 60 * 1000, // 60 minutes
      GEMBADOCS: 30 * 60 * 1000, // 30 minutes
    };

    return intervals[type] || 60 * 60 * 1000; // Default: 1 hour
  }
}
