// @design-system: domain/QrCodeCard

'use client';

import type { CSSProperties } from 'react';
import { useEffect, useRef, useState, useSyncExternalStore } from 'react';
import { cn } from '@/lib/cn';
import { Button } from '@/components/ui/primitives/Button';
import { Icon } from '@/components/ui/icons/Icon';
import {
  Dialog,
  DialogTrigger,
  DialogContent,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/overlays/Dialog';
import { useT } from '@/lib/i18n/react';
import { useLocale } from '@/lib/i18n/react';
import { formatDate } from '@/lib/format';

const MAX_TILT_DEG = 8;
const ORIENTATION_DIVISOR = 6;

type TiltState = {
  rotateX: number;
  rotateY: number;
};

const IDLE_TILT: TiltState = {
  rotateX: 0,
  rotateY: 0,
};

function clampTilt(value: number): number {
  return Math.max(-MAX_TILT_DEG, Math.min(MAX_TILT_DEG, value));
}

function subscribeReducedMotion(callback: () => void): () => void {
  if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return () => {};
  const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
  mediaQuery.addEventListener?.('change', callback);
  return () => mediaQuery.removeEventListener?.('change', callback);
}

function getReducedMotionSnapshot(): boolean {
  return typeof window !== 'undefined' && typeof window.matchMedia === 'function'
    ? window.matchMedia('(prefers-reduced-motion: reduce)').matches
    : false;
}

/** Props for QrCodeCard - large variant */
export interface QrCodeCardProps {
  /** PNG URL for the QR code. */
  qrPngUrl: string;
  /** ISO string for QR expiry. */
  expiryIso?: string;
  /** Additional class names. */
  className?: string;
}

/**
 * QrCodeCard - large QR code display for the redemption screen.
 *
 * @example
 * ```tsx
 * <QrCodeCard qrPngUrl={purchase.qrUrl} expiryIso={purchase.expiry} />
 * ```
 */
export function QrCodeCard({ qrPngUrl, expiryIso, className }: QrCodeCardProps) {
  const t = useT('domain_qr');
  const { locale } = useLocale();
  const cardRef = useRef<HTMLDivElement | null>(null);
  const [tilt, setTilt] = useState<TiltState>(IDLE_TILT);
  const reducedMotion = useSyncExternalStore(
    subscribeReducedMotion,
    getReducedMotionSnapshot,
    () => false,
  );

  useEffect(() => {
    if (reducedMotion) return;
    const card = cardRef.current;
    if (!card) return;

    const resetTilt = () => {
      setTilt(IDLE_TILT);
    };

    const handlePointerMove = (event: PointerEvent) => {
      const rect = card.getBoundingClientRect();
      const centerX = rect.left + rect.width / 2;
      const centerY = rect.top + rect.height / 2;
      const offsetX = (event.clientX - centerX) / (rect.width / 2 || 1);
      const offsetY = (event.clientY - centerY) / (rect.height / 2 || 1);
      setTilt({
        rotateX: clampTilt(offsetY * -MAX_TILT_DEG),
        rotateY: clampTilt(offsetX * MAX_TILT_DEG),
      });
    };

    const handleDeviceOrientation = (event: DeviceOrientationEvent) => {
      setTilt({
        rotateX: clampTilt(-((event.beta ?? 0) / ORIENTATION_DIVISOR)),
        rotateY: clampTilt((event.gamma ?? 0) / ORIENTATION_DIVISOR),
      });
    };

    card.addEventListener('pointermove', handlePointerMove);
    card.addEventListener('pointerleave', resetTilt);
    window.addEventListener('deviceorientation', handleDeviceOrientation);

    return () => {
      card.removeEventListener('pointermove', handlePointerMove);
      card.removeEventListener('pointerleave', resetTilt);
      window.removeEventListener('deviceorientation', handleDeviceOrientation);
    };
  }, [reducedMotion]);

  return (
    <div
      ref={cardRef}
      data-testid="voucher-card-tilt"
      className={cn(
        'bg-surface-base border-border-default flex flex-col items-center gap-4 rounded-xl border p-6 shadow-md motion-safe:transition-transform motion-safe:duration-[var(--duration-fast)] motion-safe:ease-out motion-reduce:transform-none',
        className,
      )}
      style={
        reducedMotion
          ? undefined
          : ({
              '--voucher-tilt-x': `${tilt.rotateX}deg`,
              '--voucher-tilt-y': `${tilt.rotateY}deg`,
              transform:
                'perspective(var(--qr-card-perspective)) rotateX(var(--voucher-tilt-x)) rotateY(var(--voucher-tilt-y))',
            } as CSSProperties)
      }
    >
      <img
        src={qrPngUrl}
        alt={t('qr_label')}
        width={224}
        height={224}
        className="h-56 w-56 object-contain"
        loading="lazy"
        data-testid="voucher-card-qr"
      />
      {expiryIso && (
        <p className="text-text-muted text-xs" data-testid="voucher-card-expiry">
          {t('expires')}: {formatDate(expiryIso, locale)}
        </p>
      )}
    </div>
  );
}

// ─── QrButton - inline button that opens the QR in a dialog ──────────────────

/** Props for QrButton */
export interface QrButtonProps {
  /** PNG URL for the QR code. */
  qrPngUrl: string;
  /** ISO string for QR expiry. */
  expiryIso?: string;
  /** Additional class names. */
  className?: string;
}

/**
 * QrButton - small inline "QR" button for use in ListRow.
 * Opens a Dialog with the full QR code.
 *
 * @example
 * ```tsx
 * <QrButton qrPngUrl={purchase.qrUrl} expiryIso={purchase.expiry} />
 * ```
 */
export function QrButton({ qrPngUrl, expiryIso, className }: QrButtonProps) {
  const t = useT('domain_qr');

  return (
    <Dialog>
      <DialogTrigger asChild>
        <Button
          variant="secondary"
          size="sm"
          className={className}
          iconStart={<Icon name="QrCode" size="sm" />}
        >
          {t('open_qr')}
        </Button>
      </DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>{t('qr_label')}</DialogTitle>
        </DialogHeader>
        <div className="flex justify-center py-4">
          <QrCodeCard qrPngUrl={qrPngUrl} expiryIso={expiryIso} />
        </div>
      </DialogContent>
    </Dialog>
  );
}
