"use client";

import { useState, useEffect, useRef } from "react";
import { Header } from "@/components/layout";
import { AppLayout } from "@/components/layout";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Progress } from "@/components/ui/progress";

import { PromptTextarea } from "@/components/forms";
import { Label } from "@/components/ui/label";
import { Card, CardContent } from "@/components/ui/card";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  apiClient,
  type GenerationRequest,
  type JobInfo,
  type JobDetailsResponse,
} from "@/lib/api";
import { Loader2, Download, X, Trash2, RotateCcw, Power } from "lucide-react";
import { downloadImage, downloadAuthenticatedImage } from "@/lib/utils";
import { useLocalStorage, useGeneratedImages } from "@/lib/storage";
import { useModelTypeSelection } from "@/contexts/model-selection-context";

const defaultFormData: GenerationRequest = {
  prompt: "",
  negative_prompt: "",
  width: 512,
  height: 512,
  steps: 20,
  cfg_scale: 7.5,
  seed: "",
  sampling_method: "euler_a",
  scheduler: "default",
  batch_count: 1,
};

function Text2ImgForm() {
  const {
    availableModels: vaeModels,
    selectedModel: selectedVae,
    setSelectedModel: setSelectedVae,
  } = useModelTypeSelection("vae");

  const {
    availableModels: taesdModels,
    selectedModel: selectedTaesd,
    setSelectedModel: setSelectedTaesd,
  } = useModelTypeSelection("taesd");

  const [formData, setFormData] = useLocalStorage<GenerationRequest>(
    "text2img-form-data",
    defaultFormData,
    { excludeLargeData: true, maxSize: 512 * 1024 }, // 512KB limit
  );

  const [loading, setLoading] = useState(false);
  const [jobInfo, setJobInfo] = useState<JobInfo | null>(null);
  const { images: storedImages, addImages, getLatestImages } = useGeneratedImages('text2img');
  const [generatedImages, setGeneratedImages] = useState<string[]>(() => storedImages.map(img => img.url));
  const [samplers, setSamplers] = useState<
    Array<{ name: string; description: string }>
  >([]);
  const [schedulers, setSchedulers] = useState<
    Array<{ name: string; description: string }>
  >([]);
  const [loraModels, setLoraModels] = useState<string[]>([]);
  const [embeddings, setEmbeddings] = useState<string[]>([]);
  const [error, setError] = useState<string | null>(null);
  const pollCleanupRef = useRef<(() => void) | null>(null);

  // Cleanup polling on unmount
  useEffect(() => {
    return () => {
      if (pollCleanupRef.current) {
        pollCleanupRef.current();
        pollCleanupRef.current = null;
      }
    };
  }, []);

  useEffect(() => {
    const loadOptions = async () => {
      try {
        const [samplersData, schedulersData, loras, embeds] = await Promise.all(
          [
            apiClient.getSamplers(),
            apiClient.getSchedulers(),
            apiClient.getModels("lora"),
            apiClient.getModels("embedding"),
          ],
        );
        setSamplers(samplersData);
        setSchedulers(schedulersData);
        setLoraModels(loras.models.map((m) => m.name));
        setEmbeddings(embeds.models.map((m) => m.name));
      } catch (err) {
        console.error("Failed to load options:", err);
      }
    };
    loadOptions();
  }, []);

  const handleInputChange = (
    e: React.ChangeEvent<
      HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
    >,
  ) => {
    const { name, value } = e.target;
    setFormData((prev) => ({
      ...prev,
      [name]:
        name === "prompt" ||
        name === "negative_prompt" ||
        name === "seed" ||
        name === "sampling_method" ||
        name === "scheduler"
          ? value
          : Number(value),
    }));
  };

  const pollJobStatus = async (jobId: string) => {
    const maxAttempts = 300; // 5 minutes with 2 second interval
    let attempts = 0;
    let isPolling = true;
    let timeoutId: NodeJS.Timeout | null = null;

    const poll = async () => {
      if (!isPolling) return;

      try {
        const status: JobDetailsResponse = await apiClient.getJobStatus(jobId);
        setJobInfo(status.job);

        console.log(`[DEBUG] Job ${jobId} status: ${status.job.status}, progress: ${status.job.progress}, outputs:`, status.job.outputs);

        if (status.job.status === "completed") {
          let imageUrls: string[] = [];

          // Handle both old format (result.images) and new format (outputs)
          if (status.job.outputs && status.job.outputs.length > 0) {
            console.log(`[DEBUG] Processing ${status.job.outputs.length} outputs`);
            // New format: convert output URLs to authenticated image URLs with cache-busting
            imageUrls = status.job.outputs.map((output: { filename: string }) => {
              const filename = output.filename;
              const imageUrl = apiClient.getImageUrl(jobId, filename);
              console.log(`[DEBUG] Generated URL for ${filename}: ${imageUrl}`);
              return imageUrl;
            });
          } else if (
            status.job.result?.images &&
            status.job.result.images.length > 0
          ) {
            console.log(`[DEBUG] Using old format with ${status.job.result.images.length} images`);
            // Old format: convert image URLs to authenticated URLs
            imageUrls = status.job.result.images.map((imageUrl: string) => {
              // Extract filename from URL if it's a full URL
              if (imageUrl.includes("/output/")) {
                const parts = imageUrl.split("/output/");
                if (parts.length === 2) {
                  const filename = parts[1].split("?")[0]; // Remove query params
                  return apiClient.getImageUrl(jobId, filename);
                }
              }
              // If it's just a filename, convert it directly
              return apiClient.getImageUrl(jobId, imageUrl);
            });
          } else {
            console.log(`[DEBUG] No outputs or images found in job response`);
          }

          console.log(`[DEBUG] Final image URLs:`, imageUrls);

          // Create a new array to trigger React re-render
          setGeneratedImages([...imageUrls]);
          addImages(imageUrls, jobId);
          setLoading(false);
          isPolling = false;
        } else if (status.job.status === "failed") {
          console.log(`[DEBUG] Job failed with error: ${status.job.error}`);
          setError(status.job.error || "Generation failed");
          setLoading(false);
          isPolling = false;
        } else if (status.job.status === "cancelled") {
          console.log(`[DEBUG] Job was cancelled`);
          setError("Generation was cancelled");
          setLoading(false);
          isPolling = false;
        } else if (attempts < maxAttempts) {
          attempts++;
          timeoutId = setTimeout(poll, 2000);
        } else {
          console.log(`[DEBUG] Job polling timeout after ${attempts} attempts`);
          setError("Job polling timeout");
          setLoading(false);
          isPolling = false;
        }
      } catch (err) {
        console.log(`[DEBUG] Error polling job status:`, err);
        if (isPolling) {
          setError(
            err instanceof Error ? err.message : "Failed to check job status",
          );
          setLoading(false);
          isPolling = false;
        }
      }
    };

    poll();

    // Return cleanup function
    return () => {
      isPolling = false;
      if (timeoutId) {
        clearTimeout(timeoutId);
      }
    };
  };

  const handleGenerate = async (e: React.FormEvent) => {
    e.preventDefault();

    setLoading(true);
    setError(null);
    setGeneratedImages([]);
    setJobInfo(null);

    try {
      const requestData = {
        ...formData,
        vae: selectedVae || undefined,
        taesd: selectedTaesd || undefined,
      };

      const job = await apiClient.text2img(requestData);
      setJobInfo(job);
      const jobId = job.request_id || job.id;
      if (jobId) {
        pollJobStatus(jobId).then((cleanup) => {
          pollCleanupRef.current = cleanup;
        });
      } else {
        setError("No job ID returned from server");
        setLoading(false);
      }
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to generate image");
      setLoading(false);
    }
  };

  const handleCancel = async () => {
    const jobId = jobInfo?.request_id || jobInfo?.id;
    if (jobId) {
      try {
        await apiClient.cancelJob(jobId);
        setLoading(false);
        setError("Generation cancelled");
        // Cleanup polling
        if (pollCleanupRef.current) {
          pollCleanupRef.current();
          pollCleanupRef.current = null;
        }
      } catch (err) {
        console.error("Failed to cancel job:", err);
      }
    }
  };

  const handleClearPrompts = () => {
    setFormData({ ...formData, prompt: "", negative_prompt: "" });
  };

  const handleResetToDefaults = () => {
    setFormData(defaultFormData);
  };

  const handleServerRestart = async () => {
    if (
      !confirm(
        "Are you sure you want to restart the server? This will cancel all running jobs.",
      )
    ) {
      return;
    }
    try {
      setLoading(true);
      await apiClient.restartServer();
      setError("Server restart initiated. Please wait...");
      setTimeout(() => {
        window.location.reload();
      }, 3000);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to restart server");
      setLoading(false);
    }
  };

  return (
    <AppLayout>
      <Header
        title="Text to Image"
        description="Generate images from text prompts"
      />
      <div className="container mx-auto p-6">
        <div className="grid gap-6 lg:grid-cols-2">
          {/* Left Panel - Form */}
          <Card>
            <CardContent className="pt-6">
              <form onSubmit={handleGenerate} className="space-y-4">
                <div className="space-y-2">
                  <Label htmlFor="prompt">Prompt *</Label>
                  <PromptTextarea
                    value={formData.prompt}
                    onChange={(value) =>
                      setFormData({ ...formData, prompt: value })
                    }
                    placeholder="a beautiful landscape with mountains and a lake, sunset, highly detailed..."
                    rows={4}
                    loras={loraModels}
                    embeddings={embeddings}
                  />
                  <p className="text-xs text-muted-foreground">
                    Tip: Use &lt;lora:name:weight&gt; for LoRAs (e.g.,
                    &lt;lora:myLora:0.8&gt;) and embedding names directly
                  </p>
                </div>

                <div className="space-y-2">
                  <Label htmlFor="negative_prompt">Negative Prompt</Label>
                  <PromptTextarea
                    value={formData.negative_prompt || ""}
                    onChange={(value) =>
                      setFormData({ ...formData, negative_prompt: value })
                    }
                    placeholder="blurry, low quality, distorted..."
                    rows={2}
                    loras={loraModels}
                    embeddings={embeddings}
                  />
                </div>

                {/* Utility Buttons */}
                <div className="flex gap-2">
                  <Button
                    type="button"
                    variant="outline"
                    size="sm"
                    onClick={handleClearPrompts}
                    disabled={loading}
                    title="Clear both prompts"
                  >
                    <Trash2 className="h-4 w-4 mr-1" />
                    Clear Prompts
                  </Button>
                  <Button
                    type="button"
                    variant="outline"
                    size="sm"
                    onClick={handleResetToDefaults}
                    disabled={loading}
                    title="Reset all fields to defaults"
                  >
                    <RotateCcw className="h-4 w-4 mr-1" />
                    Reset to Defaults
                  </Button>
                  <Button
                    type="button"
                    variant="outline"
                    size="sm"
                    onClick={handleServerRestart}
                    disabled={loading}
                    title="Restart the backend server"
                  >
                    <Power className="h-4 w-4 mr-1" />
                    Restart Server
                  </Button>
                </div>

                <div className="grid grid-cols-2 gap-4">
                  <div className="space-y-2">
                    <Label htmlFor="width">Width</Label>
                    <Input
                      id="width"
                      name="width"
                      type="number"
                      value={formData.width}
                      onChange={handleInputChange}
                      step={64}
                      min={256}
                      max={2048}
                    />
                  </div>
                  <div className="space-y-2">
                    <Label htmlFor="height">Height</Label>
                    <Input
                      id="height"
                      name="height"
                      type="number"
                      value={formData.height}
                      onChange={handleInputChange}
                      step={64}
                      min={256}
                      max={2048}
                    />
                  </div>
                </div>

                <div className="grid grid-cols-2 gap-4">
                  <div className="space-y-2">
                    <Label htmlFor="steps">Steps</Label>
                    <Input
                      id="steps"
                      name="steps"
                      type="number"
                      value={formData.steps}
                      onChange={handleInputChange}
                      min={1}
                      max={150}
                    />
                  </div>
                  <div className="space-y-2">
                    <Label htmlFor="cfg_scale">CFG Scale</Label>
                    <Input
                      id="cfg_scale"
                      name="cfg_scale"
                      type="number"
                      value={formData.cfg_scale}
                      onChange={handleInputChange}
                      step={0.5}
                      min={1}
                      max={30}
                    />
                  </div>
                </div>

                <div className="space-y-2">
                  <Label htmlFor="seed">Seed (optional)</Label>
                  <Input
                    id="seed"
                    name="seed"
                    value={formData.seed}
                    onChange={handleInputChange}
                    placeholder="Leave empty for random"
                  />
                </div>

                <div className="space-y-2">
                  <Label htmlFor="sampling_method">Sampling Method</Label>
                  <select
                    id="sampling_method"
                    name="sampling_method"
                    value={formData.sampling_method}
                    onChange={handleInputChange}
                    className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
                  >
                    {samplers.length > 0 ? (
                      samplers.map((sampler) => (
                        <option key={sampler.name} value={sampler.name}>
                          {sampler.name.toUpperCase()} - {sampler.description}
                        </option>
                      ))
                    ) : (
                      <option value="euler_a">Loading...</option>
                    )}
                  </select>
                </div>

                <div className="space-y-2">
                  <Label htmlFor="scheduler">Scheduler</Label>
                  <select
                    id="scheduler"
                    name="scheduler"
                    value={formData.scheduler}
                    onChange={handleInputChange}
                    className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
                  >
                    {schedulers.length > 0 ? (
                      schedulers.map((scheduler) => (
                        <option key={scheduler.name} value={scheduler.name}>
                          {scheduler.name.toUpperCase()} -{" "}
                          {scheduler.description}
                        </option>
                      ))
                    ) : (
                      <option value="default">Loading...</option>
                    )}
                  </select>
                 </div>

                 <div className="space-y-2">
                   <Label>VAE Model (Optional)</Label>
                   <Select
                     value={selectedVae || "none"}
                     onValueChange={(value) => setSelectedVae(value === "none" ? undefined : value)}
                   >
                     <SelectTrigger>
                       <SelectValue placeholder="Select VAE model" />
                     </SelectTrigger>
                     <SelectContent>
                       <SelectItem value="none">None</SelectItem>
                       {vaeModels.map((model) => {
                         const modelId = model.sha256_short || model.sha256 || model.id || model.name;
                         const displayName = model.sha256_short
                           ? `${model.name} (${model.sha256_short})`
                           : model.name;
                         return (
                           <SelectItem key={modelId} value={modelId}>
                             {displayName}
                           </SelectItem>
                         );
                       })}
                     </SelectContent>
                   </Select>
                 </div>

                 <div className="space-y-2">
                   <Label>TAESD Model (Optional)</Label>
                   <Select
                     value={selectedTaesd || "none"}
                     onValueChange={(value) => setSelectedTaesd(value === "none" ? undefined : value)}
                   >
                     <SelectTrigger>
                       <SelectValue placeholder="Select TAESD model" />
                     </SelectTrigger>
                     <SelectContent>
                       <SelectItem value="none">None</SelectItem>
                       {taesdModels.map((model) => {
                         const modelId = model.sha256_short || model.sha256 || model.id || model.name;
                         const displayName = model.sha256_short
                           ? `${model.name} (${model.sha256_short})`
                           : model.name;
                         return (
                           <SelectItem key={modelId} value={modelId}>
                             {displayName}
                           </SelectItem>
                         );
                       })}
                     </SelectContent>
                   </Select>
                 </div>

                 <div className="space-y-2">
                   <Label htmlFor="batch_count">Batch Count</Label>
                  <Input
                    id="batch_count"
                    name="batch_count"
                    type="number"
                    value={formData.batch_count}
                    onChange={handleInputChange}
                    min={1}
                    max={4}
                  />
                </div>

                <div className="flex gap-2">
                  <Button type="submit" disabled={loading} className="flex-1">
                    {loading ? (
                      <>
                        <Loader2 className="h-4 w-4 animate-spin" />
                        Generating...
                      </>
                    ) : (
                      "Generate"
                    )}
                  </Button>
                  {loading && (
                    <Button
                      type="button"
                      variant="destructive"
                      onClick={handleCancel}
                    >
                      <X className="h-4 w-4" />
                      Cancel
                    </Button>
                  )}
                </div>

                {error && (
                  <div className="rounded-md bg-destructive/10 p-3 text-sm text-destructive">
                    {error}
                  </div>
                )}
              </form>
            </CardContent>
          </Card>

          {/* Right Panel - Generated Images */}
          <Card>
            <CardContent className="pt-6">
              <div className="space-y-4">
                <h3 className="text-lg font-semibold">Generated Images</h3>
                
                {/* Progress Display */}
                {loading && jobInfo && (
                  <div className="space-y-2">
                    <div className="flex justify-between text-sm">
                      <span>Progress</span>
                      <span>{Math.round(jobInfo.overall_progress || jobInfo.progress || 0)}%</span>
                    </div>
                    <Progress value={jobInfo.overall_progress || jobInfo.progress || 0} className="w-full" />
                    {jobInfo.model_load_progress !== undefined && jobInfo.generation_progress !== undefined && (
                      <div className="grid grid-cols-2 gap-4 text-xs text-muted-foreground">
                        <div>Model Loading: {Math.round(jobInfo.model_load_progress)}%</div>
                        <div>Generation: {Math.round(jobInfo.generation_progress)}%</div>
                      </div>
                    )}
                  </div>
                )}
                
                {generatedImages.length === 0 ? (
                  <div className="flex h-96 items-center justify-center rounded-lg border-2 border-dashed border-border">
                    <p className="text-muted-foreground">
                      {loading
                        ? "Generating..."
                        : "Generated images will appear here"}
                    </p>
                  </div>
                ) : (
                  <div className="grid gap-4">
                    {generatedImages.map((image, index) => (
                      <div key={index} className="relative group">
                        <img
                          src={image}
                          alt={`Generated ${index + 1}`}
                          className="w-full rounded-lg border border-border"
                        />
                        <Button
                          size="icon"
                          variant="secondary"
                          className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity"
                          onClick={() => {
                            const authToken =
                              localStorage.getItem("auth_token");
                            const unixUser = localStorage.getItem("unix_user");
                            downloadAuthenticatedImage(
                              image,
                              `generated-${Date.now()}-${index}.png`,
                              authToken || undefined,
                              unixUser || undefined,
                            ).catch((err) => {
                              console.error("Failed to download image:", err);
                              // Fallback to regular download if authenticated download fails
                              downloadImage(
                                image,
                                `generated-${Date.now()}-${index}.png`,
                              );
                            });
                          }}
                        >
                          <Download className="h-4 w-4" />
                        </Button>
                      </div>
                    ))}
                  </div>
                )}
              </div>
            </CardContent>
          </Card>
        </div>
      </div>
    </AppLayout>
  );
}

export default function Text2ImgPage() {
  return <Text2ImgForm />;
}
