{% extends "base.html" %}

{% block title %}supyagent — Models{% endblock %}

{% block extra_css %}
.key-input-wrap {
  display: flex;
  gap: 8px;
  align-items: center;
  margin-top: 8px;
}
.key-input-wrap input { flex: 1; }
.provider-card {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
}
.provider-card:last-child { border-bottom: none; }
.provider-name { font-weight: 600; font-size: 13px; }
.provider-desc { font-size: 12px; color: var(--text-dim); margin-top: 2px; }
.role-select { width: 100%; margin-top: 4px; }
.add-model-form { display: flex; gap: 8px; margin-top: 12px; }
.add-model-form input { flex: 1; }
.security-note {
  font-size: 11px;
  color: var(--text-muted);
  margin-top: 12px;
  padding: 8px 12px;
  background: var(--bg);
  border-radius: 4px;
  border: 1px solid var(--border);
}
{% endblock %}

{% block content %}
<h1>Model Registry</h1>
<p class="subtitle">Manage API keys, registered models, and role assignments.</p>

<!-- API Keys Section -->
<div class="section">
  <h2>API Keys</h2>
  <div id="keys-list">
    <div class="text-dim">Loading...</div>
  </div>
  <div class="security-note">
    Keys are stored locally with AES encryption (Fernet). Never transmitted over the network.
  </div>
</div>

<!-- Registered Models Section -->
<div class="section">
  <div class="section-header">
    <h2>Registered Models</h2>
    <button id="verify-all-btn" class="btn btn-sm" onclick="verifyAll()">Verify All</button>
  </div>
  <table id="models-table">
    <thead>
      <tr>
        <th>Model</th>
        <th>Provider</th>
        <th>API Key</th>
        <th>Roles</th>
        <th></th>
      </tr>
    </thead>
    <tbody id="models-body">
      <tr><td colspan="5" class="text-dim">Loading...</td></tr>
    </tbody>
  </table>
  <div class="add-model-form">
    <input type="text" id="new-model-input"
           placeholder="e.g. anthropic/claude-sonnet-4-5-20250929 or any LiteLLM model string">
    <button class="btn btn-primary btn-sm" onclick="addModel()">Add Model</button>
  </div>

  <!-- Inline key prompt (shown after adding a model that needs a key) -->
  <div id="add-model-key-prompt" class="hidden mt-2">
    <div class="card">
      <p class="mb-2" id="key-prompt-label"></p>
      <div id="key-prompt-env-row" class="mb-2 hidden">
        <label>Environment variable name</label>
        <input type="text" id="key-prompt-env-name"
               placeholder="e.g. MY_PROVIDER_API_KEY">
      </div>
      <label id="key-prompt-value-label">API Key</label>
      <div class="input-row">
        <input type="password" id="key-prompt-value" class="grow"
               placeholder="Enter API key value...">
        <button class="btn btn-primary btn-sm" onclick="savePromptedKey()">Save Key</button>
        <button class="btn btn-sm" onclick="dismissKeyPrompt()">Skip</button>
      </div>
    </div>
  </div>
</div>

<!-- Roles Section -->
<div class="section">
  <h2>Roles</h2>
  <p class="text-dim text-sm mb-4">
    Agents reference roles instead of specific models. Assign a model to each role.
  </p>
  <div class="card-grid" id="roles-grid">
    <div class="text-dim">Loading...</div>
  </div>
</div>

<!-- Footer -->
<div class="footer-bar">
  <button class="btn btn-primary" onclick="finish()">Done</button>
</div>
{% endblock %}

{% block extra_js %}
let state = {};
let verifyStatus = {}; // { model: "verified" | "error" | "unverified" }

async function loadState() {
  state = await apiCall("GET", "/api/models/state");
  // Initialize verify status from state (missing_keys → missing, has_key → unverified)
  if (state.models) {
    state.models.forEach(m => {
      if (!(m.model in verifyStatus)) {
        if (m.missing_keys && m.missing_keys.length > 0) {
          verifyStatus[m.model] = "missing";
        } else if (m.has_key) {
          verifyStatus[m.model] = "unverified";
        } else {
          verifyStatus[m.model] = "missing";
        }
      }
    });
  }
  renderKeys();
  renderModels();
  renderRoles();
}

function renderKeys() {
  const el = document.getElementById("keys-list");
  // Only show providers that are relevant (known LLM keys)
  const relevant = state.providers.filter(p =>
    p.key_name.endsWith("_API_KEY") || p.key_name.endsWith("_API_BASE")
  );

  el.innerHTML = relevant.map(p => {
    const statusCls = p.configured ? "status-ok" : "status-missing";
    const statusText = p.configured
      ? (p.source === "env" ? "env" : "stored")
      : "not set";
    return `
      <div class="provider-card" data-key="${p.key_name}">
        <div>
          <div class="provider-name">${p.key_name}</div>
          <div class="provider-desc">${p.description}</div>
          <div class="key-input-wrap hidden" id="key-form-${p.key_name}">
            <input type="password" id="key-val-${p.key_name}"
                   placeholder="Enter API key...">
            <button class="btn btn-sm btn-primary"
                    onclick="saveKey('${p.key_name}')">Save</button>
            <button class="btn btn-sm"
                    onclick="toggleKeyForm('${p.key_name}', false)">Cancel</button>
          </div>
        </div>
        <div class="flex items-center gap-2">
          <span class="status ${statusCls}">
            <span class="status-dot"></span>${statusText}
          </span>
          ${p.configured
            ? `<button class="btn btn-danger btn-sm" onclick="deleteKey('${p.key_name}')">Delete</button>`
            : ""}
          <button class="btn btn-sm" onclick="toggleKeyForm('${p.key_name}', true)">
            ${p.configured ? "Update" : "Set"}
          </button>
        </div>
      </div>`;
  }).join("");
}

function toggleKeyForm(keyName, show) {
  const form = document.getElementById(`key-form-${keyName}`);
  if (form) form.classList.toggle("hidden", !show);
}

async function saveKey(keyName) {
  const input = document.getElementById(`key-val-${keyName}`);
  const value = input.value.trim();
  if (!value) return;
  try {
    await apiCall("POST", "/api/keys/set", { key_name: keyName, value });
    toast(`Saved ${keyName}`);
    input.value = "";
    await loadState();
  } catch (e) {
    toast(`Error: ${e.message}`, "error");
  }
}

async function deleteKey(keyName) {
  try {
    await apiCall("POST", "/api/keys/delete", { key_name: keyName });
    toast(`Deleted ${keyName}`);
    await loadState();
  } catch (e) {
    toast(`Error: ${e.message}`, "error");
  }
}

function renderModels() {
  const tbody = document.getElementById("models-body");
  if (!state.models || state.models.length === 0) {
    tbody.innerHTML = `<tr><td colspan="5" class="text-dim">
      No models registered. Add one below.</td></tr>`;
    return;
  }

  tbody.innerHTML = state.models.map(m => {
    const vs = verifyStatus[m.model] || "unverified";
    let statusHtml;
    if (vs === "verified") {
      statusHtml = `<span class="status status-ok"><span class="status-dot"></span>verified</span>`;
    } else if (vs === "missing") {
      const keys = (m.missing_keys && m.missing_keys.length > 0) ? m.missing_keys : [];
      if (keys.length > 0) {
        const parts = keys.map(k =>
          `<code>${k}</code> <button class="btn btn-sm" onclick="showKeyPrompt('${m.model}', '${k}')">Set</button>`
        ).join(", ");
        statusHtml = `<span class="status status-missing"><span class="status-dot"></span>missing: ${parts}</span>`;
      } else if (m.key_name) {
        statusHtml = `<span class="status status-missing"><span class="status-dot"></span>missing: <code>${m.key_name}</code> <button class="btn btn-sm" onclick="showKeyPrompt('${m.model}', '${m.key_name}')">Set</button></span>`;
      } else {
        statusHtml = `<span class="status status-missing"><span class="status-dot"></span>missing <button class="btn btn-sm" onclick="showKeyPrompt('${m.model}', null)">Set Key</button></span>`;
      }
    } else if (vs.startsWith("error:")) {
      statusHtml = `<span class="status status-missing" title="${vs.slice(6)}"><span class="status-dot"></span>error <button class="btn btn-sm" onclick="showKeyPrompt('${m.model}', null)">Set Key</button></span>`;
    } else {
      statusHtml = `<span class="status status-warn"><span class="status-dot"></span>unverified</span>`;
    }
    const badges = [];
    if (m.is_default) badges.push(`<span class="badge badge-default">default</span>`);
    m.roles.forEach(r => badges.push(`<span class="badge badge-role">${r}</span>`));
    return `
      <tr>
        <td>${m.model}</td>
        <td class="text-dim">${m.provider}</td>
        <td>${statusHtml}</td>
        <td>${badges.join(" ")}</td>
        <td class="btn-group">
          <button class="btn btn-sm" onclick="verifyModel('${m.model}')">Verify</button>
          ${!m.is_default ? `<button class="btn btn-sm" onclick="setDefault('${m.model}')">Set Default</button>` : ""}
          <button class="btn btn-danger btn-sm" onclick="removeModel('${m.model}')">Remove</button>
        </td>
      </tr>`;
  }).join("");
}

async function verifyModel(model) {
  toast(`Verifying ${model}...`);
  try {
    const res = await apiCall("POST", "/api/models/verify", { model });
    if (res.status === "verified") {
      verifyStatus[model] = "verified";
      toast(`${model}: verified`);
    } else if (res.status === "missing_keys") {
      verifyStatus[model] = "missing";
      toast(`${model}: missing keys — ${res.missing_keys.join(", ")}`, "error");
    } else {
      verifyStatus[model] = "error:" + (res.error || "unknown error");
      toast(`${model}: ${res.error || "verification failed"}`, "error");
    }
    await loadState();
  } catch (e) {
    toast(`Error: ${e.message}`, "error");
  }
}

async function verifyAll() {
  const btn = document.getElementById("verify-all-btn");
  btn.disabled = true;
  btn.textContent = "Verifying...";
  let verified = 0;
  let total = 0;
  try {
    const toVerify = (state.models || []).filter(m => verifyStatus[m.model] !== "verified");
    total = toVerify.length;
    for (const m of toVerify) {
      try {
        const res = await apiCall("POST", "/api/models/verify", { model: m.model });
        if (res.status === "verified") {
          verifyStatus[m.model] = "verified";
          verified++;
        } else if (res.status === "missing_keys") {
          verifyStatus[m.model] = "missing";
        } else {
          verifyStatus[m.model] = "error:" + (res.error || "unknown error");
        }
      } catch (e) {
        verifyStatus[m.model] = "error:" + e.message;
      }
      renderModels();
    }
    await loadState();
    toast(`Verified ${verified}/${total} models`);
  } finally {
    btn.disabled = false;
    btn.textContent = "Verify All";
  }
}

let pendingKeyModel = null; // model string waiting for a key

async function addModel() {
  const input = document.getElementById("new-model-input");
  const model = input.value.trim();
  if (!model) return;
  try {
    const res = await apiCall("POST", "/api/models/add", { model });
    toast(`Registered ${model}`);
    input.value = "";
    await loadState();

    // If the model needs a key, prompt the user
    if (res.ok && !res.has_key) {
      // Use missing_keys from litellm if available, fall back to detected_key
      const keyToPrompt = (res.missing_keys && res.missing_keys.length > 0)
        ? res.missing_keys[0]
        : res.detected_key;
      showKeyPrompt(model, keyToPrompt);
    }
  } catch (e) {
    toast(`Error: ${e.message}`, "error");
  }
}

function showKeyPrompt(model, detectedKey) {
  pendingKeyModel = model;
  const prompt = document.getElementById("add-model-key-prompt");
  const label = document.getElementById("key-prompt-label");
  const envRow = document.getElementById("key-prompt-env-row");
  const valueLabel = document.getElementById("key-prompt-value-label");

  prompt.classList.remove("hidden");

  if (detectedKey) {
    // Known provider — we know the env var name, just need the value
    label.innerHTML = `<strong>${model}</strong> needs <code>${detectedKey}</code>`;
    valueLabel.textContent = detectedKey;
    envRow.classList.add("hidden");
    prompt.dataset.keyName = detectedKey;
  } else {
    // Unknown provider — ask for both env var name and value
    label.innerHTML = `<strong>${model}</strong> uses an unknown provider. What environment variable holds its API key?`;
    valueLabel.textContent = "API Key value";
    envRow.classList.remove("hidden");
    document.getElementById("key-prompt-env-name").value = "";
    prompt.dataset.keyName = "";
  }
  document.getElementById("key-prompt-value").value = "";
  document.getElementById("key-prompt-value").focus();
}

async function savePromptedKey() {
  const prompt = document.getElementById("add-model-key-prompt");
  let keyName = prompt.dataset.keyName;
  const value = document.getElementById("key-prompt-value").value.trim();

  if (!keyName) {
    keyName = document.getElementById("key-prompt-env-name").value.trim();
  }
  if (!keyName) {
    toast("Enter an environment variable name", "error");
    return;
  }
  if (!value) {
    toast("Enter an API key value", "error");
    return;
  }

  try {
    await apiCall("POST", "/api/keys/set", { key_name: keyName, value });
    toast(`Saved ${keyName}`);
    const modelToVerify = pendingKeyModel;
    dismissKeyPrompt();
    await loadState();
    // Auto-verify the model after saving its key
    if (modelToVerify) {
      await verifyModel(modelToVerify);
    }
  } catch (e) {
    toast(`Error: ${e.message}`, "error");
  }
}

function dismissKeyPrompt() {
  document.getElementById("add-model-key-prompt").classList.add("hidden");
  pendingKeyModel = null;
}

async function removeModel(model) {
  try {
    await apiCall("POST", "/api/models/remove", { model });
    toast(`Removed ${model}`);
    await loadState();
  } catch (e) {
    toast(`Error: ${e.message}`, "error");
  }
}

async function setDefault(model) {
  try {
    await apiCall("POST", "/api/models/default", { model });
    toast(`Default: ${model}`);
    await loadState();
  } catch (e) {
    toast(`Error: ${e.message}`, "error");
  }
}

function renderRoles() {
  const grid = document.getElementById("roles-grid");
  const roles = state.standard_roles || ["fast", "smart", "reasoning", "cheap"];
  const models = state.models || [];
  const descriptions = {
    fast: "Quick responses, lower cost",
    smart: "Best general-purpose model",
    reasoning: "Complex problem solving",
    cheap: "Lowest cost option",
  };

  grid.innerHTML = roles.map(role => {
    const current = state.roles[role] || "";
    const options = models.map(m =>
      `<option value="${m.model}" ${m.model === current ? "selected" : ""}>${m.model}</option>`
    ).join("");
    return `
      <div class="card">
        <div style="font-weight:600; margin-bottom:2px;">${role}</div>
        <div class="text-dim text-sm">${descriptions[role] || ""}</div>
        <select class="role-select" onchange="assignRole('${role}', this.value)">
          <option value="">— unassigned —</option>
          ${options}
        </select>
      </div>`;
  }).join("");
}

async function assignRole(role, model) {
  try {
    if (!model) {
      await apiCall("POST", "/api/models/unassign-role", { role });
      toast(`Cleared ${role} role`);
    } else {
      await apiCall("POST", "/api/models/assign-role", { role, model });
      toast(`${role} → ${model}`);
    }
    await loadState();
  } catch (e) {
    toast(`Error: ${e.message}`, "error");
  }
}

function finish() {
  signalDone({ action: "models_done" });
  document.querySelector(".footer-bar").innerHTML =
    `<span class="text-dim">Closing... you can close this tab.</span>`;
}

// Handle Enter key in inputs
document.getElementById("new-model-input").addEventListener("keydown", e => {
  if (e.key === "Enter") addModel();
});
document.getElementById("key-prompt-value").addEventListener("keydown", e => {
  if (e.key === "Enter") savePromptedKey();
});
document.getElementById("key-prompt-env-name").addEventListener("keydown", e => {
  if (e.key === "Enter") document.getElementById("key-prompt-value").focus();
});

loadState();
{% endblock %}
