import { useState, useCallback, useEffect, useRef } from "react";
import { useMutation, useQuery } from "@tanstack/react-query";
import { Button } from "@/components/ui/button";
import { Slider } from "@/components/ui/slider";
import { Label } from "@/components/ui/label";
import { Card, CardContent } from "@/components/ui/card";
import { useToast } from "@/hooks/use-toast";
import { FileUploadZone } from "@/components/FileUploadZone";
import { BatchUploadZone } from "@/components/BatchUploadZone";
import { BatchJobPanel } from "@/components/BatchJobPanel";
import { apiRequest, queryClient } from "@/lib/queryClient";
import { Wand2, RotateCcw, Loader2, Sliders } from "lucide-react";
import type { TransformJob, BatchJob, UploadResponse } from "@shared/schema";

interface UploadedFile {
  file: File;
  id: string;
  url: string;
  duration: number;
  waveformData: number[];
}

interface BatchFile {
  id: string;
  file: File;
  status: "pending" | "uploading" | "ready" | "error";
  fileId?: string;
  duration?: number;
  waveformData?: number[];
}

let batchFileIdCounter = 0;

export default function BatchTransformer() {
  const { toast } = useToast();
  const [sourceFile, setSourceFile] = useState<UploadedFile | null>(null);
  const [batchFiles, setBatchFiles] = useState<BatchFile[]>([]);
  const [isSourceLoading, setIsSourceLoading] = useState(false);
  const [currentBatchJobId, setCurrentBatchJobId] = useState<string | null>(null);
  const [batchJob, setBatchJob] = useState<BatchJob | null>(null);
  const [jobs, setJobs] = useState<TransformJob[]>([]);
  const [intensity, setIntensity] = useState<number>(100);
  
  const wsRefs = useRef<Map<string, WebSocket>>(new Map());
  const pollingIntervalRef = useRef<NodeJS.Timeout | null>(null);

  const { data: batchStatusData, refetch: refetchBatchStatus } = useQuery<{ batchJob: BatchJob; jobs: TransformJob[] }>({
    queryKey: ["/api/batch", currentBatchJobId],
    enabled: !!currentBatchJobId && (batchJob?.status === "pending" || batchJob?.status === "processing"),
    refetchInterval: 2000,
  });

  useEffect(() => {
    if (batchStatusData) {
      setBatchJob(batchStatusData.batchJob);
      setJobs(batchStatusData.jobs);
    }
  }, [batchStatusData]);

  useEffect(() => {
    if (!currentBatchJobId || !jobs.length) return;

    jobs.forEach((job) => {
      if (job.status !== "completed" && job.status !== "error" && !wsRefs.current.has(job.id)) {
        connectJobWebSocket(job.id);
      }
    });

    return () => {
      wsRefs.current.forEach((ws) => ws.close());
      wsRefs.current.clear();
    };
  }, [currentBatchJobId, jobs]);

  const connectJobWebSocket = useCallback((jobId: string) => {
    if (wsRefs.current.has(jobId)) return;

    const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
    const ws = new WebSocket(`${protocol}//${window.location.host}/ws`);
    
    ws.onopen = () => {
      ws.send(JSON.stringify({ type: "subscribe", jobId }));
    };

    ws.onmessage = (event) => {
      try {
        const message = JSON.parse(event.data);
        if (message.type === "job_update" && message.data) {
          const updatedJob = message.data as TransformJob;
          setJobs((prevJobs) =>
            prevJobs.map((j) => (j.id === updatedJob.id ? updatedJob : j))
          );
          
          if (updatedJob.status === "completed" || updatedJob.status === "error") {
            refetchBatchStatus();
          }
        }
      } catch (error) {
        console.error("WebSocket message parse error:", error);
      }
    };

    ws.onclose = () => {
      wsRefs.current.delete(jobId);
    };

    wsRefs.current.set(jobId, ws);
  }, [refetchBatchStatus]);

  const uploadMutation = useMutation({
    mutationFn: async ({ file, type }: { file: File; type: "source" | "target" }) => {
      const formData = new FormData();
      formData.append("audio", file);
      formData.append("type", type);

      const response = await fetch("/api/upload", {
        method: "POST",
        body: formData,
      });

      if (!response.ok) {
        throw new Error("Upload failed");
      }

      return response.json() as Promise<UploadResponse>;
    },
    onSuccess: (data, variables) => {
      if (variables.type === "source") {
        const uploadedFile: UploadedFile = {
          file: variables.file,
          id: data.fileId,
          url: `/api/audio/${data.fileId}`,
          duration: data.duration,
          waveformData: data.waveformData,
        };
        setSourceFile(uploadedFile);
        setIsSourceLoading(false);
        toast({
          title: "Source file uploaded",
          description: `${variables.file.name} is ready.`,
        });
      }
    },
    onError: (error, variables) => {
      if (variables.type === "source") {
        setIsSourceLoading(false);
      }
      toast({
        title: "Upload failed",
        description: "Please try again with a valid MP3 file.",
        variant: "destructive",
      });
    },
  });

  const batchUploadMutation = useMutation({
    mutationFn: async ({ file, batchId }: { file: File; batchId: string }) => {
      const formData = new FormData();
      formData.append("audio", file);
      formData.append("type", "target");

      const response = await fetch("/api/upload", {
        method: "POST",
        body: formData,
      });

      if (!response.ok) {
        throw new Error("Upload failed");
      }

      const data = await response.json() as UploadResponse;
      return { data, batchId };
    },
    onSuccess: ({ data, batchId }) => {
      setBatchFiles((prev) =>
        prev.map((f) =>
          f.id === batchId
            ? { ...f, status: "ready" as const, fileId: data.fileId, duration: data.duration, waveformData: data.waveformData }
            : f
        )
      );
    },
    onError: (error, { batchId }) => {
      setBatchFiles((prev) =>
        prev.map((f) =>
          f.id === batchId
            ? { ...f, status: "error" as const }
            : f
        )
      );
      toast({
        title: "Upload failed",
        description: "One or more files failed to upload.",
        variant: "destructive",
      });
    },
  });

  const batchTransformMutation = useMutation({
    mutationFn: async () => {
      if (!sourceFile) {
        throw new Error("Source file required");
      }

      const readyFiles = batchFiles.filter((f) => f.status === "ready" && f.fileId);
      if (readyFiles.length === 0) {
        throw new Error("At least one target file required");
      }

      const response = await apiRequest("POST", "/api/batch-transform", {
        sourceFileId: sourceFile.id,
        targetFileIds: readyFiles.map((f) => f.fileId!),
        intensity,
      });

      return response.json() as Promise<BatchJob>;
    },
    onSuccess: (data) => {
      setCurrentBatchJobId(data.id);
      setBatchJob(data);
      setJobs([]);
      refetchBatchStatus();
      toast({
        title: "Batch transformation started",
        description: `Processing ${data.totalJobs} files...`,
      });
    },
    onError: () => {
      toast({
        title: "Batch transformation failed",
        description: "Unable to start the transformation process.",
        variant: "destructive",
      });
    },
  });

  const handleSourceSelect = useCallback(
    (file: File) => {
      setIsSourceLoading(true);
      uploadMutation.mutate({ file, type: "source" });
    },
    [uploadMutation]
  );

  const handleSourceRemove = useCallback(() => {
    setSourceFile(null);
    resetBatch();
  }, []);

  const handleBatchFilesAdd = useCallback((files: File[]) => {
    const newBatchFiles: BatchFile[] = files.map((file) => ({
      id: `batch-${++batchFileIdCounter}`,
      file,
      status: "uploading" as const,
    }));

    setBatchFiles((prev) => [...prev, ...newBatchFiles]);

    newBatchFiles.forEach((batchFile) => {
      batchUploadMutation.mutate({ file: batchFile.file, batchId: batchFile.id });
    });
  }, [batchUploadMutation]);

  const handleBatchFileRemove = useCallback((id: string) => {
    setBatchFiles((prev) => prev.filter((f) => f.id !== id));
  }, []);

  const handleClearBatchFiles = useCallback(() => {
    setBatchFiles([]);
  }, []);

  const handleTransform = useCallback(() => {
    batchTransformMutation.mutate();
  }, [batchTransformMutation]);

  const resetBatch = useCallback(() => {
    setCurrentBatchJobId(null);
    setBatchJob(null);
    setJobs([]);
    setBatchFiles([]);
    setIntensity(100);
    wsRefs.current.forEach((ws) => ws.close());
    wsRefs.current.clear();
    if (pollingIntervalRef.current) {
      clearInterval(pollingIntervalRef.current);
    }
  }, []);

  const handleReset = useCallback(() => {
    setSourceFile(null);
    resetBatch();
    queryClient.invalidateQueries({ queryKey: ["/api/batch"] });
  }, []);

  const handleDownload = useCallback((jobId: string, fileName: string) => {
    const link = document.createElement("a");
    link.href = `/api/result/${jobId}`;
    link.download = `transformed_${fileName}`;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  }, []);

  const handleDownloadAll = useCallback(() => {
    const completedJobs = jobs.filter((j) => j.status === "completed" && j.resultUrl);
    completedJobs.forEach((job) => {
      setTimeout(() => {
        handleDownload(job.id, job.targetFileName);
      }, 100);
    });
  }, [jobs, handleDownload]);

  const readyFilesCount = batchFiles.filter((f) => f.status === "ready").length;
  const canTransform = sourceFile && readyFilesCount > 0 && !batchTransformMutation.isPending;
  const isProcessing = batchJob?.status === "processing";

  return (
    <div className="min-h-screen bg-background">
      <div className="max-w-6xl mx-auto px-4 py-8 space-y-8">
        <div>
          <h1 className="text-2xl font-bold">Batch Processing</h1>
          <p className="text-muted-foreground">Transform multiple files at once</p>
        </div>

        <section className="grid grid-cols-1 lg:grid-cols-2 gap-6">
          <div>
            <h3 className="text-sm font-medium mb-3">Source Voice (Template)</h3>
            <FileUploadZone
              label="Source Voice (Template)"
              description="The voice characteristics to copy"
              type="source"
              file={sourceFile?.file || null}
              isLoading={isSourceLoading}
              duration={sourceFile?.duration}
              onFileSelect={handleSourceSelect}
              onRemove={handleSourceRemove}
            />
          </div>
          
          <div>
            <h3 className="text-sm font-medium mb-3">Target Voices (To Transform)</h3>
            <BatchUploadZone
              files={batchFiles}
              isLoading={isProcessing}
              maxFiles={10}
              onFilesAdd={handleBatchFilesAdd}
              onFileRemove={handleBatchFileRemove}
              onClearAll={handleClearBatchFiles}
            />
          </div>
        </section>

        {sourceFile && readyFilesCount > 0 && (
          <section className="max-w-md mx-auto">
            <Card>
              <CardContent className="pt-6">
                <div className="space-y-4">
                  <div className="flex items-center gap-2">
                    <Sliders className="h-4 w-4 text-muted-foreground" />
                    <Label htmlFor="batch-intensity-slider" className="text-sm font-medium">
                      Transformation Intensity
                    </Label>
                  </div>
                  <div className="flex items-center gap-4">
                    <Slider
                      id="batch-intensity-slider"
                      value={[intensity]}
                      onValueChange={(values) => setIntensity(values[0])}
                      min={0}
                      max={100}
                      step={1}
                      disabled={isProcessing}
                      className="flex-1"
                      data-testid="slider-batch-intensity"
                    />
                    <span className="text-sm font-mono w-12 text-right text-muted-foreground" data-testid="text-batch-intensity-value">
                      {intensity}%
                    </span>
                  </div>
                </div>
              </CardContent>
            </Card>
          </section>
        )}

        <section className="flex flex-col sm:flex-row items-center justify-center gap-4">
          <Button
            size="lg"
            onClick={handleTransform}
            disabled={!canTransform || isProcessing}
            className="min-w-[200px]"
            data-testid="button-batch-transform"
          >
            {isProcessing ? (
              <>
                <Loader2 className="h-5 w-5 mr-2 animate-spin" />
                Processing {readyFilesCount} files...
              </>
            ) : (
              <>
                <Wand2 className="h-5 w-5 mr-2" />
                Transform {readyFilesCount} Files
              </>
            )}
          </Button>
          <Button
            variant="secondary"
            size="lg"
            onClick={handleReset}
            disabled={isProcessing}
            data-testid="button-batch-reset"
          >
            <RotateCcw className="h-5 w-5 mr-2" />
            Reset All
          </Button>
        </section>

        {batchJob && (
          <BatchJobPanel
            batchJob={batchJob}
            jobs={jobs}
            onDownload={handleDownload}
            onDownloadAll={handleDownloadAll}
          />
        )}
      </div>
    </div>
  );
}
