import type { NextRequest } from "next/server"
import { NextResponse } from "next/server"

import { assertAdminSession } from "@/lib/auth-server"
import { DEFAULT_TENANT_ID } from "@/lib/constants"
import { prisma } from "@/lib/prisma"
import {
  computeNextRunAt,
  sanitizeRecipients,
  sanitizeReportKeys,
  serializeSchedule,
} from "@/server/report-schedule-service"

export const runtime = "nodejs"

type Ctx = RouteContext<"/api/reports/schedules/[id]">

export async function PATCH(request: NextRequest, ctx: Ctx) {
  const { id } = await ctx.params
  const session = await assertAdminSession()
  if (!session) {
    return NextResponse.json({ error: "Não autorizado" }, { status: 401 })
  }
  const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID
  const schedule = await prisma.reportExportSchedule.findFirst({
    where: { id, tenantId },
  })
  if (!schedule) {
    return NextResponse.json({ error: "Agendamento não encontrado" }, { status: 404 })
  }

  const payload = (await request.json().catch(() => null)) as Record<string, unknown> | null
  if (!payload) {
    return NextResponse.json({ error: "Payload inválido" }, { status: 400 })
  }

  const data: Record<string, unknown> = {}
  if (typeof payload.name === "string" && payload.name.trim().length > 0) {
    data.name = payload.name.trim()
  }

  if (Array.isArray(payload.reportKeys)) {
    const keys = sanitizeReportKeys(payload.reportKeys as string[])
    if (!keys.length) {
      return NextResponse.json({ error: "Selecione ao menos um relatório." }, { status: 400 })
    }
    data.reportKeys = keys
  }

  if (typeof payload.range === "string") {
    data.range = payload.range
  }
  if (payload.companyId === null || typeof payload.companyId === "string") {
    data.companyId = payload.companyId ?? null
  }
  if (payload.companyName === null || typeof payload.companyName === "string") {
    data.companyName = payload.companyName ?? null
  }
  if (typeof payload.format === "string") {
    data.format = payload.format
  }

  if (payload.recipients === null || Array.isArray(payload.recipients)) {
    const recipients = sanitizeRecipients((payload.recipients as string[] | null) ?? [])
    if (!recipients.length) {
      return NextResponse.json({ error: "Informe ao menos um destinatário válido." }, { status: 400 })
    }
    data.recipients = recipients
  }

  let needsReschedule = false
  if (typeof payload.frequency === "string") {
    const frequency = payload.frequency.toLowerCase()
    if (!["daily", "weekly", "monthly"].includes(frequency)) {
      return NextResponse.json({ error: "Frequência inválida." }, { status: 400 })
    }
    data.frequency = frequency
    needsReschedule = true
  }
  if (payload.dayOfWeek === null || typeof payload.dayOfWeek === "number") {
    data.dayOfWeek = payload.dayOfWeek ?? null
    needsReschedule = true
  }
  if (payload.dayOfMonth === null || typeof payload.dayOfMonth === "number") {
    data.dayOfMonth = payload.dayOfMonth ?? null
    needsReschedule = true
  }
  if (typeof payload.time === "string") {
    const { hour, minute } = parseTime(payload.time)
    data.hour = hour
    data.minute = minute
    needsReschedule = true
  }
  if (typeof payload.timezone === "string") {
    data.timezone = payload.timezone
    needsReschedule = true
  }
  if (typeof payload.status === "string") {
    data.status = payload.status.toUpperCase() === "PAUSED" ? "PAUSED" : "ACTIVE"
    if (data.status === "ACTIVE") {
      needsReschedule = true
    }
  }

  if (needsReschedule) {
    const cronSource = {
      frequency: (data.frequency as string) ?? schedule.frequency,
      dayOfWeek: (data.dayOfWeek as number | undefined | null) ?? schedule.dayOfWeek,
      dayOfMonth: (data.dayOfMonth as number | undefined | null) ?? schedule.dayOfMonth,
      hour: (data.hour as number | undefined) ?? schedule.hour,
      minute: (data.minute as number | undefined) ?? schedule.minute,
      timezone: (data.timezone as string | undefined) ?? schedule.timezone,
    }
    data.nextRunAt =
      data.status === "PAUSED"
        ? null
        : computeNextRunAt(
            {
              frequency: cronSource.frequency as "daily" | "weekly" | "monthly",
              dayOfWeek: cronSource.dayOfWeek,
              dayOfMonth: cronSource.dayOfMonth,
              hour: cronSource.hour,
              minute: cronSource.minute,
              timezone: cronSource.timezone,
            },
            new Date()
          )
  }

  const updated = await prisma.reportExportSchedule.update({
    where: { id: schedule.id },
    data,
  })

  return NextResponse.json({
    item: serializeSchedule(
      {
        ...updated,
        reportKeys: sanitizeReportKeys((updated.reportKeys as string[] | null) ?? []),
        recipients: (updated.recipients as string[] | null) ?? [],
      },
      []
    ),
  })
}

export async function DELETE(_: NextRequest, ctx: Ctx) {
  const { id } = await ctx.params
  const session = await assertAdminSession()
  if (!session) {
    return NextResponse.json({ error: "Não autorizado" }, { status: 401 })
  }
  const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID
  await prisma.reportExportRun.deleteMany({ where: { scheduleId: id, tenantId } })
  await prisma.reportExportSchedule.deleteMany({ where: { id, tenantId } })
  return NextResponse.json({ success: true })
}

function parseTime(value: string) {
  const match = value.match(/^(\d{1,2}):(\d{1,2})$/)
  if (!match) return { hour: 8, minute: 0 }
  const hour = Math.min(23, Math.max(0, Number(match[1])))
  const minute = Math.min(59, Math.max(0, Number(match[2])))
  return { hour, minute }
}
