import { useEffect, useRef, useState } from 'react';
import { ChevronDown, ChevronRight, Clock, FolderTree, Pencil, Search, TriangleAlert } from 'lucide-react';
import { Button, Card, Dialog, IconButton, Input, Spinner } from '../components';
import {
  getImageFolders,
  messageFromImageFolderError,
  updateImageFolderDuration,
  type ImageFolder
} from '../api';
import { navigateToPath } from '../routing';

function searchLink(libraryFolderId: number) {
  navigateToPath(`/app/search?query=${encodeURIComponent(`library_folder_id:${libraryFolderId}`)}`);
}

function EditDurationDialog({
  folder,
  open,
  onClose,
  onSaved
}: {
  folder: ImageFolder;
  open: boolean;
  onClose: () => void;
  onSaved: (durationSeconds: number | null) => void;
}) {
  // The form is mounted only while open, so its useState initializers reset each time it opens
  // (no synchronous setState in an effect).
  return (
    <Dialog onClose={onClose} open={open} title={`Duration — ${folder.name}`} width={420}>
      {open ? <EditDurationForm folder={folder} onClose={onClose} onSaved={onSaved} /> : null}
    </Dialog>
  );
}

function EditDurationForm({
  folder,
  onClose,
  onSaved
}: {
  folder: ImageFolder;
  onClose: () => void;
  onSaved: (durationSeconds: number | null) => void;
}) {
  const [value, setValue] = useState(() => (folder.durationSeconds != null ? String(folder.durationSeconds) : ''));
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const save = (clear: boolean) => {
    const parsed = clear ? null : Number(value);
    if (!clear && (!Number.isFinite(parsed) || (parsed ?? 0) <= 0)) {
      setError('Enter a duration greater than zero, or clear it.');
      return;
    }
    setBusy(true);
    setError(null);
    updateImageFolderDuration(folder.libraryFolderId, parsed)
      .then((result) => {
        onSaved(result.durationSeconds ?? null);
        onClose();
      })
      .catch((caught: unknown) => {
        setError(messageFromImageFolderError(caught, 'Unable to update duration'));
        setBusy(false);
      });
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-5, 10px)' }}>
      <Input
        label="Seconds per image"
        onChange={(event) => setValue(event.target.value)}
        placeholder="Inherited from parent"
        type="number"
        value={value}
      />
      {error ? (
        <div className="ctv-channels-error" role="alert">
          <TriangleAlert aria-hidden="true" size={15} />
          <span>{error}</span>
        </div>
      ) : null}
      <div style={{ display: 'flex', gap: 'var(--space-4, 8px)', alignItems: 'center' }}>
        <Button disabled={busy} onClick={() => save(true)} size="sm" variant="ghost">
          Clear
        </Button>
        <span className="ctv-channels-spacer" />
        <Button disabled={busy} onClick={onClose} size="sm" variant="ghost">
          Cancel
        </Button>
        <Button disabled={busy} onClick={() => save(false)} size="sm" variant="primary">
          Save
        </Button>
      </div>
    </div>
  );
}

function FolderRow({ folder, depth }: { folder: ImageFolder; depth: number }) {
  const [expanded, setExpanded] = useState(false);
  const [children, setChildren] = useState<ImageFolder[] | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [duration, setDuration] = useState<number | null>(folder.durationSeconds ?? null);
  const [editing, setEditing] = useState(false);
  const activeRef = useRef(true);

  useEffect(() => {
    activeRef.current = true;
    return () => {
      activeRef.current = false;
    };
  }, []);

  const loadChildren = () => {
    setLoading(true);
    setError(null);
    getImageFolders(folder.libraryFolderId)
      .then((result) => {
        if (activeRef.current) {
          setChildren(result);
          setLoading(false);
        }
      })
      .catch((caught: unknown) => {
        if (activeRef.current) {
          setError(messageFromImageFolderError(caught));
          setLoading(false);
        }
      });
  };

  const toggle = () => {
    if (folder.subfolderCount === 0) {
      return;
    }
    if (!expanded && children === null) {
      loadChildren();
    }
    setExpanded((current) => !current);
  };

  const hasChildren = folder.subfolderCount > 0;

  return (
    <div className="ctv-imgfolder">
      <div className="ctv-imgfolder-row" style={{ paddingLeft: `${depth * 20}px` }}>
        <IconButton
          disabled={!hasChildren}
          onClick={toggle}
          size="sm"
          title={hasChildren ? (expanded ? 'Collapse' : 'Expand') : 'No subfolders'}
        >
          {hasChildren ? (
            expanded ? (
              <ChevronDown aria-hidden="true" size={15} />
            ) : (
              <ChevronRight aria-hidden="true" size={15} />
            )
          ) : (
            <FolderTree aria-hidden="true" size={15} />
          )}
        </IconButton>
        <span className="ctv-imgfolder-name" title={folder.fullPath}>
          {folder.name}
        </span>
        <span className="ctv-imgfolder-meta">
          {folder.imageCount} image{folder.imageCount === 1 ? '' : 's'} · {folder.subfolderCount} subfolder
          {folder.subfolderCount === 1 ? '' : 's'}
        </span>
        <span className="ctv-imgfolder-duration">
          <Clock aria-hidden="true" size={13} />
          {duration != null ? `${duration}s` : 'inherited'}
        </span>
        <span className="ctv-channels-spacer" />
        <IconButton onClick={() => setEditing(true)} size="sm" title="Edit duration">
          <Pencil aria-hidden="true" size={14} />
        </IconButton>
        <IconButton onClick={() => searchLink(folder.libraryFolderId)} size="sm" title="Search this folder">
          <Search aria-hidden="true" size={14} />
        </IconButton>
      </div>
      {error ? (
        <div className="ctv-channels-error" role="alert" style={{ marginLeft: `${depth * 20 + 20}px` }}>
          <TriangleAlert aria-hidden="true" size={15} />
          <span>{error}</span>
        </div>
      ) : null}
      {expanded && loading ? (
        <div className="ctv-collections-loading" role="status" style={{ paddingLeft: `${depth * 20 + 20}px` }}>
          <Spinner size={16} />
          <span>Loading…</span>
        </div>
      ) : null}
      {expanded && children
        ? children.map((child) => (
            <FolderRow depth={depth + 1} folder={child} key={child.libraryFolderId} />
          ))
        : null}
      <EditDurationDialog
        folder={folder}
        onClose={() => setEditing(false)}
        onSaved={(next) => setDuration(next)}
        open={editing}
      />
    </div>
  );
}

export function ImageBrowserScreen() {
  const [folders, setFolders] = useState<ImageFolder[]>([]);
  const [status, setStatus] = useState<'loading' | 'error' | 'success'>('loading');
  const [error, setError] = useState<string | null>(null);
  const activeRef = useRef(true);

  useEffect(() => {
    activeRef.current = true;
    getImageFolders()
      .then((result) => {
        if (activeRef.current) {
          setFolders(result);
          setStatus('success');
        }
      })
      .catch((caught: unknown) => {
        if (activeRef.current) {
          setError(messageFromImageFolderError(caught));
          setStatus('error');
        }
      });
    return () => {
      activeRef.current = false;
    };
  }, []);

  return (
    <div className="ctv-collections">
      <div className="ctv-channels-actionbar">
        <Button
          onClick={() => navigateToPath('/app/media?kind=images')}
          size="sm"
          variant="ghost"
        >
          Browse images
        </Button>
      </div>
      {status === 'loading' ? (
        <div className="ctv-collections-loading" role="status">
          <Spinner size={18} />
          <span>Loading image folders…</span>
        </div>
      ) : status === 'error' ? (
        <div className="ctv-channels-error" role="alert">
          <TriangleAlert aria-hidden="true" size={15} />
          <span>{error}</span>
        </div>
      ) : folders.length === 0 ? (
        <Card>
          <div className="ctv-collections-empty">No image libraries found.</div>
        </Card>
      ) : (
        <Card>
          {folders.map((folder) => (
            <FolderRow depth={0} folder={folder} key={folder.libraryFolderId} />
          ))}
        </Card>
      )}
    </div>
  );
}
