import type { Asset } from './shared';
import { useCallback, useEffect, useState } from 'react';
import { useSearchParams } from 'react-router';
import {
  Archive,
  ArchiveRestore,
  ChevronLeft,
  ChevronRight,
  Download,
  Heart,
  Info,
  Trash2,
  Undo2,
  X,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { ASSET_PARAM, originalUrl, thumbUrl, videoUrl } from './shared';
import { AssetInfoPanel } from './AssetInfoPanel';
import { useAsset } from './usePhotosData';
import { useAssetActions } from './useAssetActions';

// The lightbox. Which asset is open lives in `?asset=<id>`, so it is linkable, survives a reload, and closes
// with the browser's back button — the rule from docs/navigation-audit.md applied to the one piece of this
// screen that most wants to be a modal with a useState behind it.

type AssetViewerProps = {
  /** Every asset currently on screen, in display order — this is what prev/next walk. */
  ids: string[];
  token: string | null;
  /** Trash shows restore/delete-forever instead of archive/delete. */
  inTrash?: boolean;
};

export const AssetViewer = ({ ids, token, inTrash = false }: AssetViewerProps) => {
  const [params, setParams] = useSearchParams();
  const [showInfo, setShowInfo] = useState(false);
  const openId = params.get(ASSET_PARAM);
  const { data: asset } = useAsset(openId);

  const index = openId ? ids.indexOf(openId) : -1;
  const prev = index > 0 ? ids[index - 1] : undefined;
  const next = index >= 0 && index < ids.length - 1 ? ids[index + 1] : undefined;

  const goTo = useCallback(
    (id: string | undefined) => {
      if (!id) return;
      setParams(
        (current) => {
          const nextParams = new URLSearchParams(current);
          nextParams.set(ASSET_PARAM, id);
          return nextParams;
        },
        { replace: true },
      );
    },
    [setParams],
  );

  // Replace, not push: stepping through fifty photos shouldn't bury the grid fifty entries deep in history.
  // Opening the viewer pushed one entry, so one Back still returns to where you were.
  const close = useCallback(() => {
    setParams((current) => {
      const nextParams = new URLSearchParams(current);
      nextParams.delete(ASSET_PARAM);
      return nextParams;
    });
  }, [setParams]);

  useEffect(() => {
    if (!openId) return;

    const onKey = (ev: KeyboardEvent) => {
      if (ev.key === 'Escape') close();
      else if (ev.key === 'ArrowLeft') goTo(prev);
      else if (ev.key === 'ArrowRight') goTo(next);
      else if (ev.key === 'i') setShowInfo((value) => !value);
      else return;
      ev.preventDefault();
    };

    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [openId, prev, next, close, goTo]);

  if (!openId) return null;

  return (
    <div className="fixed inset-0 z-50 flex flex-col bg-black/95">
      <ViewerHeader
        asset={asset}
        id={openId}
        token={token}
        inTrash={inTrash}
        showInfo={showInfo}
        onToggleInfo={() => setShowInfo((value) => !value)}
        onClose={close}
        onAdvance={() => (next ? goTo(next) : close())}
      />

      <div className="flex min-h-0 flex-1">
        <div className="relative flex min-w-0 flex-1 items-center justify-center">
          <NavButton side="left" disabled={!prev} onClick={() => goTo(prev)} />

          {asset?.type === 'VIDEO' ? (
            <video
              key={openId}
              src={videoUrl(openId, token)}
              poster={thumbUrl(openId, token, 'preview')}
              controls
              autoPlay
              className="max-h-full max-w-full"
            />
          ) : (
            // `preview` rather than `original`: originals are frequently 30MB HEIC/RAW the browser may not even
            // decode, and Immich's own viewer shows the preview for the same reason. Download gets the original.
            <img
              key={openId}
              src={thumbUrl(openId, token, 'preview')}
              alt={asset?.originalFileName ?? ''}
              className="max-h-full max-w-full object-contain"
            />
          )}

          <NavButton side="right" disabled={!next} onClick={() => goTo(next)} />
        </div>

        {showInfo && (
          <aside className="w-80 shrink-0 overflow-y-auto border-l border-white/10 bg-background">
            <AssetInfoPanel asset={asset} token={token} />
          </aside>
        )}
      </div>
    </div>
  );
};

type NavButtonProps = { side: 'left' | 'right'; disabled: boolean; onClick: () => void };

const NavButton = ({ side, disabled, onClick }: NavButtonProps) => (
  <button
    type="button"
    disabled={disabled}
    onClick={onClick}
    aria-label={side === 'left' ? 'Previous' : 'Next'}
    className={`absolute ${side === 'left' ? 'left-2' : 'right-2'} z-10 rounded-full bg-black/40 p-2 text-white transition hover:bg-black/70 disabled:pointer-events-none disabled:opacity-0`}
  >
    {side === 'left' ? <ChevronLeft className="h-6 w-6" /> : <ChevronRight className="h-6 w-6" />}
  </button>
);

type ViewerHeaderProps = {
  asset: Asset | undefined;
  id: string;
  token: string | null;
  inTrash: boolean;
  showInfo: boolean;
  onToggleInfo: () => void;
  onClose: () => void;
  onAdvance: () => void;
};

const ViewerHeader = ({ asset, id, token, inTrash, showInfo, onToggleInfo, onClose, onAdvance }: ViewerHeaderProps) => {
  const { setFavorite, setArchived, trash, restore, deleteForever } = useAssetActions();
  const archived = asset?.visibility === 'archive';

  // Anything that removes the asset from the current view advances to the next one first — otherwise the
  // viewer is left pointing at an id that no longer exists in the grid behind it.
  const removeThen = (run: () => void) => {
    run();
    onAdvance();
  };

  return (
    <header className="flex items-center gap-1 px-2 py-2 text-white">
      <Button variant="ghost" size="icon" onClick={onClose} title="Close (Esc)" className="text-white">
        <X className="h-5 w-5" />
      </Button>

      <span className="ml-1 min-w-0 flex-1 truncate text-sm text-white/70">{asset?.originalFileName}</span>

      {inTrash ? (
        <>
          <Button
            variant="ghost"
            size="icon"
            title="Restore"
            className="text-white"
            onClick={() => removeThen(() => restore.mutate([id]))}
          >
            <Undo2 className="h-5 w-5" />
          </Button>
          <Button
            variant="ghost"
            size="icon"
            title="Delete permanently"
            className="text-white"
            onClick={() => removeThen(() => deleteForever.mutate([id]))}
          >
            <Trash2 className="h-5 w-5" />
          </Button>
        </>
      ) : (
        <>
          <Button
            variant="ghost"
            size="icon"
            title={asset?.isFavorite ? 'Remove from favorites' : 'Favorite'}
            className="text-white"
            onClick={() => setFavorite.mutate({ ids: [id], isFavorite: !asset?.isFavorite })}
          >
            <Heart className={`h-5 w-5 ${asset?.isFavorite ? 'fill-white' : ''}`} />
          </Button>
          <Button
            variant="ghost"
            size="icon"
            title={archived ? 'Unarchive' : 'Archive'}
            className="text-white"
            onClick={() => removeThen(() => setArchived.mutate({ ids: [id], archived: !archived }))}
          >
            {archived ? <ArchiveRestore className="h-5 w-5" /> : <Archive className="h-5 w-5" />}
          </Button>
          <Button
            variant="ghost"
            size="icon"
            title="Move to trash"
            className="text-white"
            onClick={() => removeThen(() => trash.mutate([id]))}
          >
            <Trash2 className="h-5 w-5" />
          </Button>
        </>
      )}

      <a
        href={originalUrl(id, token)}
        download={asset?.originalFileName ?? ''}
        title="Download original"
        className="inline-flex h-9 w-9 items-center justify-center rounded-md text-white hover:bg-white/10"
      >
        <Download className="h-5 w-5" />
      </a>

      <Button
        variant="ghost"
        size="icon"
        title="Info (i)"
        className={`text-white ${showInfo ? 'bg-white/15' : ''}`}
        onClick={onToggleInfo}
      >
        <Info className="h-5 w-5" />
      </Button>
    </header>
  );
};
