/**
 * The 16:9 area a video card leads with.
 *
 * PIG-hosted videos carry a real frame, cut from the clip itself and named
 * after the clip's own content hash, so it cannot go stale against what it
 * claims to show.
 *
 * Everything else falls back to a generated poster, and deliberately: nothing
 * renders a frame of a Cap embed without loading the embed, and loading nine
 * embeds to decorate a grid is how a page becomes unusable on a phone. The
 * generated version is a gradient picked deterministically from the resource
 * id — deterministic, not random, because a card that re-tints on every render
 * reads as a bug and destroys the sense that these are distinct objects.
 *
 * The fallback is also the error path. A poster is asserted by the resolver
 * rather than verified on disk, so a 404 here is an ordinary condition and must
 * degrade to the gradient rather than to a broken-image glyph.
 */
import { useEffect, useState } from 'react';
import { BookOpen, LineChart, MonitorPlay, Play } from 'lucide-react';
import type { LearnTrack } from '@pig/core';
import { cn } from '@/components/ui';

/**
 * Every tint is a pair of semantic tokens, so the whole set re-tints with the
 * user's accent and inverts correctly in dark mode without a second palette.
 */
const TINTS = [
  'from-accent-subtle via-surface-2 to-surface',
  'from-surface-2 via-accent-subtle to-surface',
  'from-surface via-surface-2 to-accent-subtle',
  'from-accent-subtle via-surface to-surface-2',
] as const;

const TRACK_GLYPHS: Record<LearnTrack, typeof Play> = {
  supply: LineChart,
  demand: BookOpen,
  platform: MonitorPlay,
};

function tintFor(seed: string): string {
  let hash = 0;
  for (let index = 0; index < seed.length; index += 1) {
    hash = (hash * 31 + seed.charCodeAt(index)) % 100_000;
  }
  return TINTS[hash % TINTS.length] as string;
}

export function LearnPoster({
  seed,
  track,
  duration,
  poster,
  alt,
  size = 'card',
  className,
}: {
  seed: string;
  track: LearnTrack;
  duration: string | null;
  /** A real frame, when the video is one PIG serves itself. */
  poster?: string | null;
  /** Only used when a real frame is shown; the generated poster is decorative. */
  alt?: string;
  /** `row` drops the ornament and shrinks the play button for a list thumbnail. */
  size?: 'card' | 'row';
  className?: string;
}) {
  const Glyph = TRACK_GLYPHS[track];
  const compact = size === 'row';

  const [imageFailed, setImageFailed] = useState(false);
  // Reset when the card is reused for a different resource, or one missing
  // poster would suppress the next card's working one.
  useEffect(() => setImageFailed(false), [poster]);
  const showImage = Boolean(poster) && !imageFailed;

  return (
    <div
      className={cn(
        'relative aspect-video w-full min-w-0 overflow-hidden bg-gradient-to-br',
        tintFor(seed),
        className,
      )}
    >
      {showImage ? (
        <>
          <img
            src={poster as string}
            alt={alt ?? ''}
            loading="lazy"
            decoding="async"
            className="absolute inset-0 size-full object-cover object-top"
            onError={() => setImageFailed(true)}
          />
          {/*
            A screenshot is mostly near-white, and the play button and duration
            badge have to stay legible on top of it in both themes. A scrim at
            the corners costs nothing and removes the need to restyle either
            control per-poster.
          */}
          <div
            className="absolute inset-0 bg-gradient-to-t from-black/35 via-transparent to-black/10"
            aria-hidden
          />
        </>
      ) : null}

      {/*
        Texture, so a generated poster reads as an image rather than as a card
        that failed to load. All three layers are the palette's own tokens at
        low alpha, which is what keeps them legible in both themes without a
        second set of values for dark.
      */}
      {showImage ? null : <div
        className="absolute inset-0 bg-[repeating-linear-gradient(135deg,hsl(var(--fg)/0.04)_0px,hsl(var(--fg)/0.04)_1px,transparent_1px,transparent_10px)]"
        aria-hidden
      />}
      {showImage ? null : (
        <div
          className="absolute inset-0 bg-[radial-gradient(circle_at_28%_18%,hsl(var(--surface)/0.8),transparent_62%)]"
          aria-hidden
        />
      )}
      {/* The track's glyph, at card size only — at thumbnail size it collides
          with the play button and reads as a second, broken control. */}
      {compact || showImage ? null : (
        <Glyph
          className="absolute -bottom-6 -right-4 size-32 text-fg/[0.06]"
          strokeWidth={1.25}
          aria-hidden
        />
      )}

      <div className="absolute inset-0 flex items-center justify-center">
        <span
          className={cn(
            'inline-flex items-center justify-center rounded-full border border-border',
            'bg-surface/85 text-fg shadow-sm backdrop-blur-sm',
            'transition-transform duration-200 group-hover:scale-105 group-focus-visible:scale-105',
            compact ? 'size-9' : 'size-14',
          )}
          aria-hidden
        >
          <Play className={compact ? 'size-4' : 'size-6'} fill="currentColor" strokeWidth={0} />
        </span>
      </div>

      {duration ? (
        <span
          className={cn(
            'nums absolute rounded-md bg-fg/85 px-1.5 py-0.5 text-xs font-medium text-bg',
            compact ? 'bottom-1 right-1' : 'bottom-2 right-2',
          )}
        >
          {duration}
        </span>
      ) : null}
    </div>
  );
}
