"use client";

import {
  ActionIcon,
  Badge,
  Box,
  Button,
  CopyButton,
  FileButton,
  Group,
  Loader,
  Image,
  Paper,
  ScrollArea,
  SimpleGrid,
  Text,
  Tooltip,
} from "@mantine/core";
import { notifications } from "@mantine/notifications";
import {
  IconAlertTriangle,
  IconCheck,
  IconCopy,
  IconPhotoUp,
  IconPlus,
  IconRefresh,
} from "@tabler/icons-react";
import React, { useCallback, useEffect, useState } from "react";
import { scanEDMImages, uploadEDMAsset } from "@/lib/features/edm/action";
import type { ImageScanReport } from "@/lib/features/edm/types";

interface Props {
  html: string;
  /** Applies the rewritten html back into the open tab. */
  onHtmlChange: (html: string) => void;
  canUpload: boolean;
  /** Places uploads under this folder's prefix in the bucket. */
  folderId?: string | null;
}

/**
 * Media panel for the template currently open.
 *
 * Two jobs: list the images the html actually references, and let a broken
 * reference be fixed on the spot. A relative <img src> with no file behind it
 * is not a warning — it is a hole in every inbox that receives the email — so
 * it needs fixing where the editing happens, not only in the import wizard.
 */
export function WorkspaceMedia({ html, onHtmlChange, canUpload, folderId }: Props) {
  const [report, setReport] = useState<ImageScanReport | null>(null);
  const [scanning, setScanning] = useState(false);
  const [busyFor, setBusyFor] = useState<string | null>(null);
  const [bulkBusy, setBulkBusy] = useState(false);

  const scan = useCallback(async () => {
    if (!html.trim()) { setReport(null); return; }
    setScanning(true);
    try {
      const res = await scanEDMImages(html);
      if (res.success && res.data) setReport(res.data);
    } finally {
      setScanning(false);
    }
  }, [html]);

  // Debounced: this runs while someone is typing html, and a request per
  // keystroke would hammer the endpoint for no benefit.
  useEffect(() => {
    const t = setTimeout(() => void scan(), 700);
    return () => clearTimeout(t);
  }, [scan]);

  /**
   * Insert an <img> for an already-hosted image, just before </body>.
   *
   * Appending to the end of the string would put it after </html>, outside the
   * document, where mail clients discard it — it looks inserted in the editor
   * and never appears in the email.
   */
  const insertIntoBody = (url: string) => {
    const tag = `<img src="${url}" alt="" style="display:block;max-width:100%;" />`;
    const body = html.search(/<\/body\s*>/i);
    if (body !== -1) {
      onHtmlChange(`${html.slice(0, body)}${tag}\n${html.slice(body)}`);
    } else {
      const doc = html.search(/<\/html\s*>/i);
      onHtmlChange(
        doc !== -1
          ? `${html.slice(0, doc)}${tag}\n${html.slice(doc)}`
          : `${html}\n${tag}`,
      );
    }
    notifications.show({ color: "green", message: "Image inserted — save to keep it" });
  };

  /** Replace one broken reference everywhere it appears. */
  const applyFix = (ref: string, url: string) => {
    onHtmlChange(html.split(ref).join(url));
  };

  const fixOne = async (ref: string, file: File | null) => {
    if (!file) return;
    setBusyFor(ref);
    try {
      const res = await uploadEDMAsset(file, folderId);
      if (!res.success || !res.data) {
        notifications.show({ color: "red", message: res.message || "Upload failed" });
        return;
      }
      applyFix(ref, res.data.url);
      notifications.show({ color: "green", message: `Replaced ${ref}` });
    } finally {
      setBusyFor(null);
    }
  };

  /**
   * Bulk fix by filename, same rule the import wizard uses: match on basename,
   * case-insensitively, because "images/Hero.PNG" and a picked "hero.png" are
   * the same file to everyone except a string comparison.
   */
  const fixMany = async (files: File[]) => {
    const missing = report?.unresolved ?? [];
    if (files.length === 0 || missing.length === 0) return;

    const wanted = new Map<string, string[]>();
    for (const r of missing) {
      const base = (r.url.split(/[?#]/)[0] ?? "").split("/").pop()?.toLowerCase() ?? "";
      if (base) wanted.set(base, [...(wanted.get(base) ?? []), r.url]);
    }

    setBulkBusy(true);
    let next = html;
    let filled = 0;
    const unmatched: string[] = [];

    try {
      for (const file of files) {
        const targets = wanted.get(file.name.toLowerCase());
        if (!targets?.length) { unmatched.push(file.name); continue; }

        const res = await uploadEDMAsset(file, folderId);
        if (!res.success || !res.data) continue;
        for (const ref of targets) {
          next = next.split(ref).join(res.data.url);
          filled++;
        }
      }
      if (next !== html) onHtmlChange(next);

      notifications.show({
        color: filled > 0 ? "green" : "orange",
        message:
          `Replaced ${filled} of ${missing.length} broken reference(s).` +
          (unmatched.length ? ` ${unmatched.length} file(s) matched nothing.` : ""),
      });
    } finally {
      setBulkBusy(false);
    }
  };

  const missing = report?.unresolved ?? [];
  const external = report?.external ?? [];

  return (
    <Box style={{ borderTop: "1px solid var(--mantine-color-gray-3)" }}>
      <Group justify="space-between" px="sm" py={6}>
        <Group gap={6}>
          <Text size="xs" fw={600} c="dimmed">MEDIA</Text>
          {scanning && <Loader size={10} />}
          {missing.length > 0 && (
            <Badge size="xs" color="red" variant="light">{missing.length} missing</Badge>
          )}
          {external.length > 0 && (
            <Badge size="xs" color="teal" variant="light">{external.length} hosted</Badge>
          )}
        </Group>
        <Group gap={4}>
          {canUpload && missing.length > 0 && (
            <FileButton multiple onChange={fixMany} accept="image/*">
              {(props) => (
                <Button {...props} size="compact-xs" variant="light" loading={bulkBusy}
                  leftSection={<IconPhotoUp size={12} />}>
                  Upload all missing
                </Button>
              )}
            </FileButton>
          )}
          <Tooltip label="Re-scan">
            <ActionIcon size="sm" variant="subtle" onClick={scan}>
              <IconRefresh size={13} />
            </ActionIcon>
          </Tooltip>
        </Group>
      </Group>

      <ScrollArea.Autosize mah={170}>
        <Box px="sm" pb="sm">
          {missing.map((r) => (
            <Group key={r.url} gap={6} py={3} wrap="nowrap">
              <IconAlertTriangle size={13} color="var(--mantine-color-red-6)" />
              <Text size="xs" ff="monospace" style={{ flex: 1 }} truncate>{r.url}</Text>
              <Badge size="xs" variant="light" color="gray">{r.kind}</Badge>
              {canUpload && (
                busyFor === r.url ? (
                  <Loader size={12} />
                ) : (
                  <FileButton onChange={(f) => fixOne(r.url, f)} accept="image/*">
                    {(props) => (
                      <Button {...props} size="compact-xs" variant="subtle">Upload</Button>
                    )}
                  </FileButton>
                )
              )}
            </Group>
          ))}

          {/* Hosted images as thumbnails. A filename tells you nothing about
              whether the right picture is in the email; the picture does. */}
          {external.length > 0 && (
            <SimpleGrid cols={{ base: 4, sm: 6, md: 8 }} spacing={6} mt={missing.length ? 8 : 0}>
              {external.map((url) => (
                <Tooltip key={url} label={url} multiline w={280} openDelay={400}>
                  <Paper withBorder radius="sm" p={2}>
                    <Box
                      style={{
                        height: 46,
                        display: "flex",
                        alignItems: "center",
                        justifyContent: "center",
                        backgroundColor: "var(--mantine-color-gray-1)",
                        borderRadius: 3,
                        overflow: "hidden",
                      }}
                    >
                      {/* A broken thumbnail is a real signal here: this is the
                          same URL a mail client will fetch. */}
                      <Image src={url} h={46} fit="contain" alt="" />
                    </Box>
                    <Group gap={0} justify="center" mt={2}>
                      <Tooltip label="Insert into template">
                        <ActionIcon size="xs" variant="subtle" onClick={() => insertIntoBody(url)}>
                          <IconPlus size={11} />
                        </ActionIcon>
                      </Tooltip>
                      <CopyButton value={url}>
                        {({ copied, copy }) => (
                          <ActionIcon size="xs" variant="subtle" color={copied ? "teal" : "gray"} onClick={copy}>
                            {copied ? <IconCheck size={11} /> : <IconCopy size={11} />}
                          </ActionIcon>
                        )}
                      </CopyButton>
                    </Group>
                  </Paper>
                </Tooltip>
              ))}
            </SimpleGrid>
          )}

          {missing.length === 0 && external.length === 0 && !scanning && (
            <Text size="xs" c="dimmed" fs="italic">No images referenced.</Text>
          )}
        </Box>
      </ScrollArea.Autosize>
    </Box>
  );
}
