"use server"

import type { Prisma as PrismaTypes } from "@/generated/prisma/client"
import { prisma } from "@/lib/prisma"
import { env } from "@/lib/env"
import { sendSmtpMail } from "@/server/email-smtp"
import {
  buildBacklogWorkbook,
  buildCsatWorkbook,
  buildHoursWorkbook,
  buildSlaWorkbook,
  buildTicketsByChannelWorkbook,
  createConvexContext,
  type ConvexReportContext,
  type ReportArtifact,
  type ReportExportKey,
} from "@/server/report-exporters"
import {
  computeNextRunAt,
  sanitizeRecipients,
  sanitizeReportKeys,
} from "@/server/report-schedule-service"

const GENERATORS: Record<
  ReportExportKey,
  (
    ctx: ConvexReportContext,
    options: { range?: string; companyId?: string | null }
  ) => Promise<ReportArtifact>
> = {
  hours: buildHoursWorkbook,
  backlog: buildBacklogWorkbook,
  sla: buildSlaWorkbook,
  csat: buildCsatWorkbook,
  "tickets-by-channel": buildTicketsByChannelWorkbook,
}

type ScheduleRunnerIdentity = {
  userId?: string
  name?: string | null
  email?: string | null
  avatarUrl?: string | null
  role?: string | null
}

export type RunReportSchedulesOptions = {
  tenantId?: string
  scheduleId?: string
  initiatedBy?: ScheduleRunnerIdentity
  now?: Date
}

export type RunReportSchedulesResult = {
  processed: number
  results: Array<{ id: string; status: "completed" | "failed" | "skipped"; error?: string }>
  message?: string
}

export async function runReportSchedules(
  options: RunReportSchedulesOptions = {}
): Promise<RunReportSchedulesResult> {
  const now = options.now ?? new Date()

  const where: PrismaTypes.ReportExportScheduleWhereInput = {
    status: "ACTIVE",
  }
  if (options.tenantId) {
    where.tenantId = options.tenantId
  }
  if (options.scheduleId) {
    where.id = options.scheduleId
  } else {
    where.nextRunAt = { lte: now }
  }

  const schedules = await prisma.reportExportSchedule.findMany({
    where,
    orderBy: { nextRunAt: "asc" },
  })

  if (schedules.length === 0) {
    return {
      processed: 0,
      results: [],
      message: options.scheduleId
        ? "Agendamento não encontrado ou inativo."
        : "Nenhum agendamento pendente.",
    }
  }

  const fallbackEmail =
    options.initiatedBy?.email ??
    env.SMTP?.from ??
    process.env.REPORTS_CRON_FALLBACK_EMAIL ??
    "scheduler@sistema.local"

  const results: RunReportSchedulesResult["results"] = []

  for (const schedule of schedules) {
    const reportKeys = sanitizeReportKeys((schedule.reportKeys as string[] | null) ?? [])
    if (!reportKeys.length) {
      results.push({ id: schedule.id, status: "skipped", error: "Nenhum relatório configurado." })
      continue
    }

    const owner =
      (await prisma.user.findUnique({
        where: { id: schedule.createdBy },
        select: { name: true, email: true, avatarUrl: true, role: true },
      })) ?? null

    let context: ConvexReportContext | null = null
    try {
      context = await createConvexContext({
        tenantId: schedule.tenantId,
        name: owner?.name ?? options.initiatedBy?.name ?? options.initiatedBy?.email ?? fallbackEmail,
        email: owner?.email ?? fallbackEmail,
        avatarUrl: owner?.avatarUrl ?? options.initiatedBy?.avatarUrl ?? undefined,
        role: (owner?.role ?? options.initiatedBy?.role ?? "ADMIN").toUpperCase(),
      })
    } catch (error) {
      results.push({
        id: schedule.id,
        status: "failed",
        error: error instanceof Error ? error.message : "Falha ao inicializar contexto Convex",
      })
      continue
    }

    const run = await prisma.reportExportRun.create({
      data: {
        tenantId: schedule.tenantId,
        scheduleId: schedule.id,
        status: "RUNNING",
      },
    })

    try {
      const artifacts: Array<{ key: ReportExportKey; fileName: string; mimeType: string; data: string }> = []
      for (const key of reportKeys) {
        const generator = GENERATORS[key]
        if (!generator) continue
        const artifact = await generator(context, {
          range: schedule.range,
          companyId: schedule.companyId ?? undefined,
        })
        artifacts.push({
          key,
          fileName: artifact.fileName,
          mimeType: artifact.mimeType,
          data: Buffer.from(new Uint8Array(artifact.buffer)).toString("base64"),
        })
      }

      await prisma.reportExportRun.update({
        where: { id: run.id },
        data: {
          status: "COMPLETED",
          completedAt: new Date(),
          artifacts,
        },
      })

      await prisma.reportExportSchedule.update({
        where: { id: schedule.id },
        data: {
          lastRunAt: new Date(),
          nextRunAt: computeNextRunAt(
            {
              frequency: schedule.frequency as "daily" | "weekly" | "monthly",
              dayOfWeek: schedule.dayOfWeek,
              dayOfMonth: schedule.dayOfMonth,
              hour: schedule.hour,
              minute: schedule.minute,
              timezone: schedule.timezone,
            },
            now
          ),
        },
      })

      const recipients = sanitizeRecipients((schedule.recipients as string[] | null) ?? [])
      if (artifacts.length && recipients.length && env.SMTP) {
        await notifyRecipients(run.id, schedule.name, artifacts, recipients)
      }

      results.push({ id: schedule.id, status: "completed" })
    } catch (error) {
      await prisma.reportExportRun.update({
        where: { id: run.id },
        data: {
          status: "FAILED",
          completedAt: new Date(),
          error: error instanceof Error ? error.message : "Erro inesperado",
        },
      })
      results.push({
        id: schedule.id,
        status: "failed",
        error: error instanceof Error ? error.message : "Erro inesperado",
      })
    }
  }

  return {
    processed: results.length,
    results,
  }
}

async function notifyRecipients(
  runId: string,
  scheduleName: string,
  artifacts: Array<{ key: string; fileName: string; mimeType: string; data: string }>,
  recipients: string[]
) {
  if (!env.SMTP) return
  const baseUrl =
    env.REPORTS_CRON_BASE_URL ?? env.NEXT_PUBLIC_APP_URL ?? env.BETTER_AUTH_URL ?? "http://localhost:3000"

  const links = artifacts
    .map(
      (artifact, index) =>
        `${baseUrl.replace(/\/$/, "")}/api/reports/schedules/runs/${runId}?artifact=${index}`
    )
    .map(
      (href, index) =>
        `<li><a href="${href}">${artifacts[index].fileName}</a> (${artifacts[index].mimeType})</li>`
    )
    .join("")

  const html = `
    <p>Os relatórios agendados <strong>${scheduleName}</strong> foram gerados.</p>
    <p>Arquivos disponíveis:</p>
    <ul>${links}</ul>
    <p>Este e-mail foi enviado automaticamente.</p>
  `

  try {
    await sendSmtpMail(env.SMTP, recipients, `Relatórios agendados - ${scheduleName}`, html)
  } catch (error) {
    console.error("Falha ao enviar e-mail do agendamento", error)
  }
}
