/*
 * F-B1 스마트티켓 회전 QR 위젯 — 바인딩 기기에서 30초 회전 토큰을 폴링·표시.
 * 실 QR 인코더 라이브러리 미도입(의존성 추가 금지) → 페이로드 해시 기반 결정적 매트릭스(시각적 회전 확인) +
 * 게이트가 소비하는 실 페이로드 텍스트(ticketCode|token)를 병기한다. 캡처 스크린샷은 30초 후 무효.
 * 자체 스타일(smartticket.css) — 공유 css 무수정.
 */
import { useEffect, useState } from 'react';
import { ticketApi, type RotatingToken } from './ticketApi';
import { ApiRequestError } from '../../api/client';
import './smartticket.css';

const GRID = 25;

/** 페이로드 문자열 → 결정적 비트 매트릭스(회전마다 달라져 시각적 회전을 확인). */
function matrixFor(payload: string): boolean[] {
  const cells: boolean[] = [];
  let h = 2166136261;
  for (let i = 0; i < payload.length; i++) {
    h ^= payload.charCodeAt(i);
    h = Math.imul(h, 16777619);
  }
  for (let i = 0; i < GRID * GRID; i++) {
    // 파인더 패턴(모서리 3곳)은 고정 — QR 룩.
    const r = Math.floor(i / GRID);
    const c = i % GRID;
    if (isFinder(r, c)) {
      cells.push(finderOn(r, c));
      continue;
    }
    h ^= i + 0x9e3779b9;
    h = Math.imul(h, 2654435761);
    cells.push(((h >>> (i % 31)) & 1) === 1);
  }
  return cells;
}

function isFinder(r: number, c: number): boolean {
  const inBox = (br: number, bc: number) => r >= br && r < br + 7 && c >= bc && c < bc + 7;
  return inBox(0, 0) || inBox(0, GRID - 7) || inBox(GRID - 7, 0);
}
function finderOn(r: number, c: number): boolean {
  const lr = r >= GRID - 7 ? r - (GRID - 7) : r;
  const lc = c >= GRID - 7 ? c - (GRID - 7) : c;
  const ring = lr === 0 || lr === 6 || lc === 0 || lc === 6;
  const core = lr >= 2 && lr <= 4 && lc >= 2 && lc <= 4;
  return ring || core;
}

interface Props {
  ticketCode: string;
  deviceId: string;
}

/** 활성화 완료된 스마트티켓의 회전 QR 표시(자동 폴링·카운트다운). */
export function RotatingTicketQr({ ticketCode, deviceId }: Props) {
  const [tok, setTok] = useState<RotatingToken | null>(null);
  const [err, setErr] = useState<string | null>(null);
  const [remaining, setRemaining] = useState(0);

  // 토큰 폴링 — 남은 시간에 맞춰 재요청(최소 5초).
  useEffect(() => {
    let alive = true;
    let timer: number | undefined;
    const fetchToken = async () => {
      try {
        const r = await ticketApi.smartToken(ticketCode, deviceId);
        if (!alive) return;
        setTok(r);
        setRemaining(r.remainingSeconds);
        setErr(null);
        timer = window.setTimeout(fetchToken, Math.max(5, r.remainingSeconds) * 1000);
      } catch (e) {
        if (!alive) return;
        setErr(
          e instanceof ApiRequestError && e.code === 'FORBIDDEN'
            ? '이 기기에서 발급할 수 없는 티켓입니다.'
            : '회전 QR을 불러오지 못했습니다.',
        );
        timer = window.setTimeout(fetchToken, 8000);
      }
    };
    void fetchToken();
    return () => {
      alive = false;
      if (timer) window.clearTimeout(timer);
    };
  }, [ticketCode, deviceId]);

  // 1초 카운트다운(표시용).
  useEffect(() => {
    const id = window.setInterval(() => setRemaining((s) => (s > 0 ? s - 1 : 0)), 1000);
    return () => window.clearInterval(id);
  }, [tok?.token]);

  if (err) {
    return <p className="kx-smtk__err" role="alert">{err}</p>;
  }
  if (!tok) {
    return <div className="kx-smtk__loading" aria-live="polite">회전 QR 준비 중…</div>;
  }

  const cells = matrixFor(tok.qrPayload);
  const cell = 100 / GRID;
  const total = tok.windowSeconds || 30;
  const pct = Math.max(0, Math.min(100, (remaining / total) * 100));

  return (
    <div className="kx-smtk">
      <div className="kx-smtk__qrwrap" aria-label="회전 QR 코드">
        <svg viewBox="0 0 100 100" className="kx-smtk__qr" role="img" aria-hidden="true">
          <rect x="0" y="0" width="100" height="100" fill="#fff" />
          {cells.map((on, i) =>
            on ? (
              <rect
                key={i}
                x={(i % GRID) * cell}
                y={Math.floor(i / GRID) * cell}
                width={cell}
                height={cell}
                fill="#101828"
              />
            ) : null,
          )}
        </svg>
      </div>
      <div className="kx-smtk__meta">
        <span className="kx-smtk__code">{tok.token}</span>
        <div className="kx-smtk__count">
          <span className="kx-smtk__count-bar" style={{ width: `${pct}%` }} />
        </div>
        <span className="kx-smtk__hint">{remaining}초 후 자동 갱신 · 캡처본은 무효화됩니다</span>
      </div>
    </div>
  );
}
