"use client";

import * as React from "react";
import {
  EyeIcon,
  FileTextIcon,
  Loader2,
  RotateCwIcon,
  UploadCloudIcon,
  XIcon,
} from "lucide-react";

import { cn } from "@/lib/utils";
import { Typography } from "@/components/ui/typography";
import { Button } from "@/components/ui/button";
import { useTranslation } from "@/hooks/useTranslation";
import {
  UploadType,
  type UploadedFileRef,
  type UploadRejectionReason,
} from "@/types/upload";
import { uploadFileToStorage, validateUploadFile } from "@/lib/api/upload.api";

const ERROR_KEYS: Record<UploadRejectionReason, string> = {
  "too-large": "fileUpload.errors.tooLarge",
  "invalid-type": "fileUpload.errors.invalidType",
};

interface FileUploadProps {
  uploadType: UploadType;
  /** Controlled value — the committed tmp reference, or null when empty */
  value: UploadedFileRef | null;
  onChange: (file: UploadedFileRef | null) => void;
  /** `accept` attribute, e.g. "image/png,image/jpeg,image/webp" */
  accept: string;
  /** "image" shows a thumbnail once uploaded; "document" shows a file row */
  variant?: "image" | "document";
  /** Already-translated helper line shown in the idle drop zone */
  hint?: React.ReactNode;
  disabled?: boolean;
  id?: string;
  className?: string;
}

/**
 * Single-file uploader with the two-phase presign → PUT flow. Reused for the
 * provider logo and each KYC document. Controlled: the parent owns the resulting
 * {@link UploadedFileRef}; this component owns only the transient upload state
 * (progress / error). Object URLs for image previews are revoked on replace and
 * remove.
 */
function FileUpload({
  uploadType,
  value,
  onChange,
  accept,
  variant = "document",
  hint,
  disabled,
  id,
  className,
}: FileUploadProps) {
  const { t } = useTranslation();
  const inputRef = React.useRef<HTMLInputElement>(null);
  const [status, setStatus] = React.useState<"idle" | "uploading" | "error">(
    "idle",
  );
  const [progress, setProgress] = React.useState(0);
  const [errorKey, setErrorKey] = React.useState<string | null>(null);
  const [isDragging, setIsDragging] = React.useState(false);

  async function handleFile(file: File) {
    const rejection = validateUploadFile(file, uploadType);
    if (rejection) {
      setStatus("error");
      setErrorKey(ERROR_KEYS[rejection]);
      return;
    }

    // Replace any previous preview before creating a new one. A preview URL is
    // created for every variant (not just images) so the uploaded file can be
    // opened/viewed — images show a thumbnail, documents open in a new tab.
    if (value?.previewUrl) URL.revokeObjectURL(value.previewUrl);
    const previewUrl = URL.createObjectURL(file);

    setStatus("uploading");
    setErrorKey(null);
    setProgress(0);

    try {
      const s3Key = await uploadFileToStorage(file, uploadType, setProgress);
      onChange({ s3Key, fileName: file.name, previewUrl });
      setStatus("idle");
    } catch {
      if (previewUrl) URL.revokeObjectURL(previewUrl);
      setStatus("error");
      setErrorKey("fileUpload.errors.failed");
    }
  }

  function onInputChange(event: React.ChangeEvent<HTMLInputElement>) {
    const file = event.target.files?.[0];
    // Reset so selecting the same file again still fires change.
    event.target.value = "";
    if (file) void handleFile(file);
  }

  function handleRemove() {
    if (value?.previewUrl) URL.revokeObjectURL(value.previewUrl);
    onChange(null);
    setStatus("idle");
    setErrorKey(null);
    setProgress(0);
  }

  function openPicker() {
    if (!disabled) inputRef.current?.click();
  }

  const isUploading = status === "uploading";
  const isError = status === "error";

  return (
    <div className={cn("flex flex-col gap-2", className)}>
      <input
        ref={inputRef}
        id={id}
        type="file"
        accept={accept}
        className="sr-only"
        disabled={disabled}
        onChange={onInputChange}
      />

      {/* Uploaded — preview + remove */}
      {value && !isUploading ? (
        <div className="flex items-center gap-3 rounded-[12px] border border-input bg-card p-3">
          {variant === "image" && value.previewUrl ? (
            // eslint-disable-next-line @next/next/no-img-element
            <img
              src={value.previewUrl}
              alt=""
              className="size-12 shrink-0 rounded-[8px] object-cover"
            />
          ) : (
            <span className="flex size-12 shrink-0 items-center justify-center rounded-[8px] bg-information-light text-primary-sky-blue">
              <FileTextIcon className="size-5" />
            </span>
          )}
          <Typography
            as="span"
            variant="body"
            className="min-w-0 flex-1 truncate"
          >
            {value.fileName}
          </Typography>
          {value.previewUrl && (
            <Button
              type="button"
              variant="ghost"
              size="icon-sm"
              onClick={() =>
                window.open(value.previewUrl, "_blank", "noopener,noreferrer")
              }
              aria-label={t("fileUpload.view")}
            >
              <EyeIcon className="size-4" />
            </Button>
          )}
          <Button
            type="button"
            variant="ghost"
            size="icon-sm"
            onClick={handleRemove}
            disabled={disabled}
            aria-label={t("fileUpload.remove")}
          >
            <XIcon className="size-4" />
          </Button>
        </div>
      ) : isUploading ? (
        /* Uploading — progress */
        <div className="flex flex-col gap-2 rounded-[12px] border border-input bg-card p-3">
          <div className="flex items-center gap-2">
            <Loader2 className="size-4 shrink-0 animate-spin text-primary-blue" />
            <Typography
              as="span"
              variant="caption"
              color="secondary"
              className="min-w-0 flex-1 truncate"
            >
              {t("fileUpload.uploading")}
            </Typography>
            <Typography
              as="span"
              variant="number"
              className="text-secondary-text"
            >
              {`${progress}%`}
            </Typography>
          </div>
          <div className="h-1.5 w-full overflow-hidden rounded-full bg-muted">
            <div
              className="h-full rounded-full bg-primary-blue transition-[width]"
              style={{ width: `${progress}%` }}
            />
          </div>
        </div>
      ) : (
        /* Idle — drop zone */
        <button
          type="button"
          onClick={openPicker}
          disabled={disabled}
          onDragOver={(e) => {
            e.preventDefault();
            setIsDragging(true);
          }}
          onDragLeave={() => setIsDragging(false)}
          onDrop={(e) => {
            e.preventDefault();
            setIsDragging(false);
            const file = e.dataTransfer.files?.[0];
            if (file) void handleFile(file);
          }}
          className={cn(
            "flex flex-col items-center justify-center gap-1.5 rounded-[12px] border border-dashed p-6 text-center transition-colors",
            "hover:border-primary-blue disabled:pointer-events-none disabled:opacity-60",
            isError ? "border-destructive" : "border-input",
            isDragging && "border-primary-blue bg-background",
          )}
        >
          <span className="flex size-10 items-center justify-center rounded-full bg-information-light text-primary-sky-blue">
            <UploadCloudIcon className="size-5" />
          </span>
          <Typography as="span" variant="body" className="font-medium">
            {t("fileUpload.browse")}
          </Typography>
          <Typography as="span" variant="caption" color="secondary">
            {t("fileUpload.dropHint")}
          </Typography>
          {hint && (
            <Typography as="span" variant="caption" color="secondary">
              {hint}
            </Typography>
          )}
        </button>
      )}

      {isError && errorKey && (
        <div className="flex items-center justify-between gap-2">
          <Typography as="span" variant="caption" className="text-destructive">
            {t(errorKey)}
          </Typography>
          <Button
            type="button"
            variant="ghost"
            size="sm"
            onClick={openPicker}
            disabled={disabled}
            className="gap-1.5"
          >
            <RotateCwIcon className="size-3.5" />
            {t("fileUpload.retry")}
          </Button>
        </div>
      )}
    </div>
  );
}

export { FileUpload };
export type { FileUploadProps };
