/*
 * requests/requests.jsx — "בקשות תמונה" — the photo-request tile.
 *
 * The microphone (formerly at the bottom of /config/) lives here now: speak
 * or type the photo you wish existed, and it's captured as a *request*. This
 * app has no backend, so we only CAPTURE the request (a future backend turns
 * it into an illustration). See issue #115 / #122.
 *
 * Each request is a card with:
 *   - a big PHOTO (when ready) or a pending placeholder (not ready yet),
 *   - a REPLAY button that reads the request back aloud (SpeechSynthesis),
 *   - press-to-reveal TEXT (tap the card to see the transcript),
 *   - a status: pending ⏳ vs ready ✓.
 *
 * Two MOCK requests (one pending, one ready) are always shown as a live
 * design reference for the two states; real dictated/typed requests are read
 * from localStorage and shown above them, newest first.
 *
 * Self-contained: reads shared/sounds.js, writes only its own localStorage
 * key — cannot affect the rest of the suite or its tests.
 */

const REQUESTS_KEY = "aleph-bet.photo-requests.v1";

// Recording cap. Long enough for a child's description plus a parent's
// follow-up — "a fire truck… a big red one with a ladder" — but short enough
// that nobody is left holding an open mic. The user asked for 10–20s; 18s
// sits near the top of that range, leaving room for a two-person sentence
// without running away. A depleting bar shows the time vanishing. (issue #128)
const RECORD_LIMIT_MS = 18000;

// Demo cards illustrating the two states. Marked isMock so they are never
// written to storage and can be told apart in tests.
const MOCKS = [
  {
    id: "mock-ready",
    text: "חתול כתום",
    status: "ready",
    photo: "assets/images/hatul.svg",
    at: Date.now() - 1000 * 60 * 60 * 3,
    isMock: true,
  },
  {
    id: "mock-pending",
    text: "מכונית כיבוי אדומה",
    status: "pending",
    photo: null,
    at: Date.now() - 1000 * 60 * 8,
    isMock: true,
  },
];

function readRequests() {
  try {
    const raw = window.localStorage.getItem(REQUESTS_KEY);
    const arr = raw ? JSON.parse(raw) : [];
    return Array.isArray(arr) ? arr : [];
  } catch (_) { return []; }
}
function writeRequests(arr) {
  try { window.localStorage.setItem(REQUESTS_KEY, JSON.stringify(arr)); } catch (_) {}
}

function sound(name) { if (window.SOUNDS) window.SOUNDS.play(name); }

// "Replay the message": with no recorded audio (the Web Speech API gives us
// text, not sound), the honest replay is to read the captured text back via
// speech synthesis. Best-effort — silently no-ops where unsupported.
function speak(text) {
  try {
    const synth = window.speechSynthesis;
    if (!synth) return false;
    synth.cancel();
    const u = new SpeechSynthesisUtterance(text);
    u.lang = "he-IL";
    synth.speak(u);
    return true;
  } catch (_) { return false; }
}

// ── Icons ─────────────────────────────────────────────────────────────────
function MicIcon() {
  return (
    <svg viewBox="0 0 24 24" aria-hidden="true">
      <rect x="9" y="2.5" width="6" height="11" rx="3" fill="currentColor" />
      <path d="M5.5 11a6.5 6.5 0 0 0 13 0" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
      <path d="M12 17.5V21M8.5 21h7" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
    </svg>
  );
}
function ReplayIcon() {
  // Speaker with sound waves — "hear it again".
  return (
    <svg viewBox="0 0 24 24" aria-hidden="true">
      <path d="M4 9v6h4l5 4V5L8 9H4z" fill="currentColor" />
      <path d="M16 8.5a5 5 0 0 1 0 7M18.5 6a8.5 8.5 0 0 1 0 12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
    </svg>
  );
}

// ── One request card ────────────────────────────────────────────────────────
// Press the card to reveal/hide the transcript text; the replay button
// (separate hit target) reads the request aloud.
function RequestCard({ req }) {
  const [showText, setShowText] = React.useState(false);
  const ready = req.status === "ready";
  return (
    <div className="rq-card" data-status={req.status} data-mock={req.isMock ? "1" : "0"}>
      <button
        type="button"
        className="rq-card__main"
        aria-label={showText ? req.text : "הצג את הבקשה"}
        aria-expanded={showText}
        onClick={() => { setShowText((s) => !s); sound("tap"); }}
      >
        <span className="rq-card__photo">
          {ready && req.photo ? (
            <img src={req.photo} alt="" aria-hidden="true" />
          ) : (
            <span className="rq-card__pending" aria-hidden="true">
              {/* hourglass — "not ready yet" */}
              <svg viewBox="0 0 24 24">
                <path d="M7 3h10M7 21h10M7 3c0 5 5 5 5 9s-5 4-5 9M17 3c0 5-5 5-5 9s5 4 5 9"
                      fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
              </svg>
            </span>
          )}
          <span className={"rq-card__badge rq-card__badge--" + req.status} aria-hidden="true">
            {ready ? (
              <svg viewBox="0 0 24 24"><path d="M5 13l4 4L19 7" fill="none" stroke="#fff" strokeWidth="3.2" strokeLinecap="round" strokeLinejoin="round" /></svg>
            ) : (
              <svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9" fill="none" stroke="#fff" strokeWidth="2" /><path d="M12 7v5l3 2" fill="none" stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /></svg>
            )}
          </span>
        </span>

        {/* Transcript revealed on press. Hidden by default ("press and see"). */}
        <span className="rq-card__text" data-show={showText ? "1" : "0"} dir="rtl">
          {req.text}
        </span>
      </button>

      <button
        type="button"
        className="rq-card__replay"
        aria-label="השמע שוב"
        onClick={() => { speak(req.text); sound("tap"); }}
      >
        <ReplayIcon />
      </button>
    </div>
  );
}

function App() {
  const [requests, setRequests] = React.useState(readRequests);
  const [requestText, setRequestText] = React.useState("");
  const [listening, setListening] = React.useState(false);
  const recRef = React.useRef(null);
  const inputRef = React.useRef(null);
  const timerRef = React.useRef(null);   // auto-stop at RECORD_LIMIT_MS
  const baseRef = React.useRef("");       // text already captured before this take

  const clearTimer = () => {
    if (timerRef.current) { clearTimeout(timerRef.current); timerRef.current = null; }
  };

  const stopRecording = () => {
    clearTimer();
    if (recRef.current) { try { recRef.current.stop(); } catch (_) {} }
    setListening(false);
  };

  // Short press → start dictation. We can't pop iOS's system dictation
  // programmatically, but the Web Speech API does in-browser speech→text on
  // iOS Safari 14.5+ / Chrome. `continuous` keeps the mic open across pauses
  // so a child's sentence + a parent's follow-up are captured as one take,
  // until the 18s cap (or a manual stop). Where the API is missing we focus
  // the textarea so the keyboard's own dictation mic is one tap away.
  const startRecording = (fresh) => {
    const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
    if (!SR) { if (inputRef.current) inputRef.current.focus(); return; }
    stopRecording();
    if (fresh) setRequestText("");
    baseRef.current = fresh ? "" : (requestText ? requestText.trim() : "");
    const r = new SR();
    r.lang = "he-IL";
    r.interimResults = true;
    r.continuous = true;
    r.onresult = (e) => {
      let txt = "";
      for (let i = 0; i < e.results.length; i++) txt += e.results[i][0].transcript;
      const base = baseRef.current;
      setRequestText((base ? base + " " : "") + txt);
    };
    r.onend = () => { clearTimer(); setListening(false); };
    r.onerror = () => { clearTimer(); setListening(false); };
    recRef.current = r;
    setListening(true);
    try { r.start(); } catch (_) { setListening(false); }
    timerRef.current = setTimeout(stopRecording, RECORD_LIMIT_MS);
  };

  // Cancel: stop + discard the take. (The "redo" button is gone — pressing
  // the mic again was the same action; now the mic only ever STARTS, while
  // send/cancel end a take.)
  const cancel = () => { stopRecording(); setRequestText(""); sound("tap"); };

  // Always stop the mic + timer if the screen unmounts.
  React.useEffect(() => () => stopRecording(), []);

  // "Release" the request: commit whatever is in the field (typed or dictated)
  // as a new pending card.
  const submitRequest = (e) => {
    if (e) e.preventDefault();
    stopRecording();
    const t = requestText.trim();
    if (!t) return;
    const next = requests.concat([{ id: "r" + Date.now(), text: t, status: "pending", photo: null, at: Date.now() }]);
    setRequests(next);
    writeRequests(next);
    setRequestText("");
    sound("correct");
  };

  const hasText = requestText.trim().length > 0;
  // A "take" is in progress once the mic is pressed (recording) or there's
  // text to act on. While active the mic is hidden and send/cancel take over.
  const active = listening || hasText;

  // Newest user requests first, then the two mock demo cards.
  const cards = requests.slice().reverse().concat(MOCKS);

  return (
    <div className="app app--requests">
      <div className="header header--bare">
        {/* The אותיות brand mark — same single artifact as the games' Crown
            (this tool doesn't load game-shared.jsx, so it references it directly). */}
        <a className="crown" href="index.html" aria-label="חזרה לעמוד הבית"><img className="crown-mark" src="assets/brand/mark.svg" alt="" aria-hidden="true" /></a>
      </div>

      {/* Speak (or type) the photo you want. The big mic is the only control
          until a take begins; once you press it (or type), the mic gives way
          to SEND (green, release) and CANCEL (red, discard). Multi-line so a
          whole little conversation fits. */}
      <form className="rq-mic-row" data-recording={listening ? "1" : "0"} onSubmit={submitRequest}>
        <textarea
          ref={inputRef}
          className="rq-input"
          value={requestText}
          onChange={(e) => setRequestText(e.target.value)}
          aria-label="בקשת תמונה"
          rows={3}
          dir="rtl"
        />

        {/* Vanishing-time indicator — drains over RECORD_LIMIT_MS while live. */}
        <div className="rq-timer" data-on={listening ? "1" : "0"} aria-hidden="true">
          <div
            className="rq-timer__fill"
            key={listening ? "run" : "idle"}
            style={{ animationDuration: RECORD_LIMIT_MS + "ms" }}
          />
        </div>

        <div className="rq-controls" data-active={active ? "1" : "0"}>
          {active ? (
            <React.Fragment>
              {/* cancel — discard the take (smaller, red X, per the
                  continuation-button convention: stop = smaller + red) */}
              <button
                type="button"
                className="rq-ctl rq-ctl--cancel"
                onClick={cancel}
                aria-label="בטל"
              >
                <svg viewBox="0 0 24 24" aria-hidden="true">
                  <path d="M6 6l12 12M18 6L6 18" fill="none" stroke="currentColor" strokeWidth="2.6" strokeLinecap="round" />
                </svg>
              </button>

              {/* send / release — commit the request (bigger, green) */}
              <button
                type="submit"
                className="rq-ctl rq-ctl--send rq-ctl--lg"
                disabled={!hasText}
                aria-label="שלח בקשה"
              >
                <svg viewBox="0 0 24 24" aria-hidden="true">
                  <path d="M4 12l15-7-5 7 5 7-15-7z" fill="currentColor" />
                </svg>
              </button>
            </React.Fragment>
          ) : (
            // idle — the big mic is the only thing on screen; short-press to record
            <button
              type="button"
              className="rq-mic"
              onClick={() => startRecording(false)}
              aria-label="דבר/י כדי לבקש תמונה"
            >
              <MicIcon />
            </button>
          )}
        </div>
      </form>

      <div className="rq-list">
        {cards.map((req) => <RequestCard key={req.id} req={req} />)}
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
