import { eq, and, gte, lte, desc } from "drizzle-orm";
import { scheduledReports, kpiSnapshots } from "../../db/schema";
import type { DrizzleDB } from "../../db/config/migrations";
import type { NewScheduledReport, ScheduledReport } from "../../db/schema";
import { getAllLatestKPIs, getKPIHistory, getKPIStatus, type KPIKey } from "./kpi-service";

export interface ReportData {
  periodStart: Date;
  periodEnd: Date;
  kpis: Record<string, { value: number; status: "healthy" | "warning" | "critical"; change: number }>;
  alerts: string[];
  summary: string;
}

export async function generateWeeklyReport(db: DrizzleDB): Promise<ReportData> {
  const now = new Date();
  const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);

  const kpis = await getAllLatestKPIs(db);
  const kpiData: ReportData["kpis"] = {};

  for (const [key, snapshot] of Object.entries(kpis)) {
    if (!snapshot) {
      kpiData[key] = { value: 0, status: "warning", change: 0 };
      continue;
    }

    const history = await getKPIHistory(db, key as KPIKey, weekAgo, now);
    const previousValue = history.length > 1 ? history[history.length - 2]?.kpiValue ?? snapshot.kpiValue : snapshot.kpiValue;
    const change = previousValue !== 0 ? ((snapshot.kpiValue - previousValue) / previousValue) * 100 : 0;
    const status = getKPIStatus(key as KPIKey, snapshot.kpiValue);

    kpiData[key] = { value: snapshot.kpiValue, status, change };
  }

  const alertMessages = Object.entries(kpiData)
    .filter(([, data]) => data.status !== "healthy")
    .map(([key, data]) => `⚠️ ${key}: ${data.value.toFixed(2)} (${data.status})`);

  const healthyCount = Object.values(kpiData).filter((d) => d.status === "healthy").length;
  const totalKPIs = Object.keys(kpiData).length;

  const summary = `Weekly Report (${weekAgo.toISOString().split("T")[0]} - ${now.toISOString().split("T")[0]})\n${healthyCount}/${totalKPIs} KPIs healthy. ${alertMessages.length > 0 ? "Alerts: " + alertMessages.join(", ") : "No alerts."}`;

  return {
    periodStart: weekAgo,
    periodEnd: now,
    kpis: kpiData,
    alerts: alertMessages,
    summary,
  };
}

export async function generateMonthlyReport(db: DrizzleDB): Promise<ReportData> {
  const now = new Date();
  const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);

  const kpis = await getAllLatestKPIs(db);
  const kpiData: ReportData["kpis"] = {};

  for (const [key, snapshot] of Object.entries(kpis)) {
    if (!snapshot) {
      kpiData[key] = { value: 0, status: "warning", change: 0 };
      continue;
    }

    const history = await getKPIHistory(db, key as KPIKey, monthAgo, now);
    const previousValue = history.length > 1 ? history[history.length - 2]?.kpiValue ?? snapshot.kpiValue : snapshot.kpiValue;
    const change = previousValue !== 0 ? ((snapshot.kpiValue - previousValue) / previousValue) * 100 : 0;
    const status = getKPIStatus(key as KPIKey, snapshot.kpiValue);

    kpiData[key] = { value: snapshot.kpiValue, status, change };
  }

  const alertMessages = Object.entries(kpiData)
    .filter(([, data]) => data.status !== "healthy")
    .map(([key, data]) => `⚠️ ${key}: ${data.value.toFixed(2)} (${data.status})`);

  const healthyCount = Object.values(kpiData).filter((d) => d.status === "healthy").length;
  const totalKPIs = Object.keys(kpiData).length;

  const summary = `Monthly Report (${monthAgo.toISOString().split("T")[0]} - ${now.toISOString().split("T")[0]})\n${healthyCount}/${totalKPIs} KPIs healthy.`;

  return {
    periodStart: monthAgo,
    periodEnd: now,
    kpis: kpiData,
    alerts: alertMessages,
    summary,
  };
}

export async function formatReportMarkdown(report: ReportData): Promise<string> {
  const lines: string[] = [];
  lines.push(`# KPI Report`);
  lines.push(``);
  lines.push(`**Period:** ${report.periodStart.toISOString().split("T")[0]} → ${report.periodEnd.toISOString().split("T")[0]}`);
  lines.push(``);

  lines.push(`## Summary`);
  lines.push(``);
  lines.push(report.summary);
  lines.push(``);

  lines.push(`## KPI Details`);
  lines.push(``);
  lines.push(`| KPI | Value | Status | Change |`);
  lines.push(`|-----|-------|--------|--------|`);

  for (const [key, data] of Object.entries(report.kpis)) {
    const statusIcon = data.status === "healthy" ? "✅" : data.status === "warning" ? "⚠️" : "🔴";
    const changeStr = data.change >= 0 ? `+${data.change.toFixed(1)}%` : `${data.change.toFixed(1)}%`;
    lines.push(`| ${key} | ${data.value.toFixed(2)} | ${statusIcon} ${data.status} | ${changeStr} |`);
  }

  if (report.alerts.length > 0) {
    lines.push(``);
    lines.push(`## Alerts`);
    lines.push(``);
    for (const alert of report.alerts) {
      lines.push(`- ${alert}`);
    }
  }

  return lines.join("\n");
}

export async function formatReportSlackBlocks(report: ReportData): Promise<unknown[]> {
  const blocks: unknown[] = [
    {
      type: "header",
      text: {
        type: "plain_text",
        text: `📊 KPI Report`,
        emoji: true,
      },
    },
    {
      type: "section",
      text: {
        type: "mrkdwn",
        text: `*Period:* ${report.periodStart.toISOString().split("T")[0]} → ${report.periodEnd.toISOString().split("T")[0]}`,
      },
    },
  ];

  for (const [key, data] of Object.entries(report.kpis)) {
    const statusIcon = data.status === "healthy" ? "✅" : data.status === "warning" ? "⚠️" : "🔴";
    const changeStr = data.change >= 0 ? `+${data.change.toFixed(1)}%` : `${data.change.toFixed(1)}%`;
    blocks.push({
      type: "section",
      fields: [
        {
          type: "mrkdwn",
          text: `*${key}:*\n${data.value.toFixed(2)}`,
        },
        {
          type: "mrkdwn",
          text: `*Status:*\n${statusIcon} ${data.status}`,
        },
        {
          type: "mrkdwn",
          text: `*Change:*\n${changeStr}`,
        },
      ],
    });
  }

  return blocks;
}

export async function createScheduledReport(
  db: DrizzleDB,
  input: Omit<NewScheduledReport, "lastRunAt" | "nextRunAt">
): Promise<ScheduledReport> {
  const result = await db.insert(scheduledReports).values({
    ...input,
    lastRunAt: null,
    nextRunAt: computeNextRun(input.schedule),
  }).returning();
  return result[0]!;
}

export async function getActiveScheduledReports(
  db: DrizzleDB
): Promise<ScheduledReport[]> {
  return await db
    .select()
    .from(scheduledReports)
    .where(eq(scheduledReports.isActive, true))
    .orderBy(scheduledReports.nextRunAt);
}

export async function runDueReports(db: DrizzleDB): Promise<ScheduledReport[]> {
  const now = new Date();
  const dueReports = await db
    .select()
    .from(scheduledReports)
    .where(and(
      eq(scheduledReports.isActive, true),
      lte(scheduledReports.nextRunAt, now)
    ));

  const runResults: ScheduledReport[] = [];

  for (const report of dueReports) {
    let reportData: ReportData;

    switch (report.reportType) {
      case "weekly_kpi":
        reportData = await generateWeeklyReport(db);
        break;
      case "monthly_kpi":
        reportData = await generateMonthlyReport(db);
        break;
      default:
        reportData = await generateWeeklyReport(db);
    }

    await db
      .update(scheduledReports)
      .set({
        lastRunAt: now,
        nextRunAt: computeNextRun(report.schedule),
      })
      .where(eq(scheduledReports.id, report.id));

    runResults.push(report);
  }

  return runResults;
}

function computeNextRun(schedule: string): Date {
  const now = new Date();

  switch (schedule) {
    case "weekly":
      const nextWeek = new Date(now);
      nextWeek.setDate(nextWeek.getDate() + 7);
      nextWeek.setHours(9, 0, 0, 0);
      return nextWeek;
    case "monthly":
      const nextMonth = new Date(now);
      nextMonth.setMonth(nextMonth.getMonth() + 1);
      nextMonth.setDate(1);
      nextMonth.setHours(9, 0, 0, 0);
      return nextMonth;
    case "daily":
      const tomorrow = new Date(now);
      tomorrow.setDate(tomorrow.getDate() + 1);
      tomorrow.setHours(9, 0, 0, 0);
      return tomorrow;
    default:
      return new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
  }
}
