import { useEffect, useMemo, useRef, useState } from "react";

function resolveApiBase() {
  const explicitBase = import.meta.env.VITE_API_BASE;
  if (explicitBase) return explicitBase.replace(/\/$/, "");

  const host = window.location.hostname;
  const isLocal =
    host === "localhost" || host === "127.0.0.1" || host === "0.0.0.0";

  // In local Vite dev, backend runs on :7860. In Spaces/prod, serve same-origin.
  if (isLocal && window.location.port === "5173") {
    return "http://localhost:7860";
  }
  return window.location.origin.replace(/\/$/, "");
}

const API_BASE = resolveApiBase();
const WS_URL = `${API_BASE.replace(/^http/, "ws")}/ws`;
const TASKS = ["easy_screening", "budgeted_screening", "complex_tradeoff"];

async function apiPost(path, body) {
  const res = await fetch(`${API_BASE}${path}`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  if (!res.ok) {
    const msg = await res.text();
    throw new Error(msg || `HTTP ${res.status}`);
  }
  return res.json();
}

export default function App() {
  const [taskId, setTaskId] = useState("budgeted_screening");
  const [obs, setObs] = useState(null);
  const [log, setLog] = useState([]);
  const [loading, setLoading] = useState(false);
  const [action, setAction] = useState({
    action_type: "query_ddi",
    drug_id_1: "",
    drug_id_2: "",
    target_drug_id: "",
    intervention_type: "stop",
    proposed_new_drug_id: "",
    rationale: "",
  });

  const medIds = useMemo(
    () => (obs?.current_medications || []).map((m) => m.drug_id),
    [obs]
  );
  const hasValidEpisode = Boolean(obs?.episode_id) && (obs?.current_medications?.length || 0) > 0;
  const isDone = Boolean(obs?.done);
  const finalScore =
    typeof obs?.metadata?.grader_score === "number" ? obs.metadata.grader_score : null;
  const noBudgetsLeft =
    hasValidEpisode &&
    (obs?.remaining_query_budget ?? 0) <= 0 &&
    (obs?.remaining_intervention_budget ?? 0) <= 0;
  const wsRef = useRef(null);
  const pendingRef = useRef([]);

  const wsEnsure = async () => {
    if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) return wsRef.current;
    if (wsRef.current && wsRef.current.readyState === WebSocket.CONNECTING) {
      await new Promise((r) => setTimeout(r, 80));
      return wsEnsure();
    }

    const ws = new WebSocket(WS_URL);
    wsRef.current = ws;

    ws.onmessage = (evt) => {
      try {
        const msg = JSON.parse(evt.data);
        const pending = pendingRef.current.shift();
        if (pending) pending.resolve(msg);
      } catch (e) {
        const pending = pendingRef.current.shift();
        if (pending) pending.reject(e);
      }
    };
    ws.onerror = (err) => {
      const pending = pendingRef.current.shift();
      if (pending) pending.reject(err);
    };
    ws.onclose = () => {
      wsRef.current = null;
    };

    await new Promise((resolve, reject) => {
      const t = setTimeout(() => reject(new Error("WebSocket connect timeout")), 2500);
      ws.onopen = () => {
        clearTimeout(t);
        resolve();
      };
    });
    return ws;
  };

  const wsSend = async (type, data) => {
    const ws = await wsEnsure();
    return await new Promise((resolve, reject) => {
      pendingRef.current.push({ resolve, reject });
      ws.send(JSON.stringify({ type, data }));
    });
  };

  useEffect(() => {
    return () => {
      try {
        wsRef.current?.close();
      } catch {
        // ignore
      }
    };
  }, []);

  const appendLog = (text) => {
    setLog((prev) => [`${new Date().toLocaleTimeString()}  ${text}`, ...prev].slice(0, 20));
  };

  const normalizeObsFromWs = (packetData) => {
    const observation = packetData?.observation || {};
    const mergedMetadata = {
      ...(observation?.metadata || {}),
      ...(packetData?.info || {}),
    };
    return {
      ...observation,
      done: Boolean(packetData?.done ?? observation?.done ?? false),
      reward: packetData?.reward ?? observation?.reward ?? null,
      metadata: mergedMetadata,
    };
  };

  const handleReset = async () => {
    setLoading(true);
    try {
      const msg = await wsSend("reset", { task_id: taskId });
      const data = msg?.data || {};
      const normalized = normalizeObsFromWs(data);
      setObs(normalized);
      const ids = (normalized?.current_medications || []).map((m) => m.drug_id);
      setAction((prev) => ({
        ...prev,
        drug_id_1: ids[0] || "",
        drug_id_2: ids[1] || "",
        target_drug_id: ids[0] || "",
      }));
      appendLog(`Reset task=${taskId}`);
    } catch (err) {
      appendLog(`Reset failed: ${err.message}`);
    } finally {
      setLoading(false);
    }
  };

  const buildActionPayload = () => {
    if (noBudgetsLeft) {
      return { action_type: "finish_review" };
    }
    if (action.action_type === "query_ddi") {
      return {
        action_type: "query_ddi",
        drug_id_1: action.drug_id_1,
        drug_id_2: action.drug_id_2,
      };
    }
    if (action.action_type === "propose_intervention") {
      return {
        action_type: "propose_intervention",
        target_drug_id: action.target_drug_id,
        intervention_type: action.intervention_type,
        proposed_new_drug_id: action.proposed_new_drug_id || undefined,
        rationale: action.rationale || undefined,
      };
    }
    return { action_type: "finish_review" };
  };

  const isActionValid = () => {
    if (!hasValidEpisode) return false;
    if (isDone) return false;
    if (noBudgetsLeft) return true;
    if (action.action_type === "query_ddi") {
      return Boolean(action.drug_id_1 && action.drug_id_2);
    }
    if (action.action_type === "propose_intervention") {
      return Boolean(action.target_drug_id && action.intervention_type);
    }
    return true;
  };

  const handleStep = async (overrideAction = null) => {
    if (!hasValidEpisode) {
      appendLog("Run Reset Episode before stepping.");
      return;
    }
    setLoading(true);
    try {
      const payload = overrideAction || buildActionPayload();
      const msg = await wsSend("step", payload);
      const data = msg?.data || {};
      const normalized = normalizeObsFromWs(data);
      setObs(normalized);
      appendLog(`Step: ${payload.action_type} -> reward=${data.reward ?? 0}`);
    } catch (err) {
      appendLog(`Step failed: ${err.message}`);
    } finally {
      setLoading(false);
    }
  };

  const askAi = async () => {
    if (!hasValidEpisode) {
      appendLog("Run Reset Episode before asking AI.");
      return;
    }
    setLoading(true);
    try {
      const data = await apiPost("/agent/suggest", { observation: obs });
      appendLog(`AI suggestion: ${data.action.action_type}`);
      await handleStep(data.action);
    } catch (err) {
      appendLog(`AI suggestion failed: ${err.message}`);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="shell">
      <div className="bg-orb orb-a" />
      <div className="bg-orb orb-b" />

      <div className="container">
      <header className="topbar glass">
        <div className="title-wrap">
          <h1>Polypharmacy Control Center</h1>
          <p>Metaverse Clinical Ops Console</p>
        </div>
        <div className={`status-chip ${hasValidEpisode ? "live" : "idle"}`}>
          {hasValidEpisode ? "Session Live" : "Waiting for reset"}
        </div>
        <div className="actions">
          <select value={taskId} onChange={(e) => setTaskId(e.target.value)}>
            {TASKS.map((t) => (
              <option key={t} value={t}>
                {t}
              </option>
            ))}
          </select>
          <button onClick={handleReset} disabled={loading}>
            Reset Episode
          </button>
          <button className="secondary" onClick={askAi} disabled={!hasValidEpisode || isDone || loading}>
            Ask AI + Auto Step
          </button>
        </div>
      </header>

      <main className="layout">
        <section className="panel glass panel-wide">
          <h2>Episode</h2>
          {hasValidEpisode ? (
            <div className="kpi-grid">
              <div><span>Episode</span><strong>{obs.episode_id}</strong></div>
              <div><span>Task</span><strong>{obs.task_id}</strong></div>
              <div><span>Age / Sex</span><strong>{obs.age} / {obs.sex}</strong></div>
              <div><span>Step</span><strong>{obs.step_index}</strong></div>
              <div><span>Query budget</span><strong>{obs.remaining_query_budget}</strong></div>
              <div><span>Intervention budget</span><strong>{obs.remaining_intervention_budget}</strong></div>
            </div>
          ) : (
            <p className="muted">Start with Reset Episode. Until then, step actions are blocked.</p>
          )}
          {noBudgetsLeft && (
            <p className="muted budget-note">Query and intervention budgets are exhausted. Finish review to get final score.</p>
          )}
          {isDone && (
            <p className="muted budget-note">
              Episode complete
              {finalScore !== null ? ` • final score: ${finalScore.toFixed(3)}` : ""}.
              Click Reset Episode to start a new case.
            </p>
          )}
        </section>

        <section className="panel glass">
          <h2>Action Console</h2>
          <div className="action-row">
            <label>Action type</label>
            <select
              value={action.action_type}
              onChange={(e) => setAction((a) => ({ ...a, action_type: e.target.value }))}
            >
              <option value="query_ddi">query_ddi</option>
              <option value="propose_intervention">propose_intervention</option>
              <option value="finish_review">finish_review</option>
            </select>
          </div>

          {action.action_type === "query_ddi" && (
            <div className="stack stack-two">
              <input
                placeholder="drug_id_1"
                value={action.drug_id_1}
                onChange={(e) => setAction((a) => ({ ...a, drug_id_1: e.target.value }))}
              />
              <input
                placeholder="drug_id_2"
                value={action.drug_id_2}
                onChange={(e) => setAction((a) => ({ ...a, drug_id_2: e.target.value }))}
              />
            </div>
          )}

          {action.action_type === "propose_intervention" && (
            <div className="stack">
              <select
                value={action.target_drug_id}
                onChange={(e) => setAction((a) => ({ ...a, target_drug_id: e.target.value }))}
              >
                <option value="">Select target drug</option>
                {medIds.map((id) => (
                  <option key={id} value={id}>
                    {id}
                  </option>
                ))}
              </select>
              <select
                value={action.intervention_type}
                onChange={(e) => setAction((a) => ({ ...a, intervention_type: e.target.value }))}
              >
                <option value="stop">stop</option>
                <option value="dose_reduce">dose_reduce</option>
                <option value="substitute">substitute</option>
                <option value="add_monitoring">add_monitoring</option>
              </select>
              <input
                placeholder="proposed_new_drug_id (optional)"
                value={action.proposed_new_drug_id}
                onChange={(e) =>
                  setAction((a) => ({ ...a, proposed_new_drug_id: e.target.value }))
                }
              />
              <input
                placeholder="rationale (optional)"
                value={action.rationale}
                onChange={(e) => setAction((a) => ({ ...a, rationale: e.target.value }))}
              />
            </div>
          )}
          <button onClick={() => handleStep()} disabled={!isActionValid() || loading}>
            {noBudgetsLeft ? "Finish Review" : "Submit Step"}
          </button>
        </section>

        <section className="panel glass">
          <h2>Current Medications</h2>
          <div className="med-grid">
            {(obs?.current_medications || []).map((m) => (
              <div key={m.drug_id} className="med-card">
                <strong>{m.drug_id}</strong>
                <p>{m.generic_name}</p>
                <small>{m.dose_mg} mg • {m.atc_class}</small>
              </div>
            ))}
          </div>
        </section>

        <section className="panel glass">
          <h2>Event Log</h2>
          <div className="logs">
            {log.map((line, idx) => (
              <div key={idx}>{line}</div>
            ))}
          </div>
        </section>
      </main>
      </div>
    </div>
  );
}
