const els = {
  apiBase: document.getElementById("apiBase"),
  healthStatus: document.getElementById("healthStatus"),
  projectSelect: document.getElementById("projectSelect"),
  chatSelect: document.getElementById("chatSelect"),
  messages: document.getElementById("messages"),
  messageInput: document.getElementById("messageInput"),
  chatForm: document.getElementById("chatForm"),
  sendButton: document.getElementById("sendButton"),
  createProjectBtn: document.getElementById("createProject"),
  refreshProjectsBtn: document.getElementById("refreshProjects"),
  createChatBtn: document.getElementById("createChat"),
  renameProjectBtn: document.getElementById("renameProject"),
  deleteProjectBtn: document.getElementById("deleteProject"),
  renameChatBtn: document.getElementById("renameChat"),
  clearMessagesBtn: document.getElementById("clearMessages"),
  clearChatMemoryBtn: document.getElementById("clearChatMemory"),
  checkHealthBtn: document.getElementById("checkHealth"),
  projectNameInput: document.getElementById("projectName"),
  chatNameInput: document.getElementById("chatName"),
};

const PROJECT_ID_KEY = "onecAgent.projectId";
const CHAT_BY_PROJECT_KEY = "onecAgent.chatByProject";

const REQUEST_TIMEOUT_MS = 120000;
const TURN_TIMEOUT_MS = 90000;

const state = {
  projectId: "",
  chatId: "",
  chatByProject: {},
};

function loadStateFromStorage() {
  try {
    const projectId = localStorage.getItem(PROJECT_ID_KEY);
    if (projectId) {
      state.projectId = projectId;
    }
    const map = localStorage.getItem(CHAT_BY_PROJECT_KEY);
    if (map) {
      const parsed = JSON.parse(map);
      if (parsed && typeof parsed === "object") {
        state.chatByProject = parsed;
      }
    }
  } catch {
    state.projectId = "";
    state.chatByProject = {};
  }
}

function persistProjectId(projectId) {
  state.projectId = projectId || "";
  try {
    if (projectId) {
      localStorage.setItem(PROJECT_ID_KEY, projectId);
    } else {
      localStorage.removeItem(PROJECT_ID_KEY);
    }
  } catch {
    // no-op if localStorage unavailable
  }
}

function persistChatId(projectId, chatId) {
  const map = state.chatByProject && typeof state.chatByProject === "object" ? { ...state.chatByProject } : {};
  if (projectId && chatId) {
    map[projectId] = chatId;
  } else if (projectId) {
    delete map[projectId];
  }
  state.chatByProject = map;
  state.chatId = chatId || "";
  try {
    localStorage.setItem(CHAT_BY_PROJECT_KEY, JSON.stringify(state.chatByProject));
  } catch {
    // no-op if localStorage unavailable
  }
}

function removePersistedProject(projectId) {
  persistProjectId("");
  if (!projectId) {
    return;
  }
  const map = state.chatByProject && typeof state.chatByProject === "object" ? { ...state.chatByProject } : {};
  delete map[projectId];
  state.chatByProject = map;
  try {
    localStorage.setItem(CHAT_BY_PROJECT_KEY, JSON.stringify(state.chatByProject));
  } catch {
    // no-op if localStorage unavailable
  }
}

function getApiBase() {
  return (els.apiBase.value || window.location.origin).replace(/\/$/, "");
}

function setStatus(message) {
  els.healthStatus.textContent = message;
}

function safeStringify(value) {
  try {
    return JSON.stringify(value, null, 2);
  } catch {
    return String(value ?? {});
  }
}

function formatMetaTime(requestPayload, created_at, payload) {
  const rawTime = requestPayload?.createdAt
    ? requestPayload.createdAt
    : (created_at || payload?.created_at || payload?.timestamp || null);
  if (!rawTime) {
    return new Date().toLocaleTimeString();
  }
  const dt = new Date(rawTime);
  return Number.isNaN(dt.getTime()) ? String(rawTime) : dt.toLocaleTimeString("ru-RU", { hour: "2-digit", minute: "2-digit", second: "2-digit" });
}

function buildExchangeLog(role, payload) {
  const transport = payload && typeof payload === "object" ? payload.transport : null;
  const sections = [];
  if (role === "user" && transport?.outbound) {
    sections.push({
      title: "Что отправили модели",
      value: transport.outbound,
    });
  }
  if (role === "assistant") {
    if (transport?.inbound) {
      sections.push({
        title: "Что получили от модели",
        value: transport.inbound,
      });
    }
    if (payload?.raw !== undefined) {
      sections.push({
        title: "Полный ответ провайдера",
        value: payload.raw,
      });
    }
  }
  if (payload?.route !== undefined) {
    sections.push({
      title: "Маршрут модели",
      value: payload.route,
    });
  }
  if (payload?.provider !== undefined) {
    sections.push({
      title: "Провайдер",
      value: payload.provider,
    });
  }
  return sections;
}

function renderMessage({ role, content, payload, requestPayload, created_at }) {
  const wrapper = document.createElement("div");
  wrapper.className = `message ${role}`;
  const meta = document.createElement("div");
  meta.className = "meta";
  const timestamp = formatMetaTime(requestPayload, created_at, payload);
  meta.textContent = `${role} • ${timestamp}`;
  const text = document.createElement("div");
  text.textContent = content || "";
  wrapper.appendChild(meta);
  wrapper.appendChild(text);

  const logs = buildExchangeLog(role, payload || {});
  if (logs.length) {
    const journal = document.createElement("details");
    journal.className = "journal";
    journal.open = false;
    const summary = document.createElement("summary");
    summary.textContent = "Журнал обмена";
    journal.appendChild(summary);
    logs.forEach((item) => {
      const heading = document.createElement("div");
      heading.className = "journal-title";
      heading.textContent = item.title;
      const pre = document.createElement("pre");
      pre.className = "mono";
      pre.textContent = safeStringify(item.value);
      journal.appendChild(heading);
      journal.appendChild(pre);
    });
    wrapper.appendChild(journal);
  }
  els.messages.appendChild(wrapper);
}

async function apiRequest(path, options = {}) {
  const controller = new AbortController();
  const { timeoutMs, ...fetchOptions } = options;
  const effectiveTimeoutMs = timeoutMs ?? REQUEST_TIMEOUT_MS;
  const timeout = setTimeout(() => controller.abort(), effectiveTimeoutMs);
  const url = `${getApiBase()}${path}`;
  let response;
  try {
    try {
      response = await fetch(url, {
        ...fetchOptions,
        signal: controller.signal,
        headers: {
          "Content-Type": "application/json",
          ...(fetchOptions.headers || {}),
        },
      });
    } catch (error) {
      if (error?.name === "AbortError") {
        throw new Error(`Превышен таймаут запроса (${Math.round(effectiveTimeoutMs / 1000)} сек)`);
      }
      throw error;
    }
  } finally {
    clearTimeout(timeout);
  }
  const bodyText = await response.text();
  const body = bodyText ? JSON.parse(bodyText) : {};
  if (!response.ok) {
    const message = body?.error?.message || `HTTP ${response.status}`;
    throw new Error(message);
  }
  return body;
}

async function checkHealth() {
  const data = await apiRequest("/v1/health");
  setStatus(`health ok: ${data.status}, service=${data.service}`);
  return data;
}

async function loadProjects() {
  const data = await apiRequest("/v1/projects");
  const projects = data.projects || [];
  els.projectSelect.innerHTML = "";
  projects.forEach((project) => {
    const option = document.createElement("option");
    option.value = project.id;
    option.textContent = project.name;
    els.projectSelect.appendChild(option);
  });
  if (!projects.length) {
    state.projectId = "";
    state.chatId = "";
    persistProjectId("");
    persistChatId("", "");
    els.projectNameInput.value = "";
    els.chatNameInput.value = "";
    els.chatSelect.innerHTML = "";
    return false;
  }
  if (!state.projectId || !projects.some((p) => p.id === state.projectId)) {
    state.projectId = projects[0].id;
  }
  persistProjectId(state.projectId);
  els.projectSelect.value = state.projectId;
  const currentProject = projects.find((project) => project.id === state.projectId);
  if (currentProject) {
    els.projectNameInput.value = currentProject.name || "";
  }
  return true;
}

async function loadChats() {
  if (!state.projectId) {
    els.chatSelect.innerHTML = "";
    state.chatId = "";
    els.chatNameInput.value = "";
    return;
  }
  const chats = await apiRequest(`/v1/projects/${state.projectId}/chats`);
  const list = chats.chats || [];
  els.chatSelect.innerHTML = "";
  list.forEach((chat) => {
    const option = document.createElement("option");
    option.value = chat.id;
    option.textContent = chat.title;
    els.chatSelect.appendChild(option);
  });
  if (!list.length) {
    state.chatId = "";
    els.chatNameInput.value = "";
    els.chatSelect.innerHTML = "";
    persistChatId(state.projectId, "");
    return;
  }

  const savedChatId = state.chatByProject[state.projectId];
  if (!state.chatId || !list.some((c) => c.id === state.chatId)) {
    state.chatId = savedChatId || "";
  }
  if (!state.chatId || !list.some((c) => c.id === state.chatId)) {
    state.chatId = list[0].id;
  }
  els.chatSelect.value = state.chatId;
  const activeChat = list.find((chat) => chat.id === state.chatId);
  if (activeChat) {
    els.chatNameInput.value = activeChat.title || "";
  }
  persistChatId(state.projectId, state.chatId);
}

async function loadMessages() {
  if (!state.projectId || !state.chatId) {
    els.messages.innerHTML = "";
    const placeholder = document.createElement("div");
    placeholder.className = "message system";
    if (!state.projectId) {
      placeholder.textContent = "Нет проекта. Создайте проект вручную.";
    } else if (!state.chatId) {
      placeholder.textContent = "В выбранном проекте нет чатов. Создайте чат вручную.";
    }
    els.messages.appendChild(placeholder);
    return;
  }
  const data = await apiRequest(`/v1/projects/${state.projectId}/chats/${state.chatId}/messages?limit=200`);
  const messages = data.messages || [];
  els.messages.innerHTML = "";
  const orderedMessages = [...messages].reverse();
  orderedMessages.forEach((message) => {
    renderMessage(message);
  });
  if (!messages.length) {
    const placeholder = document.createElement("div");
    placeholder.className = "message system";
    placeholder.textContent = "История чата пуста. Напишите первый вопрос.";
    els.messages.appendChild(placeholder);
  }
  els.messages.scrollTop = 0;
}

async function refreshConversation() {
  const hasProjects = await loadProjects();
  if (!hasProjects) {
    await loadMessages();
    return;
  }
  await loadChats();
  await loadMessages();
}

async function createChat() {
  if (!state.projectId) {
    return;
  }
  const created = await apiRequest(`/v1/projects/${state.projectId}/chats`, {
    method: "POST",
    body: JSON.stringify({
      title: els.chatNameInput.value.trim() || `Веб-чат ${new Date().toLocaleTimeString("ru-RU")}`,
    }),
  });
  state.chatId = created.chat.id;
  persistChatId(state.projectId, state.chatId);
  await loadChats();
  await loadMessages();
}

async function sendTurn(messageText) {
  await apiRequest(`/v1/projects/${state.projectId}/chats/${state.chatId}/turn`, {
    method: "POST",
    body: JSON.stringify({
      message: messageText,
    }),
    timeoutMs: TURN_TIMEOUT_MS,
  });
  await loadMessages();
}

function formatTimeoutMessage(durationMs) {
  const seconds = Math.max(1, Math.round(durationMs / 1000));
  return `Ожидаем ответ модели ~${seconds} сек...`;
}

function setSendingFeedback(startTs) {
  const tick = () => {
    const elapsed = Date.now() - startTs;
    if (els.sendButton.disabled) {
      setStatus(formatTimeoutMessage(elapsed));
    }
  };
  const timer = setInterval(tick, 12000);
  return () => clearInterval(timer);
}

els.checkHealthBtn.addEventListener("click", async () => {
  try {
    await checkHealth();
  } catch {
    // handled in function
  }
});

els.refreshProjectsBtn.addEventListener("click", async () => {
  await refreshConversation();
});

els.createProjectBtn.addEventListener("click", async () => {
  const now = new Date().toLocaleString("ru-RU");
  const customName = els.projectNameInput.value.trim();
  const created = await apiRequest("/v1/projects", {
    method: "POST",
    body: JSON.stringify({
      name: customName || `Проект ${now}`,
      description: "Создано из веб-интерфейса",
    }),
  });
  state.projectId = created.project.id;
  persistProjectId(state.projectId);
  persistChatId(state.projectId, "");
  await refreshConversation();
});

els.createChatBtn.addEventListener("click", async () => {
  if (!state.projectId) {
    return;
  }
  await createChat();
});

els.deleteProjectBtn.addEventListener("click", async () => {
  if (!state.projectId) {
    return;
  }
  const projectName = els.projectNameInput.value.trim() || "текущий проект";
  if (!window.confirm(`Удалить проект «${projectName}» и все его чаты?`)) {
    return;
  }
  await apiRequest(`/v1/projects/${state.projectId}`, {
    method: "DELETE",
  });
  removePersistedProject(state.projectId);
  state.projectId = "";
  state.chatId = "";
  els.projectNameInput.value = "";
  els.chatNameInput.value = "";
  await refreshConversation();
});

els.renameProjectBtn.addEventListener("click", async () => {
  if (!state.projectId) {
    return;
  }
  const newName = els.projectNameInput.value.trim();
  if (!newName) {
    return;
  }
  await apiRequest(`/v1/projects/${state.projectId}`, {
    method: "PATCH",
    body: JSON.stringify({ name: newName }),
  });
  await refreshConversation();
});

els.renameChatBtn.addEventListener("click", async () => {
  if (!state.projectId || !state.chatId) {
    return;
  }
  const newTitle = els.chatNameInput.value.trim();
  if (!newTitle) {
    return;
  }
  await apiRequest(`/v1/projects/${state.projectId}/chats/${state.chatId}`, {
    method: "PATCH",
    body: JSON.stringify({ title: newTitle }),
  });
  await refreshConversation();
});

els.projectSelect.addEventListener("change", async () => {
  state.projectId = els.projectSelect.value;
  state.chatId = "";
  persistProjectId(state.projectId);
  persistChatId(state.projectId, "");
  await refreshConversation();
});

els.chatSelect.addEventListener("change", async () => {
  state.chatId = els.chatSelect.value;
  persistChatId(state.projectId, state.chatId);
  await loadMessages();
});

els.clearMessagesBtn.addEventListener("click", () => {
  els.messages.innerHTML = "";
  const placeholder = document.createElement("div");
  placeholder.className = "message system";
  if (!state.projectId) {
    placeholder.textContent = "Нет проекта. Создайте проект вручную.";
  } else if (!state.chatId) {
    placeholder.textContent = "В выбранном проекте нет чатов. Создайте чат вручную.";
  } else {
    placeholder.textContent = "История чата очищена в интерфейсе.";
  }
  els.messages.appendChild(placeholder);
});

els.clearChatMemoryBtn.addEventListener("click", async () => {
  if (!state.projectId || !state.chatId) {
    return;
  }
  await apiRequest(`/v1/projects/${state.projectId}/chats/${state.chatId}/messages`, {
    method: "DELETE",
  });
  await loadMessages();
});

els.messageInput.addEventListener("keydown", (event) => {
  if (event.key === "Enter" && !event.shiftKey) {
    event.preventDefault();
    if (!els.messageInput.value.trim()) {
      return;
    }
    els.chatForm.requestSubmit();
  }
});

els.chatForm.addEventListener("submit", async (event) => {
  event.preventDefault();
  const messageText = els.messageInput.value.trim();
  if (!messageText) {
    return;
  }
  if (!state.projectId) {
    setStatus("Сначала выберите или создайте проект.");
    return;
  }
  if (!state.chatId) {
    setStatus("Сначала выберите или создайте чат.");
    return;
  }

  els.sendButton.disabled = true;
  setStatus("Отправляю сообщение...");
  const stopSendingFeedback = setSendingFeedback(Date.now());
  try {
    await sendTurn(messageText);
    els.messageInput.value = "";
  } catch (error) {
    setStatus(`Ошибка отправки: ${error.message}`);
  } finally {
    stopSendingFeedback();
    els.sendButton.disabled = false;
    if (!els.healthStatus.textContent.startsWith("Ошибка отправки") && !els.healthStatus.textContent.startsWith("Ожидаем")) {
      setStatus("Готов");
    }
  }
});

els.apiBase.value = window.location.origin;

(async function init() {
  loadStateFromStorage();
  try {
    await checkHealth();
    await refreshConversation();
  } catch (error) {
    setStatus(`Не удалось инициализироваться: ${error.message}`);
  }
})();
