/**
 * PDF Generation API
 *
 * Endpunkt für PDF-Generierung über die Queue oder direkt.
 * Unterstützt HTML-zu-PDF und URL-zu-PDF.
 */

import { getPayload } from 'payload'
import config from '@payload-config'
import { NextRequest, NextResponse } from 'next/server'
import { enqueuePdf, getPdfJobStatus, isQueueAvailable } from '@/lib/queue'
import { generatePdfFromHtml, generatePdfFromUrl } from '@/lib/pdf/pdf-service'
import { logAccessDenied } from '@/lib/audit/audit-service'
import {
  publicApiLimiter,
  rateLimitHeaders,
  createSafeLogger,
  runApiGuards,
  createApiErrorResponse,
} from '@/lib/security'
import {
  asObject,
  validateJsonBody,
  validationIssue,
  validationErrorResponse,
  type ApiValidationResult,
} from '@/lib/validation'

const RATE_LIMIT_MAX = 10
const logger = createSafeLogger('API:GeneratePdf')

interface UserWithTenants {
  id: number
  email?: string
  isSuperAdmin?: boolean
  tenants?: Array<{
    tenant: { id: number } | number
  }>
}

interface GeneratePdfBody {
  tenantId: number
  source: 'html' | 'url'
  html?: string
  url?: string
  options: Record<string, unknown>
  queued: boolean
  documentType?: 'invoice' | 'report' | 'export' | 'certificate' | 'other'
  filename?: string
  priority?: 'high' | 'normal' | 'low'
}

function validateGeneratePdfBody(input: unknown): ApiValidationResult<GeneratePdfBody> {
  const objectResult = asObject(input)
  if (!objectResult.valid) {
    return objectResult as ApiValidationResult<GeneratePdfBody>
  }

  const data = objectResult.data
  const issues = []

  const tenantIdValue = Number(data.tenantId)
  if (!Number.isFinite(tenantIdValue)) {
    issues.push(validationIssue('tenantId', 'invalid_type', 'tenantId must be a number'))
  }

  const source = data.source
  if (source !== 'html' && source !== 'url') {
    issues.push(validationIssue('source', 'invalid_value', 'source must be "html" or "url"'))
  }
  const sourceValue = source === 'html' || source === 'url' ? source : undefined

  const html = typeof data.html === 'string' ? data.html : undefined
  const url = typeof data.url === 'string' ? data.url : undefined
  if (source === 'html' && !html) {
    issues.push(validationIssue('html', 'required', 'html is required when source="html"'))
  }
  if (source === 'url' && !url) {
    issues.push(validationIssue('url', 'required', 'url is required when source="url"'))
  }

  const options = data.options && typeof data.options === 'object' && !Array.isArray(data.options)
    ? (data.options as Record<string, unknown>)
    : {}

  const queued = typeof data.queued === 'boolean' ? data.queued : true
  const documentType = ['invoice', 'report', 'export', 'certificate', 'other'].includes(
    String(data.documentType),
  )
    ? (data.documentType as 'invoice' | 'report' | 'export' | 'certificate' | 'other')
    : undefined
  const filename = typeof data.filename === 'string' ? data.filename : undefined
  const priority = ['high', 'normal', 'low'].includes(String(data.priority))
    ? (data.priority as 'high' | 'normal' | 'low')
    : undefined

  if (issues.length > 0) {
    return {
      valid: false,
      issues,
    }
  }

  return {
    valid: true,
    data: {
      tenantId: tenantIdValue,
      source: sourceValue as 'html' | 'url',
      html,
      url,
      options,
      queued,
      documentType,
      filename,
      priority,
    },
  }
}

/**
 * Prüft ob User Zugriff auf den angegebenen Tenant hat
 */
function userHasAccessToTenant(user: UserWithTenants, tenantId: number): boolean {
  if (user.isSuperAdmin) {
    return true
  }

  if (!user.tenants || user.tenants.length === 0) {
    return false
  }

  return user.tenants.some((t) => {
    const userTenantId = typeof t.tenant === 'object' ? t.tenant.id : t.tenant
    return userTenantId === tenantId
  })
}

/**
 * POST /api/generate-pdf
 *
 * Generiert ein PDF aus HTML oder URL.
 *
 * Body:
 * - tenantId: number (erforderlich)
 * - source: 'html' | 'url' (erforderlich)
 * - html?: string (wenn source='html')
 * - url?: string (wenn source='url')
 * - options?: { format, landscape, margin, printBackground, scale }
 * - queued?: boolean (true = async via Queue, false = sync)
 * - documentType?: string (invoice, report, export, etc.)
 * - filename?: string
 */
export async function POST(req: NextRequest) {
  try {
    const payload = await getPayload({ config })
    const guardResult = await runApiGuards(req, {
      endpoint: '/api/generate-pdf',
      ipEndpoint: 'generatePdf',
      csrf: 'browser',
      authProvider: payload,
      requireUser: true,
      rateLimiter: publicApiLimiter,
      rateLimitMax: RATE_LIMIT_MAX,
      rateLimitIdentifier: ({ user, ip }) =>
        typeof user === 'object' && user && 'id' in user
          ? String((user as { id: unknown }).id)
          : ip,
    })
    if (!guardResult.ok) {
      return guardResult.response
    }

    const typedUser = guardResult.user as UserWithTenants

    const bodyResult = await validateJsonBody(req, validateGeneratePdfBody)
    if (!bodyResult.valid) {
      return validationErrorResponse(bodyResult.issues)
    }

    const {
      tenantId,
      source,
      html,
      url,
      options,
      queued,
      documentType,
      filename,
      priority,
    } = bodyResult.data

    // Zugriffskontrolle
    if (!userHasAccessToTenant(typedUser, tenantId)) {
      await logAccessDenied(
        payload,
        `/api/generate-pdf (tenantId: ${tenantId})`,
        typedUser.id,
        typedUser.email as string,
      )

      return createApiErrorResponse(
        403,
        'FORBIDDEN',
        'You do not have access to this tenant',
      )
    }

    const rlHeaders = guardResult.rateLimit
      ? rateLimitHeaders(guardResult.rateLimit, RATE_LIMIT_MAX)
      : undefined

    // Queued PDF Generation (async)
    if (queued) {
      const queueAvailable = await isQueueAvailable()
      if (!queueAvailable) {
        return NextResponse.json(
          { error: 'Queue system unavailable. Use queued=false for direct generation.' },
          { status: 503, headers: rlHeaders },
        )
      }

      const job = await enqueuePdf({
        tenantId,
        source,
        html,
        url,
        options,
        documentType,
        filename,
        triggeredBy: String(typedUser.id),
        priority: priority || 'normal',
        correlationId: `pdf-${Date.now()}-${typedUser.id}`,
      })

      return NextResponse.json(
        {
          success: true,
          queued: true,
          message: 'PDF generation job queued successfully',
          jobId: job.id,
        },
        { headers: rlHeaders },
      )
    }

    // Direct PDF Generation (sync)
    let result
    if (source === 'html') {
      result = await generatePdfFromHtml(html || '', options)
    } else {
      result = await generatePdfFromUrl(url || '', options)
    }

    if (!result.success) {
      return NextResponse.json(
        { success: false, error: result.error },
        { status: 500, headers: rlHeaders },
      )
    }

    // PDF als Base64 zurückgeben
    return NextResponse.json(
      {
        success: true,
        queued: false,
        pdf: result.buffer?.toString('base64'),
        filename: filename || `document-${Date.now()}.pdf`,
        pageCount: result.pageCount,
        fileSize: result.fileSize,
        duration: result.duration,
      },
      { headers: rlHeaders },
    )
  } catch (error) {
    logger.error('generate-pdf error', error)
    return NextResponse.json(
      { error: error instanceof Error ? error.message : 'Unknown error' },
      { status: 500 },
    )
  }
}

/**
 * GET /api/generate-pdf?jobId=...
 *
 * Gibt den Status/das Ergebnis eines PDF-Jobs zurück.
 */
export async function GET(req: NextRequest) {
  try {
    const { searchParams } = new URL(req.url)
    const jobId = searchParams.get('jobId')

    // Wenn keine jobId, zeige API-Dokumentation
    if (!jobId) {
      return NextResponse.json({
        endpoint: '/api/generate-pdf',
        methods: {
          POST: {
            description: 'Generate a PDF from HTML or URL',
            authentication: 'Required',
            body: {
              tenantId: 'number (required)',
              source: '"html" | "url" (required)',
              html: 'string (required if source="html")',
              url: 'string (required if source="url")',
              options: {
                format: '"A4" | "A3" | "Letter" | "Legal"',
                landscape: 'boolean',
                margin: '{ top, right, bottom, left }',
                printBackground: 'boolean',
                scale: 'number',
              },
              queued: 'boolean (default: true)',
              documentType: 'string (invoice, report, export, etc.)',
              filename: 'string',
              priority: '"high" | "normal" | "low"',
            },
          },
          GET: {
            description: 'Get status/result of a PDF generation job',
            query: {
              jobId: 'string (required)',
            },
          },
        },
        examples: {
          generateFromHtml: {
            tenantId: 1,
            source: 'html',
            html: '<h1>Hello</h1><p>World</p>',
            options: { format: 'A4' },
            queued: true,
          },
          generateFromUrl: {
            tenantId: 1,
            source: 'url',
            url: 'https://example.com/invoice/123',
            queued: false,
          },
        },
      })
    }

    const payload = await getPayload({ config })
    const guardResult = await runApiGuards(req, {
      endpoint: '/api/generate-pdf',
      csrf: 'none',
      authProvider: payload,
      requireUser: true,
    })
    if (!guardResult.ok) {
      return guardResult.response
    }

    // Job-Status abrufen
    const status = await getPdfJobStatus(jobId)

    if (!status) {
      return NextResponse.json({ error: 'Job not found' }, { status: 404 })
    }

    // Wenn abgeschlossen, Ergebnis mitliefern
    if (status.state === 'completed' && status.result) {
      return NextResponse.json({
        jobId,
        state: status.state,
        result: status.result,
      })
    }

    return NextResponse.json({
      jobId,
      state: status.state,
      progress: status.progress,
      failedReason: status.failedReason,
    })
  } catch (error) {
    logger.error('generate-pdf status error', error)
    return NextResponse.json(
      { error: error instanceof Error ? error.message : 'Unknown error' },
      { status: 500 },
    )
  }
}
