import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";
import { requireSupabaseAuth } from "@/integrations/supabase/auth-middleware";
import type { PhotoshootInput, PhotoshootPlan } from "./photoshoot-data";
import { PhotoshootPlanSchema } from "./ai-schemas";

const InputSchema = z.object({
  title: z.string().min(1).max(200),
  eventType: z.string().min(1).max(80),
  date: z.string().max(40),
  location: z.string().max(200),
  emotion: z.string().min(1).max(80),
  personality: z.string().min(1).max(80),
  style: z.string().min(1).max(80),
  extraPromptIds: z.array(z.string().uuid()).max(20).optional(),
  extraPromptTexts: z.array(z.string().max(8000)).max(20).optional(),
});

const FALLBACK_SYSTEM = `You are the Personal Photoshoot Studio™ creative director. You design emotionally intentional photoshoots that tell stories.
Always respond with ONLY valid JSON (no markdown, no prose) matching the schema requested. Be vivid, specific, and luxury-editorial in tone.`;

export const generatePhotoshootPlan = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((d: unknown) => InputSchema.parse(d))
  .handler(async ({ data, context }): Promise<PhotoshootPlan> => {
    const key = process.env.LOVABLE_API_KEY;
    if (!key) throw new Error("AI service is not configured. Please contact support.");

    const { enforceRateLimit } = await import("./rate-limit.server");
    const { logAiRequest } = await import("./ai-monitoring.server");
    const { supabaseAdmin } = await import("@/integrations/supabase/client.server");

    // 1) Rate limit per user/plan
    await enforceRateLimit(context.supabase, context.userId, "photoshoot_plan");

    // 2) Resolve the active system prompt version (falls back to inline if none)
    const { data: activePrompt } = await supabaseAdmin
      .from("system_prompt_versions")
      .select("id, system_text")
      .eq("active", true)
      .maybeSingle();
    const systemText = activePrompt?.system_text ?? FALLBACK_SYSTEM;
    const systemVersionId = activePrompt?.id ?? null;

    const user = `Create a complete photoshoot plan.

INPUT:
- Title: ${data.title}
- Event: ${data.eventType}
- Date: ${data.date || "TBD"}
- Location context: ${data.location || "open"}
- Desired emotion: ${data.emotion}
- Personality: ${data.personality}
- Photography style: ${data.style}
${data.extraPromptTexts && data.extraPromptTexts.length > 0 ? `\nADDITIONAL CREATIVE DIRECTION (from curated prompt library — blend these influences into the plan, do not contradict the inputs above):\n${data.extraPromptTexts.map((p, i) => `(${i + 1}) ${p}`).join("\n")}\n` : ""}

Return JSON with EXACTLY these keys:
{
  "emotionalGoal": string,
  "tone": string,
  "visualTheme": string,
  "storyTitle": string (poetic, 3-7 words),
  "storyNarrative": string (2-4 sentences),
  "visualDirection": string (2-3 sentences),
  "wardrobe": string[] (5-7 specific outfit ideas),
  "colorPalette": [{"name": string, "hex": string}] (5 swatches, valid hex codes),
  "locations": [{"name": string, "reason": string}] (6 ideas),
  "shotList": string[] (22 specific shot descriptions, numbered ideas without leading numbers),
  "aiPrompts": [{"tool": "ChatGPT"|"Midjourney"|"Flux"|"Ideogram", "prompt": string}] (one per tool, each prompt 2-4 sentences, cinematic),
  "moodboard": {"lighting": string, "wardrobeStyle": string, "poseStyle": string, "environment": string},
  "checklist": {
    "wardrobe": string[] (4-6),
    "props": string[] (4-6),
    "makeupHair": string[] (4-6),
    "logistics": string[] (4-6),
    "backupPlan": string[] (3-5),
    "weather": string[] (3-5)
  }
}`;

    const startedAt = Date.now();
    const model = "google/gemini-3-flash-preview";

    let res: Response;
    try {
      res = await fetch("https://ai.gateway.lovable.dev/v1/chat/completions", {
        method: "POST",
        headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
        body: JSON.stringify({
          model,
          messages: [
            { role: "system", content: systemText },
            { role: "user", content: user },
          ],
          response_format: { type: "json_object" },
        }),
      });
    } catch (e) {
      await logAiRequest({
        userId: context.userId, kind: "photoshoot_plan", status: "error",
        latencyMs: Date.now() - startedAt, model,
        errorCode: "network", errorMessage: e instanceof Error ? e.message : String(e),
      });
      throw new Error("Could not reach the AI service. Please try again.");
    }

    if (!res.ok) {
      const text = await res.text();
      await logAiRequest({
        userId: context.userId, kind: "photoshoot_plan", status: "error",
        latencyMs: Date.now() - startedAt, model,
        errorCode: String(res.status), errorMessage: text.slice(0, 500),
      });
      if (res.status === 429) throw new Error("The AI service is busy. Please try again in a moment.");
      if (res.status === 402) throw new Error("AI credits exhausted. Please contact support.");
      throw new Error("The AI service returned an error. Please try again.");
    }

    const json = await res.json();
    const content: string = json?.choices?.[0]?.message?.content ?? "";
    const usage = json?.usage ?? {};

    // 3) Parse + validate
    let parsedRaw: unknown;
    try {
      parsedRaw = JSON.parse(content);
    } catch {
      const m = content.match(/\{[\s\S]*\}/);
      if (!m) {
        await logAiRequest({
          userId: context.userId, kind: "photoshoot_plan", status: "validation_failed",
          latencyMs: Date.now() - startedAt, model,
          promptTokens: usage.prompt_tokens, completionTokens: usage.completion_tokens, totalTokens: usage.total_tokens,
          errorCode: "json_parse", errorMessage: content.slice(0, 400),
        });
        throw new Error("The AI returned an unreadable response. Please try again.");
      }
      try { parsedRaw = JSON.parse(m[0]); } catch {
        await logAiRequest({
          userId: context.userId, kind: "photoshoot_plan", status: "validation_failed",
          latencyMs: Date.now() - startedAt, model,
          errorCode: "json_parse", errorMessage: m[0].slice(0, 400),
        });
        throw new Error("The AI returned an unreadable response. Please try again.");
      }
    }

    const validated = PhotoshootPlanSchema.safeParse(parsedRaw);
    if (!validated.success) {
      await logAiRequest({
        userId: context.userId, kind: "photoshoot_plan", status: "validation_failed",
        latencyMs: Date.now() - startedAt, model,
        promptTokens: usage.prompt_tokens, completionTokens: usage.completion_tokens, totalTokens: usage.total_tokens,
        errorCode: "schema", errorMessage: validated.error.issues.slice(0, 5).map((i) => `${i.path.join(".")}: ${i.message}`).join("; "),
      });
      throw new Error("The AI response was incomplete. Please try again — usually a single retry fixes it.");
    }

    await logAiRequest({
      userId: context.userId, kind: "photoshoot_plan", status: "ok",
      latencyMs: Date.now() - startedAt, model,
      promptTokens: usage.prompt_tokens, completionTokens: usage.completion_tokens, totalTokens: usage.total_tokens,
    });

    // Stash the system version id on the returned plan so the client can pass it back at save time.
    return { ...(validated.data as PhotoshootPlan), __systemPromptVersionId: systemVersionId } as PhotoshootPlan & { __systemPromptVersionId: string | null };
  });

const SaveSchema = z.object({
  input: InputSchema,
  plan: z.any(),
});

export const savePhotoshoot = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((d: unknown) => SaveSchema.parse(d))
  .handler(async ({ data, context }) => {
    const { input, plan } = data as { input: PhotoshootInput; plan: PhotoshootPlan & { __systemPromptVersionId?: string | null } };

    // Stamp library version ids from the extras that were used to generate this plan
    let libraryVersionIds: string[] = [];
    if (input.extraPromptIds && input.extraPromptIds.length > 0) {
      const { supabaseAdmin } = await import("@/integrations/supabase/client.server");
      const { data: vers } = await supabaseAdmin
        .from("library_prompt_versions")
        .select("id, prompt_id, version")
        .in("prompt_id", input.extraPromptIds);
      // pick the latest version per prompt_id
      const latest = new Map<string, { id: string; version: number }>();
      for (const v of vers ?? []) {
        const cur = latest.get(v.prompt_id as string);
        if (!cur || (v.version as number) > cur.version) {
          latest.set(v.prompt_id as string, { id: v.id as string, version: v.version as number });
        }
      }
      libraryVersionIds = Array.from(latest.values()).map((v) => v.id);
    }

    const cleanPlan = { ...plan };
    delete (cleanPlan as { __systemPromptVersionId?: unknown }).__systemPromptVersionId;

    const { data: row, error } = await context.supabase
      .from("photoshoots")
      .insert({
        user_id: context.userId,
        title: input.title,
        event_type: input.eventType,
        emotion: input.emotion,
        personality: input.personality,
        style: input.style,
        shoot_date: input.date || null,
        location: input.location || null,
        input: input as never,
        plan: cleanPlan as never,
        system_prompt_version_id: plan.__systemPromptVersionId ?? null,
        library_prompt_version_ids: libraryVersionIds,
      })
      .select("id")
      .single();
    if (error) throw new Error(error.message);
    return { id: row.id };
  });

export const listPhotoshoots = createServerFn({ method: "GET" })
  .middleware([requireSupabaseAuth])
  .handler(async ({ context }) => {
    const { data, error } = await context.supabase
      .from("photoshoots")
      .select("id, title, event_type, emotion, style, shoot_date, created_at")
      .order("created_at", { ascending: false });
    if (error) throw new Error(error.message);
    return data ?? [];
  });

export const getPhotoshoot = createServerFn({ method: "GET" })
  .middleware([requireSupabaseAuth])
  .inputValidator((d: unknown) => z.object({ id: z.string().uuid() }).parse(d))
  .handler(async ({ data, context }) => {
    const { data: row, error } = await context.supabase
      .from("photoshoots")
      .select("id, input, plan, moodboard_images, share_slug, is_public")
      .eq("id", data.id)
      .single();
    if (error) throw new Error(error.message);
    return {
      id: row.id,
      input: row.input as unknown as PhotoshootInput,
      plan: row.plan as unknown as PhotoshootPlan,
      moodboardImages: (row.moodboard_images as Record<string, string> | null) ?? null,
      shareSlug: row.share_slug,
      isPublic: row.is_public,
    };
  });

export const deletePhotoshoot = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((d: unknown) => z.object({ id: z.string().uuid() }).parse(d))
  .handler(async ({ data, context }) => {
    const { error } = await context.supabase.from("photoshoots").delete().eq("id", data.id);
    if (error) throw new Error(error.message);
    return { ok: true };
  });
