import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';

@Injectable()
export class DashboardService {
  constructor(private readonly prisma: PrismaService) {}

  async summary() {
    const totalContacts = await this.prisma.contact.count();
    const totalWebhookEvents = await this.prisma.webhookEvent.count();
    const failedWebhookEvents = await this.prisma.webhookEvent.count({
      where: { processingStatus: 'failed' },
    });

    return {
      totalContacts,
      totalWebhookEvents,
      deliveredRate: 0,
      webhookHealth: failedWebhookEvents > 0 ? 'degraded' : 'healthy',
    };
  }

  async analyticsSummary() {
    const now = new Date();
    const last24Hours = new Date(now.getTime() - 24 * 60 * 60 * 1000);
    const lastHour = new Date(now.getTime() - 60 * 60 * 1000);

    const [
      totalJobs,
      pendingJobs,
      processingJobs,
      failedJobs24h,
      totalWebhookEvents,
      verifiedWebhookEvents,
      pendingWebhookEvents,
      auditTrailTotal,
      recentJobs,
      recentWebhookEvents,
    ] = await this.prisma.$transaction([
      this.prisma.job.count(),
      this.prisma.job.count({ where: { status: 'queued' } }),
      this.prisma.job.count({ where: { status: 'processing' } }),
      this.prisma.job.count({
        where: {
          status: 'failed',
          updatedAt: { gte: last24Hours },
        },
      }),
      this.prisma.webhookEvent.count(),
      this.prisma.webhookEvent.count({ where: { verified: true } }),
      this.prisma.webhookEvent.count({
        where: {
          processingStatus: { not: 'processed' },
        },
      }),
      this.prisma.auditLog.count(),
      this.prisma.job.findMany({
        where: {
          createdAt: { gte: lastHour },
        },
        select: {
          queueName: true,
          status: true,
          attempts: true,
          maxAttempts: true,
          createdAt: true,
          processedAt: true,
        },
      }),
      this.prisma.webhookEvent.findMany({
        where: {
          createdAt: { gte: lastHour },
        },
        select: {
          createdAt: true,
          verified: true,
          processingStatus: true,
        },
      }),
    ]);

    const workerGroups = recentJobs.reduce<Record<string, number>>((acc, job) => {
      const key = `${job.queueName}-worker`;
      acc[key] = (acc[key] || 0) + 1;
      return acc;
    }, {});

    const workerHealth = Object.entries(workerGroups).map(([name, count]) => {
      const load = Math.max(4, Math.min(100, count * 12));
      return {
        name,
        load,
        tone: load >= 70 ? 'success' : 'warning',
      };
    });

    const latencySamples = recentJobs
      .filter((job) => job.processedAt)
      .map((job) => job.processedAt!.getTime() - job.createdAt.getTime())
      .filter((value) => Number.isFinite(value) && value >= 0);

    const averageLatencyMs =
      latencySamples.length > 0
        ? Math.round(latencySamples.reduce((sum, value) => sum + value, 0) / latencySamples.length)
        : 120;

    const processedLastHour = recentJobs.filter((job) => job.status === 'processed').length;
    const webhookLastHour = recentWebhookEvents.length;
    const verifiedWebhookRate =
      totalWebhookEvents > 0 ? Math.round((verifiedWebhookEvents / totalWebhookEvents) * 100) : 0;
    const throughputPerMinute = Math.max(1, Math.round((processedLastHour + webhookLastHour) / 60));
    const averageAttempts =
      recentJobs.length > 0
        ? Math.round(
            recentJobs.reduce((sum, job) => sum + Math.min(job.attempts, job.maxAttempts), 0) /
              recentJobs.length,
          )
        : 0;

    const databaseConnectionsEstimate = Math.max(
      1,
      Math.min(200, recentJobs.length + recentWebhookEvents.length + Math.min(auditTrailTotal, 120)),
    );
    const memoryUsageGbEstimate = Number(
      (1.8 + (recentJobs.length + recentWebhookEvents.length + pendingWebhookEvents) * 0.07).toFixed(1),
    );

    const apiLatencyBars = [
      Math.max(4, Math.min(100, 32 + pendingJobs * 2)),
      Math.max(4, Math.min(100, 42 + processingJobs * 8)),
      Math.max(4, Math.min(100, 36 + failedJobs24h * 6)),
      Math.max(4, Math.min(100, 56 + pendingWebhookEvents * 8)),
      Math.max(4, Math.min(100, 28 + averageAttempts * 9)),
      Math.max(4, Math.min(100, 22 + processingJobs * 7)),
      Math.max(4, Math.min(100, 30 + verifiedWebhookRate)),
    ];

    const memoryBars = [
      Math.max(4, Math.min(100, 48 + pendingJobs * 3)),
      Math.max(4, Math.min(100, 54 + processingJobs * 5)),
      Math.max(4, Math.min(100, 58 + failedJobs24h * 4)),
      Math.max(4, Math.min(100, 50 + pendingWebhookEvents * 7)),
      Math.max(4, Math.min(100, 55 + verifiedWebhookRate)),
      Math.max(4, Math.min(100, 62 + processingJobs * 4)),
      Math.max(4, Math.min(100, 64 + averageAttempts * 5)),
    ];

    return {
      generatedAt: now.toISOString(),
      queue: {
        pendingJobs,
        processingJobs,
        failedJobs24h,
      },
      workers: workerHealth,
      throughput: {
        perMinute: throughputPerMinute,
        verifiedWebhookRate,
        jobsLastHour: recentJobs.length,
        webhooksLastHour: webhookLastHour,
      },
      metrics: {
        apiLatencyMs: averageLatencyMs,
        apiLatencyBars,
        databaseConnectionsEstimate,
        databaseUsagePercent: Math.max(4, Math.min(100, Math.round((databaseConnectionsEstimate / 200) * 100))),
        memoryUsageGbEstimate,
        memoryBars,
      },
      totals: {
        totalJobs,
        totalWebhookEvents,
        pendingWebhookEvents,
        auditTrailTotal,
      },
      health: {
        status: 'ok',
        database: 'ok',
      },
    };
  }
}
