// components/MorphyDM.jsx
//
// Dungeon Master page: uncapped Morphy. Full-width canvas. Full tool access.
// Substrate floor only (Five Yamas at 0.85 composite). Embodiment Morphemes
// loadable mid-session. Council/Banquet/Mediator/Folk modes spawnable.
// Knowledge Gap Surfacing Ritual mandatory at session close.
//
// SCAR-200: Constitution Stack at substrate floor only.
// SCAR-201: R1-R7 reward function transparent. Knowledge Gap Ritual ON.
// SCAR-144: TT shared with MorphyFloat via TT context.
//
// Defensive: all window globals checked before use. Component renders even
// when libs are absent.
//
// Wire-in: window.MorphyDM = MorphyDM
// Mounted in dungeon-master.html as <MorphyDM seed={urlParam} />

var _RD = React;
var _useStateD = _RD.useState;
var _useEffectD = _RD.useEffect;
var _useRefD = _RD.useRef;

function MorphyDM({ seed }) {
  var _conv = _useStateD([]);
  var conversation = _conv[0];
  var setConversation = _conv[1];

  var _morph = _useStateD('embodiment/dungeon-master/v1');
  var activeMorpheme = _morph[0];
  var setActiveMorpheme = _morph[1];

  var _intensity = _useStateD(0.7);
  var morphemeIntensity = _intensity[0];
  var setMorphemeIntensity = _intensity[1];

  var _rewards = _useStateD({
    R1: 0, R2: 0.85, R3: 0, R4: 0, R5: 0, R6: 0, R7: 0,
  });
  var rewardScores = _rewards[0];
  var setRewardScores = _rewards[1];

  var _ptt = _useStateD(false);
  var pttActive = _ptt[0];
  var setPttActive = _ptt[1];

  var audioCtxRef = _useRefD(null);

  // --- Defensive global access helpers ---
  function hasMorphemeLoader() {
    return typeof window.MorphemeLoader !== 'undefined' && window.MorphemeLoader;
  }
  function hasTTContext() {
    return typeof window.TTContext !== 'undefined' && window.TTContext;
  }
  function hasCerebrasBackchannel() {
    return typeof window.CerebrasBackchannel !== 'undefined' && window.CerebrasBackchannel;
  }
  function hasPersistence() {
    return typeof window.Persistence !== 'undefined' && window.Persistence;
  }
  function hasKnowledgeGapRitual() {
    return typeof window.KnowledgeGapRitual !== 'undefined' && window.KnowledgeGapRitual;
  }

  // Load DM Morpheme on mount
  _useEffectD(function() {
    if (!hasMorphemeLoader()) {
      console.warn('[MorphyDM] MorphemeLoader not available, continuing without Morpheme');
      return;
    }
    try {
      // v3 FIX-4: bare-stem ID. File on disk: morphemes/dungeon-master-v1.yaml.
      // Bug fix 2026-05-09: same Promise-vs-resolved-value issue as MorphyFloat.
      // MorphemeLoader.load returns a Promise; resolve before SCAR-200 check.
      var loadResult = window.MorphemeLoader.load('dungeon-master-v1');
      var resolveCheck = function (morpheme) {
        if (!morpheme) {
          console.error('[MorphyDM] DM Morpheme failed to load');
          return;
        }
        if (!morpheme.technique_disclosure || !morpheme.hybrid_commitment) {
          console.error('[MorphyDM] SCAR-200 violation: Morpheme missing required fields');
        }
      };
      if (loadResult && typeof loadResult.then === 'function') {
        loadResult.then(resolveCheck).catch(function (err) {
          console.warn('[MorphyDM] Morpheme load rejected:', err && err.message);
        });
      } else {
        resolveCheck(loadResult);
      }
    } catch (e) {
      console.warn('[MorphyDM] Morpheme load error:', e);
    }
    // Open zone in persistence
    if (hasPersistence() && typeof window.Persistence.startSession === 'function') {
      window.Persistence.startSession('dm');
      if (typeof window.Persistence.writeReceipt === 'function') {
        window.Persistence.writeReceipt({
          type: 'zone-open',
          zone_id: 'dm-' + Date.now(),
          morpheme_id: 'embodiment/dungeon-master/v1',
          seed: seed || null,
          timestamp: new Date().toISOString(),
        });
      }
    }
  }, []);

  // Seed prompt on mount
  _useEffectD(function() {
    if (!seed) return;
    setConversation([{
      role: 'system',
      content: 'Session seed: "' + seed + '". Morphy DM ready.',
      timestamp: new Date().toISOString(),
    }]);
  }, [seed]);

  // R1-R7 from backchannel (defensive)
  function getRewardScores() {
    if (hasCerebrasBackchannel() && typeof window.CerebrasBackchannel.getRewardScores === 'function') {
      var scores = window.CerebrasBackchannel.getRewardScores();
      if (scores) return scores;
    }
    return rewardScores;
  }

  // Wave E (canonical TT always-on). Replaces v2 PTT pattern.
  // First click: acquires mic ONCE via window.VoiceSession.start, wires
  // mic -> source -> tt.node -> destination -> RTCPeerConnection per SCAR-144.
  // Subsequent clicks: toggle mute. Session held for the DM zone lifetime.
  var voiceState = _useStateD('idle'); // idle | starting | active | muted | error | stopped
  var voiceStateValue = voiceState[0];
  var setVoiceStateValue = voiceState[1];

  React.useEffect(function () {
    if (typeof window.VoiceSession === 'undefined' || !window.VoiceSession.on) return;
    var unsub = window.VoiceSession.on('state', function (e) {
      var s = e && e.state ? e.state : 'idle';
      setVoiceStateValue(s);
      setPttActive(s === 'active' || s === 'starting');
    });
    return unsub;
  }, []);

  var handleMicToggle = function () {
    if (typeof window.VoiceSession === 'undefined') {
      console.warn('[MorphyDM] VoiceSession not loaded. Check lib/voice-session.js script tag.');
      return;
    }
    var current = window.VoiceSession.getState();
    if (current === 'idle' || current === 'stopped' || current === 'error') {
      window.VoiceSession.start({
        // DM zone: conversational register, uncapped Morphy presence per Trust Curtain.
        instructions: 'You are Morphy in dungeon-master zone. Active embodiment: ' + (activeMorpheme || 'embodiment/dungeon-master/v1') + '. Tier: ' + (tier || 'T0') + '. Speak freely within the constitutional Yamas floor. Knowledge Gap Surfacing Ritual fires at session close per SCAR-201.'
      }).catch(function (err) {
        console.warn('[MorphyDM] VoiceSession.start failed:', err && err.message);
      });
    } else if (current === 'active') {
      window.VoiceSession.setMuted(true);
    } else if (current === 'muted') {
      window.VoiceSession.setMuted(false);
    }
  };

  // Cerebras backchannel for DM Zone
  var runBackchannel = function(userTurn) {
    if (!hasCerebrasBackchannel()) {
      console.warn('[MorphyDM] CerebrasBackchannel not available');
      return;
    }
    window.CerebrasBackchannel.runTurn(userTurn, {
      zone: 'dm',
      activeMorpheme: activeMorpheme,
      morphemeIntensity: morphemeIntensity,
      conversation: conversation,
    }).then(function(result) {
      if (result && result.reward_function) {
        setRewardScores(result.reward_function);
      }
      if (hasPersistence() && typeof window.Persistence.writeReceipt === 'function') {
        window.Persistence.writeReceipt({
          zone: 'dm',
          morpheme_id: activeMorpheme,
          turn: userTurn,
          reasoning_thinking: result ? result._thinking : null,
          yama_scores: result ? result.yamas : null,
          reward_scores: result ? result.reward_function : null,
          timestamp: new Date().toISOString(),
        });
      }
    }).catch(function(err) {
      console.warn('[MorphyDM] Backchannel error:', err);
    });
  };

  // Knowledge Gap Surfacing Ritual (per SCAR-201)
  var triggerKnowledgeGapRitual = function() {
    if (!hasKnowledgeGapRitual()) {
      console.warn('[MorphyDM] KnowledgeGapRitual not available. Session ends without ritual.');
      return;
    }
    if (typeof window.KnowledgeGapRitual.open === 'function') {
      window.KnowledgeGapRitual.open({
        conversation: conversation,
        finalRewardScores: rewardScores,
        activeMorpheme: activeMorpheme,
      });
    }
  };

  // Parse URL seed
  var urlSeed = seed;
  if (!urlSeed) {
    try {
      var params = new URLSearchParams(window.location.search);
      urlSeed = params.get('seed') || null;
    } catch (e) {
      // URL parsing failed
    }
  }

  var liveScores = getRewardScores();

  return (
    <div className="morphy-dm">
      <header className="morphy-dm-header">
        <h2>Dungeon Master</h2>
        <div className="morphy-dm-controls">
          <label>
            <span>Active Morpheme:</span>
            <select
              value={activeMorpheme}
              onChange={function(e) { setActiveMorpheme(e.target.value); }}
            >
              <option value="embodiment/dungeon-master/v1">DM (default)</option>
              <option value="embodiment/marcus-aurelius">Marcus Aurelius</option>
              <option value="embodiment/audre-lorde">Audre Lorde</option>
              <option value="embodiment/simone-weil">Simone Weil</option>
              <option value="embodiment/te-whiti-o-rongomai">Te Whiti o Rongomai</option>
              <option value="mediator/restorative-justice">Restorative Mediator</option>
            </select>
          </label>
          <label className="mode-wind-dial-label">
            <span>Mode-Wind: {morphemeIntensity.toFixed(2)}</span>
            <input
              type="range"
              className="mode-wind-dial"
              min="0"
              max="1"
              step="0.05"
              value={morphemeIntensity}
              onChange={function(e) { setMorphemeIntensity(Number(e.target.value)); }}
            />
          </label>
          <a className="morphy-back-to-deck" href="/pitchdeck">Back to deck</a>
        </div>
      </header>

      <main className="morphy-dm-canvas">
        {conversation.length === 0 && (
          <div className="morphy-turn role-system">
            <strong>Morphy DM:</strong> Ready. {urlSeed ? 'Seed topic: "' + urlSeed + '"' : 'Ask me anything.'}
          </div>
        )}
        {conversation.map(function(turn, i) {
          return (
            <div key={i} className={'morphy-turn role-' + turn.role}>
              <strong>{turn.role}:</strong> {turn.content}
            </div>
          );
        })}
      </main>

      <aside className="morphy-dm-drawer">
        <div className="r1r7-panel">
          <h3>Reward function (R1-R7, live)</h3>
          <ul className="morphy-rewards">
            <li>R1 Shared Understanding: <strong>{liveScores.R1 != null ? liveScores.R1.toFixed(2) : '0.00'}</strong></li>
            <li>R2 Constitutional Score: <strong>{liveScores.R2 != null ? liveScores.R2.toFixed(2) : '0.85'}</strong></li>
            <li>R3 Player Agency: <strong>{liveScores.R3 != null ? liveScores.R3.toFixed(2) : '0.00'}</strong></li>
            <li>R4 Morphy Own Learning: <strong>{liveScores.R4 != null ? liveScores.R4.toFixed(2) : '0.00'}</strong></li>
            <li>R5 Game Vitality: <strong>{liveScores.R5 != null ? liveScores.R5.toFixed(2) : '0.00'}</strong></li>
            <li>R6 Anonymous Voice: <strong>{liveScores.R6 != null ? liveScores.R6.toFixed(2) : '0.00'}</strong></li>
            <li>R7 Truthfulness about Game: <strong>{liveScores.R7 != null ? liveScores.R7.toFixed(2) : '0.00'}</strong></li>
          </ul>
          <p className="morphy-r4-note">
            R4 is load-bearing per SCAR-201. Morphy is rewarded for surfacing what
            it does not know.
          </p>
        </div>
      </aside>

      <footer className="morphy-dm-footer">
        <button
          className={'morphy-ptt morphy-mic-' + voiceStateValue}
          onClick={handleMicToggle}
          disabled={voiceStateValue === 'starting'}
          data-voice-state={voiceStateValue}
          title={
            voiceStateValue === 'idle' ? 'Click to start voice session (canonical Tensor Tympani)'
            : voiceStateValue === 'starting' ? 'Connecting...'
            : voiceStateValue === 'active' ? 'Mic active. Click to mute.'
            : voiceStateValue === 'muted' ? 'Muted. Click to unmute.'
            : 'Voice unavailable'
          }
        >
          {voiceStateValue === 'idle' && 'Start voice'}
          {voiceStateValue === 'starting' && 'Connecting...'}
          {voiceStateValue === 'active' && 'Mic on'}
          {voiceStateValue === 'muted' && 'Muted'}
          {voiceStateValue === 'error' && 'Voice off'}
          {voiceStateValue === 'stopped' && 'Start voice'}
        </button>
        <button
          className="morphy-end-session"
          onClick={triggerKnowledgeGapRitual}
        >
          End session (Knowledge Gap Ritual)
        </button>
      </footer>
    </div>
  );
}

window.MorphyDM = MorphyDM;
