import * as React from 'react';
import { Contrast } from 'lucide-react';

import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';

const STORAGE_KEY = 'pig-demo:contrast';

/**
 * `localStorage` is not always readable. In a cross-origin iframe with third-
 * party storage blocked, and in Safari's private mode, the accessor itself
 * THROWS rather than returning null — so every access has to be wrapped, not
 * just null-checked. Unreadable storage means "off", never a crash.
 */
function readStored(): boolean {
  try {
    return window.localStorage.getItem(STORAGE_KEY) === 'high';
  } catch {
    return false;
  }
}

function writeStored(high: boolean): void {
  try {
    window.localStorage.setItem(STORAGE_KEY, high ? 'high' : 'normal');
  } catch {
    /* Preference is session-only in this context. The toggle still works. */
  }
}

/**
 * One value shared by every mounted toggle, not one `useState` each.
 *
 * This control renders in three places — the desktop bar, the mobile sheet and
 * the Play tab — and at most two are ever visible at once. With local state the
 * hidden one keeps a stale `aria-pressed`, so a visitor who toggles on the
 * board and then opens the menu is told the setting is off while the page is
 * plainly showing it on.
 */
let high = false;
let initialised = false;
const listeners = new Set<() => void>();

function applyToRoot(next: boolean): void {
  const root = document.documentElement;
  // Removed rather than set to "normal": the CSS keys off
  // `:root[data-contrast='high']`, and a leftover attribute makes the DOM lie
  // about which palette is actually applied.
  if (next) root.setAttribute('data-contrast', 'high');
  else root.removeAttribute('data-contrast');
}

function setHigh(next: boolean): void {
  high = next;
  applyToRoot(next);
  writeStored(next);
  for (const listener of listeners) listener();
}

function subscribe(listener: () => void): () => void {
  if (!initialised) {
    initialised = true;
    high = readStored();
    applyToRoot(high);
  }
  listeners.add(listener);
  return () => {
    listeners.delete(listener);
  };
}

function useContrast(): boolean {
  // The server snapshot is `false` so a prerendered page never claims a
  // preference it cannot know; the real value lands on the first subscribe.
  return React.useSyncExternalStore(
    subscribe,
    () => high,
    () => false,
  );
}

export interface ContrastToggleProps {
  className?: string;
  /**
   * Print "High contrast" beside the icon. This is the form the Play tab and
   * the mobile sheet use: a colour-blind visitor looking at the board finds the
   * mode by its name, not by guessing what a half-filled circle means.
   */
  labelled?: boolean;
}

export function ContrastToggle({ className, labelled = false }: ContrastToggleProps) {
  const isHigh = useContrast();
  const ariaLabel = isHigh ? 'High contrast tiles on. Turn off.' : 'High contrast tiles off. Turn on.';

  if (labelled) {
    return (
      <Button
        type="button"
        variant="outline"
        size="touch"
        // 44px below lg, 36px at lg — the same step as the icon form, so the
        // Play tab's row lines up with the ghost button beside it.
        className={cn(
          'justify-start lg:min-h-9',
          isHigh && 'border-accent-edge bg-accent-subtle text-accent-fg',
          className,
        )}
        aria-pressed={isHigh}
        aria-label={ariaLabel}
        onClick={() => setHigh(!isHigh)}
      >
        <Contrast aria-hidden="true" />
        <span className="text-ui">High contrast</span>
        {/* The state as a word as well as a fill, on the control that exists
            for people who cannot rely on the fill. */}
        <span className="font-mono text-label uppercase tracking-label text-muted">
          {isHigh ? 'on' : 'off'}
        </span>
      </Button>
    );
  }

  return (
    <Button
      type="button"
      variant="ghost"
      size="icon-touch"
      className={cn('lg:size-9', isHigh && 'bg-accent-subtle text-accent-fg', className)}
      aria-pressed={isHigh}
      aria-label={ariaLabel}
      title="High contrast tiles"
      onClick={() => setHigh(!isHigh)}
    >
      <Contrast aria-hidden="true" />
    </Button>
  );
}

/** The Play tab's form of the toggle. Same store, same state, with its name on it. */
export function LabelledContrastToggle({ className }: { className?: string }) {
  return <ContrastToggle labelled className={className} />;
}
