/* Shared capture pattern.
 *
 * Used by Member / Class / Note. Implements modes 1-5 of the input area
 * (idle / recording / transcribing / summarising / review). Spec section 5.1.
 *
 * Caller supplies:
 *   - type: "member" | "class" | "note"
 *   - title: drawer header text
 *   - placeholder: input placeholder when empty
 *   - preChrome: rendered above the input (e.g. member picker)
 *   - postChrome: rendered below the input (e.g. follow-up toggle, tag chips)
 *   - extraEntryFields(): entry fields specific to this capture type, merged
 *     into the saved entry record.
 *   - onSaved(entry): fired after the entry persists to Firebase.
 *   - canSave(state): optional guard — defaults to "any text or transcript".
 */
(function () {
  const { useState, useRef, useEffect, useCallback } = React;
  const {
    Drawer,
    Button,
    TextArea,
    Icons,
    LoadingSpinner,
    showToast,
    tokens,
    fb,
    api,
    coach,
    recorder,
  } = window.CC;

  const MODE = {
    IDLE: "idle",
    RECORDING: "recording",
    TRANSCRIBING: "transcribing",
    SUMMARISING: "summarising",
    REVIEW: "review",
  };

  function CaptureFlow({
    open,
    onClose,
    type,
    title,
    placeholder = "Type or tap mic to record.",
    preChrome,
    postChrome,
    canSave,
    extraEntryFields,
    memberName, // used in summarisation prompt
    onSaved,
  }) {
    const [mode, setMode] = useState(MODE.IDLE);
    const [text, setText] = useState("");
    const [transcript, setTranscript] = useState("");
    const [audioBlob, setAudioBlob] = useState(null);
    const [audioPath, setAudioPath] = useState(null);
    const [audioDuration, setAudioDuration] = useState(0);
    const [elapsed, setElapsed] = useState(0);
    const [saving, setSaving] = useState(false);
    const recRef = useRef(null);

    // Reset on every open so a re-entry never inherits stale state.
    useEffect(() => {
      if (open) {
        setMode(MODE.IDLE);
        setText("");
        setTranscript("");
        setAudioBlob(null);
        setAudioPath(null);
        setAudioDuration(0);
        setElapsed(0);
        setSaving(false);
      }
    }, [open]);

    const startRecording = useCallback(async () => {
      const r = new recorder.Recorder({
        chunkMs: 5000,
        onElapsed: (s) => setElapsed(s),
      });
      try {
        await r.start();
      } catch (e) {
        if (e.code === "denied") {
          showToast("Mic access denied — type instead.");
        } else if (e.code === "unsupported") {
          showToast("Voice not supported on this device — type instead.");
        } else {
          showToast("Couldn’t start recording.");
        }
        return;
      }
      recRef.current = r;
      setMode(MODE.RECORDING);
      setElapsed(0);
    }, []);

    const stopRecording = useCallback(async () => {
      const r = recRef.current;
      if (!r) return;
      const result = await r.stop();
      recRef.current = null;
      setAudioBlob(result.blob);
      setAudioDuration(result.durationSec);
      setMode(MODE.TRANSCRIBING);

      // Upload then transcribe. Firebase upload is wrapped in try/catch
      // because we still want the transcript even if upload fails.
      const entryId = fb.newEntryId();
      const path = `audio/${entryId}.${
        result.mime.includes("mp4") ? "m4a" : "webm"
      }`;
      let uploadedPath = null;
      try {
        const up = await fb.uploadAudio(path, result.blob);
        uploadedPath = up.path;
        setAudioPath(up.path);
      } catch (e) {
        console.warn("audio upload failed", e);
      }

      let tr;
      try {
        tr = await api.transcribeAudio({
          audioPath: uploadedPath || path,
          durationSec: result.durationSec,
        });
      } catch (e) {
        tr = { text: "", fallback: true };
      }
      setTranscript(tr.text || "");
      setMode(MODE.SUMMARISING);

      const sum = await api.summariseEntry({
        transcript: tr.text || "",
        type,
        memberName,
      });
      setText(sum.summary || tr.text || "");
      setMode(MODE.REVIEW);
    }, [type, memberName]);

    const cancel = useCallback(() => {
      const r = recRef.current;
      if (r) r.cancel();
      recRef.current = null;
      onClose?.();
    }, [onClose]);

    // ────────────────────────────────────────────────────────────
    // Save — optimistic write to /coaches-copilot/entries/{id}
    // ────────────────────────────────────────────────────────────
    const save = useCallback(async () => {
      if (saving) return;
      const me = coach.get() || "simon";
      const id = fb.newEntryId();
      const now = Date.now();
      const body = (text || "").trim();
      if (!body && !transcript) {
        showToast("Type something first.");
        return;
      }
      const entry = {
        id,
        type,
        coach: me,
        created_at: now,
        body: body || transcript.slice(0, 180),
        transcript: transcript || null,
        audio_path: audioPath,
        audio_duration_sec: audioDuration ? Math.round(audioDuration) : null,
        members: [],
        tag: null,
        needs_followup: false,
        pinned: false,
        pinned_by: null,
        mentions: extractMentions(body),
        edited_at: null,
        deleted: false,
        ...(extraEntryFields ? extraEntryFields() : {}),
      };
      setSaving(true);
      try {
        await fb.set(`entries/${id}`, entry);
        onSaved?.(entry);
        onClose?.();
      } catch (e) {
        console.error(e);
        showToast("Couldn’t save. Retry?", {
          action: { label: "Retry", onClick: save },
        });
      } finally {
        setSaving(false);
      }
    }, [
      audioPath,
      audioDuration,
      text,
      transcript,
      type,
      extraEntryFields,
      onSaved,
      onClose,
      saving,
    ]);

    const saveDisabled =
      saving ||
      mode === MODE.RECORDING ||
      mode === MODE.TRANSCRIBING ||
      mode === MODE.SUMMARISING ||
      (canSave ? !canSave({ text, transcript }) : !(text || transcript).trim());

    return (
      <Drawer
        open={open}
        onClose={cancel}
        title={title}
        footer={
          <Button
            fullWidth
            size="lg"
            onClick={save}
            disabled={saveDisabled}
            loading={saving}
          >
            {saving ? "Saving" : "Save"}
          </Button>
        }
      >
        <div style={{ display: "grid", gap: 16 }}>
          {preChrome}
          <CaptureInput
            mode={mode}
            text={text}
            onTextChange={setText}
            placeholder={placeholder}
            elapsed={elapsed}
            audioDuration={audioDuration}
            onStart={startRecording}
            onStop={stopRecording}
          />
          {mode === MODE.REVIEW && (
            <p
              style={{
                margin: 0,
                fontSize: 13,
                color: tokens.colors.textMuted,
              }}
            >
              Edit if anything’s off — or tap save.
            </p>
          )}
          {postChrome}
        </div>
      </Drawer>
    );
  }

  // The voice/text input area. Modes 1–5.
  function CaptureInput({
    mode,
    text,
    onTextChange,
    placeholder,
    elapsed,
    audioDuration,
    onStart,
    onStop,
  }) {
    const recording = mode === MODE.RECORDING;
    const transcribing = mode === MODE.TRANSCRIBING;
    const summarising = mode === MODE.SUMMARISING;
    const review = mode === MODE.REVIEW;
    const idle = mode === MODE.IDLE;

    const placeholderShown = transcribing
      ? "Transcribing your voice note…"
      : summarising
      ? "Summarising…"
      : placeholder;

    return (
      <div className="cc-capture-input">
        <div className="cc-capture-input__field">
          {recording ? (
            <Waveform />
          ) : (
            <TextArea
              value={text}
              onChange={onTextChange}
              placeholder={placeholderShown}
              rows={3}
              maxRows={10}
              disabled={transcribing || summarising}
            />
          )}
          {recording && (
            <div className="cc-capture-input__timer">
              {window.CC.time.audioDuration(elapsed)}
            </div>
          )}
          {(transcribing || summarising) && (
            <div className="cc-capture-input__pending">
              <LoadingSpinner size="sm" delay={200} />
              <span>
                {transcribing ? "Transcribing" : "Summarising"}…
              </span>
            </div>
          )}
        </div>
        <RecordButton
          state={
            recording
              ? "recording"
              : transcribing || summarising
              ? "processing"
              : "idle"
          }
          onTap={recording ? onStop : onStart}
          disabled={transcribing || summarising}
        />
      </div>
    );
  }

  // Big circular record button. 64px diameter.
  function RecordButton({ state, onTap, disabled }) {
    const cls = `cc-record cc-record--${state} ${
      disabled ? "is-disabled" : ""
    }`;
    return (
      <button
        type="button"
        className={cls}
        onClick={onTap}
        disabled={disabled}
        aria-label={
          state === "recording" ? "Stop recording" : "Start recording"
        }
      >
        {state === "processing" ? (
          <LoadingSpinner size="sm" delay={0} />
        ) : state === "recording" ? (
          Icons.stop(20)
        ) : (
          Icons.mic(22)
        )}
      </button>
    );
  }

  // CSS-only "live" waveform — sin-driven 24 bars. Not a real analyser.
  function Waveform() {
    const bars = Array.from({ length: 24 });
    return (
      <div className="cc-waveform" aria-hidden="true">
        {bars.map((_, i) => (
          <span
            key={i}
            className="cc-waveform__bar"
            style={{ animationDelay: `${(i % 12) * 60}ms` }}
          />
        ))}
      </div>
    );
  }

  function extractMentions(body) {
    const hits = (body || "").match(/@(simon|perrie)/gi) || [];
    return Array.from(
      new Set(hits.map((h) => h.slice(1).toLowerCase()))
    );
  }

  window.CC = window.CC || {};
  window.CC.CaptureFlow = CaptureFlow;
  window.CC.MODE = MODE;
})();
