"use client";

import { useMemo, useState } from "react";
import { Download } from "lucide-react";

import {
  ExportPanel,
  MediaUploadDropzone,
  MetadataPanel,
  ProcessingStatus,
  VideoPlayerPreview
} from "@/components/media";
import { EmptyState } from "@/components/shared/empty-state";
import { ErrorState } from "@/components/shared/error-state";
import { SimpleToastStack } from "@/components/shared/simple-toast-stack";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectTrigger,
  SelectValue
} from "@/components/ui/select";
import { useObjectUrl } from "@/hooks/use-object-url";
import { useSimpleToast } from "@/hooks/use-simple-toast";
import {
  parseApiErrorMessage,
  parseBooleanHeader,
  parseNumberHeader
} from "@/lib/client/api-utils";
import { formatBytes } from "@/lib/client/file-validation";
import { formatSecondsAsClock } from "@/lib/client/time-format";
import { conversionPresetOptions, videoFormatOptions } from "@/lib/media-processing/conversion-presets";
import type { ApiSuccessResponse } from "@/types/api";
import type { MetadataItem } from "@/types/media-ui";
import type {
  ConversionQualityPreset,
  VideoMetadata,
  VideoOutputFormat
} from "@/types/media-processing";

export function VideoFormatConverterTool() {
  const [selectedFile, setSelectedFile] = useState<File | null>(null);
  const sourceObjectUrl = useObjectUrl(selectedFile);

  const [metadata, setMetadata] = useState<VideoMetadata | null>(null);
  const [metadataState, setMetadataState] = useState<"idle" | "processing" | "success" | "error">("idle");
  const [metadataError, setMetadataError] = useState<string | null>(null);

  const [outputFormat, setOutputFormat] = useState<VideoOutputFormat>("mp4");
  const [qualityPreset, setQualityPreset] = useState<ConversionQualityPreset>("standard");

  const [conversionState, setConversionState] = useState<"idle" | "processing" | "success" | "error">("idle");
  const [conversionProgress, setConversionProgress] = useState(0);
  const [conversionError, setConversionError] = useState<string | null>(null);
  const [convertedBlob, setConvertedBlob] = useState<Blob | null>(null);
  const [convertedFileName, setConvertedFileName] = useState<string>("converted.mp4");
  const [convertedFormat, setConvertedFormat] = useState<VideoOutputFormat | null>(null);
  const [convertedPreset, setConvertedPreset] = useState<ConversionQualityPreset | null>(null);
  const [audioPreserved, setAudioPreserved] = useState<boolean | null>(null);
  const [outputDurationSeconds, setOutputDurationSeconds] = useState<number | null>(null);

  const convertedObjectUrl = useObjectUrl(convertedBlob);
  const { toasts, pushToast, dismissToast } = useSimpleToast();

  const selectedFormatDefinition = useMemo(
    () => videoFormatOptions.find((option) => option.format === outputFormat),
    [outputFormat]
  );
  const selectedPresetDefinition = useMemo(
    () => conversionPresetOptions.find((option) => option.preset === qualityPreset),
    [qualityPreset]
  );

  const metadataItems: MetadataItem[] = useMemo(() => {
    if (!metadata) {
      return [];
    }

    return [
      {
        key: "filename",
        label: "Filename",
        value: metadata.filename
      },
      {
        key: "duration",
        label: "Duration",
        value: formatSecondsAsClock(metadata.durationSeconds)
      },
      {
        key: "resolution",
        label: "Resolution",
        value: `${metadata.width} x ${metadata.height}`
      },
      {
        key: "frameRate",
        label: "Frame Rate",
        value: metadata.frameRate ? `${metadata.frameRate.toFixed(2)} fps` : "Unknown"
      },
      {
        key: "videoCodec",
        label: "Video Codec",
        value: metadata.videoCodec ?? "Unknown"
      },
      {
        key: "audio",
        label: "Audio",
        value: metadata.hasAudio ? "Present" : "None"
      }
    ];
  }, [metadata]);

  const canConvert = Boolean(selectedFile) && metadataState === "success" && Boolean(metadata);

  const resetConversionOutput = () => {
    setConversionState("idle");
    setConversionProgress(0);
    setConversionError(null);
    setConvertedBlob(null);
    setConvertedFileName("converted.mp4");
    setConvertedFormat(null);
    setConvertedPreset(null);
    setAudioPreserved(null);
    setOutputDurationSeconds(null);
  };

  const handleClearMedia = () => {
    setSelectedFile(null);
    setMetadata(null);
    setMetadataState("idle");
    setMetadataError(null);
    setOutputFormat("mp4");
    setQualityPreset("standard");
    resetConversionOutput();
  };

  const loadMetadata = async (file: File) => {
    setMetadataState("processing");
    setMetadataError(null);
    setMetadata(null);

    try {
      const formData = new FormData();
      formData.set("file", file);

      const response = await fetch("/api/tools/video-format-converter/metadata", {
        method: "POST",
        body: formData
      });

      if (!response.ok) {
        const message = await parseApiErrorMessage(response, "Failed to load video metadata.");
        throw new Error(message);
      }

      const payload = (await response.json()) as ApiSuccessResponse<VideoMetadata>;
      if (!payload.ok) {
        throw new Error("Unexpected metadata response.");
      }

      setMetadata(payload.data);
      setMetadataState("success");
    } catch (error) {
      const message = error instanceof Error ? error.message : "Failed to load metadata.";
      setMetadataState("error");
      setMetadataError(message);
      pushToast({
        title: "Metadata failed",
        description: message,
        variant: "error"
      });
    }
  };

  const handleConvert = async () => {
    if (!selectedFile || !metadata) {
      return;
    }

    setConversionState("processing");
    setConversionProgress(12);
    setConversionError(null);
    setConvertedBlob(null);
    setAudioPreserved(null);
    setOutputDurationSeconds(null);

    const progressTimer = setInterval(() => {
      setConversionProgress((previousProgress) => Math.min(previousProgress + Math.random() * 11, 92));
    }, 240);

    try {
      const formData = new FormData();
      formData.set("file", selectedFile);
      formData.set("format", outputFormat);
      formData.set("preset", qualityPreset);

      const response = await fetch("/api/tools/video-format-converter/convert", {
        method: "POST",
        body: formData
      });

      if (!response.ok) {
        const message = await parseApiErrorMessage(response, "Video conversion failed.");
        throw new Error(message);
      }

      const convertedVideoBlob = await response.blob();
      if (!convertedVideoBlob || convertedVideoBlob.size <= 0) {
        throw new Error("Converted video output was empty.");
      }

      const fileName =
        response.headers.get("x-videohaus-file-name") ||
        `${selectedFile.name.replace(/\.[^.]+$/, "")}-${qualityPreset}.${outputFormat}`;

      const formatFromHeader = response.headers.get("x-videohaus-format");
      const presetFromHeader = response.headers.get("x-videohaus-preset");

      setConvertedBlob(convertedVideoBlob);
      setConvertedFileName(fileName);
      setConvertedFormat(
        formatFromHeader === "mp4" ||
          formatFromHeader === "mov" ||
          formatFromHeader === "webm" ||
          formatFromHeader === "mkv" ||
          formatFromHeader === "avi"
          ? formatFromHeader
          : outputFormat
      );
      setConvertedPreset(
        presetFromHeader === "high" || presetFromHeader === "standard" || presetFromHeader === "small"
          ? presetFromHeader
          : qualityPreset
      );
      setAudioPreserved(parseBooleanHeader(response.headers.get("x-videohaus-audio-preserved")));
      setOutputDurationSeconds(parseNumberHeader(response.headers.get("x-videohaus-source-duration")));
      setConversionProgress(100);
      setConversionState("success");

      pushToast({
        title: "Conversion complete",
        description: `${outputFormat.toUpperCase()} output is ready to download.`,
        variant: "success"
      });
    } catch (error) {
      const message = error instanceof Error ? error.message : "Failed to convert video.";
      setConversionState("error");
      setConversionProgress(0);
      setConversionError(message);
      pushToast({
        title: "Conversion failed",
        description: message,
        variant: "error"
      });
    } finally {
      clearInterval(progressTimer);
    }
  };

  return (
    <div className="space-y-5">
      <MediaUploadDropzone
        file={selectedFile}
        onFileSelected={(file) => {
          setSelectedFile(file);
          resetConversionOutput();
          void loadMetadata(file);
        }}
        onClear={handleClearMedia}
        busy={metadataState === "processing"}
        validation={{
          maxSizeBytes: 1_000_000_000,
          allowedExtensions: [".mp4", ".mov", ".m4v", ".webm", ".mkv", ".avi", ".wmv", ".flv"]
        }}
        onValidationError={(message) =>
          pushToast({
            title: "Invalid file",
            description: message,
            variant: "error"
          })
        }
      />

      <ProcessingStatus
        state={metadataState}
        label={
          metadataState === "processing"
            ? "Loading source metadata..."
            : metadataState === "success"
              ? "Metadata loaded"
              : metadataState === "error"
                ? "Metadata failed"
                : "Awaiting upload"
        }
        details={metadataError ?? undefined}
        progress={metadataState === "processing" ? 35 : metadataState === "success" ? 100 : 0}
      />

      {metadataState === "error" && metadataError ? (
        <ErrorState
          title="Metadata Error"
          description={metadataError}
          actionLabel="Retry"
          onAction={() => {
            if (selectedFile) {
              void loadMetadata(selectedFile);
            }
          }}
        />
      ) : null}

      {metadata ? (
        <>
          <MetadataPanel title="Source Metadata" items={metadataItems} columns={3} />

          <VideoPlayerPreview src={sourceObjectUrl} />

          <ExportPanel
            title="Format Conversion"
            description="Convert to a delivery format with centralized quality presets while keeping aspect ratio intact."
            primaryAction={{
              label: "Convert Video",
              onClick: () => {
                void handleConvert();
              },
              disabled: !canConvert || conversionState === "processing",
              loading: conversionState === "processing"
            }}
          >
            <div className="grid gap-3 sm:grid-cols-2">
              <div className="space-y-2">
                <p className="text-xs uppercase tracking-wide text-slate-400">Output Format</p>
                <Select
                  value={outputFormat}
                  onValueChange={(value) => {
                    setOutputFormat(value as VideoOutputFormat);
                    resetConversionOutput();
                  }}
                >
                  <SelectTrigger className="w-full border-white/20 bg-slate-950">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectGroup>
                      {videoFormatOptions.map((formatOption) => (
                        <SelectItem key={formatOption.format} value={formatOption.format}>
                          {formatOption.label}
                        </SelectItem>
                      ))}
                    </SelectGroup>
                  </SelectContent>
                </Select>
                {selectedFormatDefinition ? (
                  <p className="text-xs text-slate-400">{selectedFormatDefinition.description}</p>
                ) : null}
              </div>

              <div className="space-y-2">
                <p className="text-xs uppercase tracking-wide text-slate-400">Quality Preset</p>
                <Select
                  value={qualityPreset}
                  onValueChange={(value) => {
                    setQualityPreset(value as ConversionQualityPreset);
                    resetConversionOutput();
                  }}
                >
                  <SelectTrigger className="w-full border-white/20 bg-slate-950">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectGroup>
                      {conversionPresetOptions.map((presetOption) => (
                        <SelectItem key={presetOption.preset} value={presetOption.preset}>
                          {presetOption.label}
                        </SelectItem>
                      ))}
                    </SelectGroup>
                  </SelectContent>
                </Select>
                {selectedPresetDefinition ? (
                  <p className="text-xs text-slate-400">{selectedPresetDefinition.description}</p>
                ) : null}
              </div>
            </div>

            <div className="rounded-lg border border-white/10 bg-white/5 px-3 py-2 text-xs text-slate-300">
              Aspect ratio is preserved during conversion. Audio is retained automatically when the source has an audio
              stream.
            </div>
          </ExportPanel>

          <ProcessingStatus
            state={conversionState}
            label={
              conversionState === "processing"
                ? "Converting video..."
                : conversionState === "success"
                  ? "Conversion ready"
                  : conversionState === "error"
                    ? "Conversion failed"
                    : "No conversion yet"
            }
            details={conversionError ?? undefined}
            progress={conversionProgress}
          />

          {convertedObjectUrl ? (
            <section className="glass-panel rounded-2xl border border-white/15 p-5">
              <div className="flex flex-wrap items-start justify-between gap-3">
                <div className="space-y-1">
                  <h3 className="font-[var(--font-space-grotesk)] text-lg font-medium text-white">
                    Converted Video
                  </h3>
                  <div className="flex flex-wrap items-center gap-2">
                    <Badge className="border-violet-300/35 bg-violet-500/15 text-violet-100">
                      {(convertedFormat ?? outputFormat).toUpperCase()}
                    </Badge>
                    <Badge className="border-white/20 bg-slate-900/75 text-slate-100">
                      {(convertedPreset ?? qualityPreset) === "small"
                        ? "Small file"
                        : (convertedPreset ?? qualityPreset).charAt(0).toUpperCase() +
                          (convertedPreset ?? qualityPreset).slice(1)}
                    </Badge>
                    <Badge className="border-white/20 bg-slate-900/75 text-slate-100">
                      {audioPreserved ? "Audio preserved" : "No source audio"}
                    </Badge>
                  </div>
                  <p className="text-xs text-slate-400">
                    {formatBytes(convertedBlob?.size ?? 0)}
                    {outputDurationSeconds != null ? ` | Duration ${formatSecondsAsClock(outputDurationSeconds)}` : ""}
                  </p>
                </div>
                <Button
                  render={
                    <a href={convertedObjectUrl} download={convertedFileName}>
                      <Download data-icon="inline-start" />
                      Download
                    </a>
                  }
                />
              </div>

              <video className="mt-4 aspect-video w-full rounded-xl border border-white/10 bg-black" controls>
                <source src={convertedObjectUrl} />
                Your browser does not support video playback.
              </video>
            </section>
          ) : (
            <EmptyState
              title="No Converted Output Yet"
              description="Choose format and quality preset, then run conversion to preview and download."
            />
          )}
        </>
      ) : null}

      <SimpleToastStack toasts={toasts} onDismiss={dismissToast} />
    </div>
  );
}
