import { NextRequest, NextResponse } from "next/server";
import { createClient } from "@/lib/supabase/server";
import { getAdapter } from "@/lib/adapters";
import { generatePrompt } from "@/lib/ai/provider";
import { checkRateLimit } from "@/lib/rate-limit";
import { resolveUserEnvVarsCached, hasAnyAiKey } from "@/lib/env-vars-resolver";
import { GenerateRequestSchema } from "@/lib/schemas/universal-prompt.schema";
import { calcEstimatedCost } from "@/lib/model-costs";
import { checkAndIncrementUsage } from "@/lib/usage-quota";
import { isCsrfSafe, csrfForbiddenPayload } from "@/lib/csrf";
import type { GeneratorOutput, PromptVariation } from "@/lib/schemas/universal-prompt.schema";
// Fix 3: Extend Vercel serverless timeout for AI generation (max 60s on Hobby, 300s on Pro)
export const maxDuration = 60;

const MAX_RETRIES = 2;

export async function POST(request: NextRequest) {
  try {
    // BUG-13 FIX: CSRF check — reject cross-site requests
    if (!isCsrfSafe(request)) {
      return NextResponse.json(csrfForbiddenPayload(), { status: 403 });
    }

    const supabase = createClient();
    const { data: { user }, error: authError } = await supabase.auth.getUser();
    if (authError || !user) {
      return NextResponse.json({ success: false, error: "Unauthorized" }, { status: 401 });
    }

    // Fix 1: Rate limit is now async (supports Upstash Redis in production)
    const rateLimit = await checkRateLimit(user.id);
    if (!rateLimit.allowed) {
      const retryAfterSec = Math.ceil((rateLimit.resetAt - Date.now()) / 1000);
      return NextResponse.json(
        { success: false, error: "Rate limit exceeded. Coba lagi dalam 1 menit." },
        {
          status: 429,
          headers: {
            "X-RateLimit-Remaining": "0",
            // Fix: Retry-After header agar client tahu kapan bisa coba lagi
            "Retry-After": String(retryAfterSec > 0 ? retryAfterSec : 60),
          },
        }
      );
    }

    // Resolve API keys (cached) — parallel dengan quota check nanti
    const resolvedVars = await resolveUserEnvVarsCached(supabase, user.id);

    // Cek API key tersedia
    if (!hasAnyAiKey(resolvedVars)) {
      return NextResponse.json(
        {
          success: false,
          error: "Belum ada AI API key. Tambahkan di Settings → API Keys.",
          redirectTo: "/settings",
        },
        { status: 402 }
      );
    }

    // ── Validasi request body dulu SEBELUM quota di-increment ────────────────
    // BUG-05 FIX: Quota tidak boleh di-increment untuk request yang invalid.
    // Urutan yang benar: parse body → validasi Zod → validasi adapter → baru increment quota.
    const body = await request.json();
    const parsed = GenerateRequestSchema.safeParse(body);
    if (!parsed.success) {
      return NextResponse.json(
        { success: false, error: "Request tidak valid", details: parsed.error.errors },
        { status: 400 }
      );
    }

    // Fix #1: Gunakan input.variations sebagai sumber kebenaran tunggal untuk jumlah variasi.
    // variationCount di request body dihapus; semua adapter pakai input.variations.
    const { platform, input } = parsed.data;
    const variationCount = input.variations;
    const adapter = getAdapter(platform);

    const validation = adapter.validateInput(input);
    if (!validation.success) {
      return NextResponse.json(
        { success: false, error: "Validasi gagal", details: validation.errors },
        { status: 400 }
      );
    }

    // ── Atomic quota check + pre-increment ───────────────────────────────────
    // Dilakukan SETELAH validasi agar quota tidak habis untuk request yang invalid.
    // RPC menggunakan SELECT FOR UPDATE sehingga hanya satu request yang lolos
    // ketika user berada tepat di batas kuota.
    const quotaResult = await checkAndIncrementUsage(supabase, user.id, 1);
    if (quotaResult === "quota_exceeded") {
      return NextResponse.json(
        { success: false, error: "Kuota habis. Upgrade plan untuk lebih banyak generasi." },
        { status: 403 }
      );
    }
    // "not_found" dan "error" → fail open (jangan block user karena DB error)

    // adapter is typed as AnyAdapter (input: unknown) so no intersection/never issue.
    // Runtime safety is guaranteed: getAdapter(platform) returns the correct adapter
    // and the Zod discriminated union schema ensures input matches the platform.
    const systemPrompt = adapter.constructSystemPrompt(input, variationCount);
    // Fix #2: variationIndex dihapus — constructUserPrompt tidak butuh index karena
    // satu AI call menghasilkan semua variasi sekaligus.
    const userPrompt   = adapter.constructUserPrompt(input);

    // Generate dengan retry, pakai resolvedVars dari DB/env
    let rawOutput = "";
    let model = "unknown";
    let provider = "unknown";
    let lastError: Error | null = null;
    let tokenUsage: { promptTokens: number; completionTokens: number; totalTokens: number } | undefined;

    for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
      try {
        const response = await generatePrompt(
          [
            { role: "system", content: systemPrompt },
            { role: "user", content: userPrompt },
          ],
          { temperature: 0.8, maxTokens: 6000 },
          resolvedVars
        );
        rawOutput    = response.content;
        model        = response.model;
        provider     = response.provider;
        tokenUsage   = response.usage;
        lastError    = null;
        break;
      } catch (e) {
        lastError = e as Error;
        if (attempt < MAX_RETRIES) {
          await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));
        }
      }
    }

    if (lastError) {
      return NextResponse.json(
        { success: false, error: lastError.message },
        { status: 500 }
      );
    }

    // Parse output — type-safe, no as-any
    let variations: PromptVariation[] = [];
    try {
      const parsedOutput = JSON.parse(rawOutput) as { variations?: PromptVariation[] };
      if (Array.isArray(parsedOutput.variations) && parsedOutput.variations.length > 0) {
        variations = parsedOutput.variations;
      } else {
        variations = [adapter.transformOutput(rawOutput, input)];
      }
    } catch {
      variations = [adapter.transformOutput(rawOutput, input)];
    }

    // Fix 9: Do NOT pad variations — returning fewer than requested is acceptable.
    // We only cap at variationCount to ensure we never return more than requested.
    const output: GeneratorOutput = {
      platform,
      variations: variations.slice(0, variationCount),
      metadata: {
        generatedAt: new Date().toISOString(),
        model: `${provider}/${model}`,
        inputLanguage: input.language,
      },
    };

    // Fix A: Simpan semua variasi sebagai JSONB array, bukan hanya variasi[0]
    const historyData = {
      user_id: user.id,
      platform,
      mode: input.mode,
      input_data: input as Record<string, unknown>,
      // Tetap simpan variasi[0] di kolom lama agar backward-compatible
      output_text: output.variations[0]?.textPrompt ?? null,
      output_json: output.variations[0]?.jsonOutput ?? null,
      output_shot_list: output.variations[0]?.shotList
        ? JSON.stringify(output.variations[0].shotList)
        : null,
      output_script: output.variations[0]?.script ?? null,
      // Fix A: Simpan SEMUA variasi (1-5) sebagai array JSON
      all_variations: output.variations,
      variations: variationCount,
      reference_image_url: input.referenceImageUrl ?? null,
    };

    // Estimasi biaya
    const estimatedCostUsd = calcEstimatedCost(provider, model, tokenUsage ?? null);

    // Simpan history — quota sudah di-increment atomic di atas (pre-generate)
    const [historyRes] = await Promise.all([
      supabase.from("prompt_history").insert(historyData).select("id").single(),
    ]);

    // Fix C: Simpan token usage & estimasi biaya ke usage_logs
    if (historyRes.data?.id && tokenUsage) {
      await supabase.from("usage_logs").insert({
        user_id: user.id,
        action: "generate_prompt",
        platform,
        metadata: {
          history_id:       historyRes.data.id,
          model,
          provider,
          variation_count:  output.variations.length,
        },
        prompt_tokens:      tokenUsage.promptTokens,
        completion_tokens:  tokenUsage.completionTokens,
        total_tokens:       tokenUsage.totalTokens,
        estimated_cost_usd: estimatedCostUsd,
      });
    }

    return NextResponse.json(
      { success: true, data: output, historyId: historyRes.data?.id ?? null },
      { headers: { "X-RateLimit-Remaining": String(rateLimit.remaining) } }
    );
  } catch (error) {
    console.error("[generate]", error);
    return NextResponse.json({ success: false, error: "Internal server error" }, { status: 500 });
  }
}
