import { NextRequest, NextResponse } from "next/server"
import type { Browser } from "playwright"

import { api } from "@/convex/_generated/api"
import type { Id } from "@/convex/_generated/dataModel"
import { DEFAULT_TENANT_ID } from "@/lib/constants"
import { assertAuthenticatedSession } from "@/lib/auth-server"
import { createConvexClient } from "@/server/convex-client"

export const runtime = "nodejs"

const WAIT_SELECTOR_DEFAULT = "[data-dashboard-ready='true']"
const DEFAULT_VIEWPORT_WIDTH = 1600
const DEFAULT_VIEWPORT_HEIGHT = 900

function slugifyFilename(raw: string, extension: "pdf" | "png") {
  const base = raw
    .normalize("NFKD")
    .replace(/[\u0300-\u036f]/g, "")
    .replace(/[^a-zA-Z0-9-_]+/g, "-")
    .replace(/-{2,}/g, "-")
    .replace(/^-+|-+$/g, "")
    .toLowerCase()
  const safe = base.length > 0 ? base : "dashboard"
  return `${safe}.${extension}`
}

type ExportFormat = "pdf" | "png"

function isSupportedFormat(value: string): value is ExportFormat {
  return value === "pdf" || value === "png"
}

function buildDisposition(filename: string) {
  const encoded = encodeURIComponent(filename)
  return `attachment; filename="${filename}"; filename*=UTF-8''${encoded}`
}

export async function GET(
  request: NextRequest,
  context: { params: Promise<{ id: string; format: string }> },
) {
  const { id, format } = await context.params
  const formatParam = format?.toLowerCase()
  if (!isSupportedFormat(formatParam)) {
    return NextResponse.json({ error: "Formato não suportado" }, { status: 400 })
  }

  const session = await assertAuthenticatedSession()
  if (!session) {
    return NextResponse.json({ error: "Não autorizado" }, { status: 401 })
  }

  const convex = createConvexClient()
  const tenantId = session.user.tenantId ?? DEFAULT_TENANT_ID

  let ensuredUser: { _id: Id<"users"> } | null = null
  try {
    ensuredUser = await convex.mutation(api.users.ensureUser, {
      tenantId,
      name: session.user.name ?? session.user.email,
      email: session.user.email,
      avatarUrl: session.user.avatarUrl ?? undefined,
      role: session.user.role.toUpperCase(),
    })
  } catch (error) {
    console.error("[dashboards.export] Falha ao garantir usuário no Convex", error)
    return NextResponse.json({ error: "Não foi possível preparar a exportação" }, { status: 500 })
  }

  const viewerId = ensuredUser?._id
  if (!viewerId) {
    return NextResponse.json({ error: "Usuário não encontrado no Convex" }, { status: 403 })
  }

  let detail: { dashboard: { name?: string | null; readySelector?: string | null } | null } | null = null
  try {
    detail = await convex.query(api.dashboards.get, {
      tenantId,
      viewerId,
      dashboardId: id as Id<"dashboards">,
    })
  } catch (error) {
    console.error("[dashboards.export] Falha ao obter dashboard", error, { tenantId, dashboardId: id })
    return NextResponse.json({ error: "Não foi possível carregar o dashboard" }, { status: 500 })
  }

  if (!detail?.dashboard) {
    return NextResponse.json({ error: "Dashboard não encontrado" }, { status: 404 })
  }

  const requestUrl = request.nextUrl
  const printUrl = new URL(`/dashboards/${id}/print`, requestUrl.origin).toString()
  const waitForSelector = detail.dashboard.readySelector ?? WAIT_SELECTOR_DEFAULT
  const width = Number(requestUrl.searchParams.get("width") ?? DEFAULT_VIEWPORT_WIDTH) || DEFAULT_VIEWPORT_WIDTH
  const height = Number(requestUrl.searchParams.get("height") ?? DEFAULT_VIEWPORT_HEIGHT) || DEFAULT_VIEWPORT_HEIGHT
  let browser: Browser | null = null

  try {
    const { chromium } = await import("playwright")
    try {
      browser = await chromium.launch({ headless: true })
    } catch (launchError) {
      const message = (launchError as Error)?.message ?? String(launchError)
      if (/Executable doesn't exist|Please run the following command to download new browsers/i.test(message)) {
        return NextResponse.json(
          {
            error: "Playwright browsers not installed",
            hint: "Execute: npx playwright install",
            details: message,
          },
          { status: 501 },
        )
      }
      throw launchError
    }

    const page = await browser.newPage({ viewport: { width, height } })
    await page.goto(printUrl, { waitUntil: "networkidle" })
    if (waitForSelector) {
      try {
        await page.waitForSelector(waitForSelector, { timeout: 15000 })
      } catch (error) {
        console.warn("[dashboards.export] Tempo excedido aguardando seletor", waitForSelector, error)
      }
    }

    await page.emulateMedia({ media: "screen" }).catch(() => undefined)

    if (formatParam === "pdf") {
      const scrollHeight = await page.evaluate(() =>
        Math.max(
          document.documentElement?.scrollHeight ?? 0,
          document.body?.scrollHeight ?? 0,
          window.innerHeight,
        ),
      )
      const pdfBuffer = await page.pdf({
        width: `${width}px`,
        height: `${Math.max(scrollHeight, height)}px`,
        printBackground: true,
        margin: {
          top: "12px",
          bottom: "12px",
          left: "18px",
          right: "18px",
        },
      })
      const bytes = new Uint8Array(pdfBuffer)
      const filename = slugifyFilename(detail.dashboard.name ?? "dashboard", "pdf")
      return new NextResponse(bytes, {
        status: 200,
        headers: {
          "Content-Type": "application/pdf",
          "Content-Disposition": buildDisposition(filename),
          "Cache-Control": "no-store",
        },
      })
    }

    const screenshot = await page.screenshot({ type: "png", fullPage: true })
    const bytes = new Uint8Array(screenshot)
    const filename = slugifyFilename(detail.dashboard.name ?? "dashboard", "png")
    return new NextResponse(bytes, {
      status: 200,
      headers: {
        "Content-Type": "image/png",
        "Content-Disposition": buildDisposition(filename),
        "Cache-Control": "no-store",
      },
    })
  } catch (error) {
    console.error("[dashboards.export] Falha ao exportar dashboard", error)
    return NextResponse.json({ error: "Falha ao exportar o dashboard" }, { status: 500 })
  } finally {
    if (browser) {
      await browser.close().catch(() => undefined)
    }
  }
}
