import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";
import * as diff from "diff";

type RewriteMetrics = {
  duration_ms: number;
  input_length: number;
  model: string;
  success: boolean;
  error_category?: string | null;
};

type RuntimeStatus = {
  hotkey_registered: boolean;
  background_mode: boolean;
  active_model: string;
  api_key_configured: boolean;
  last_error?: string | null;
  rewrite_attempts: number;
  rewrite_successes: number;
  rewrite_failures: number;
  last_metrics?: RewriteMetrics | null;
};

function requireElement<T extends HTMLElement>(id: string): T {
  const element = document.getElementById(id);
  if (!element) {
    throw new Error(`Missing required element: ${id}`);
  }
  return element as T;
}

const inputEl = requireElement<HTMLTextAreaElement>("input-text");
const outputEl = requireElement<HTMLTextAreaElement>("output-text");
const modelSelect = requireElement<HTMLSelectElement>("model-select");
const modeSelect = requireElement<HTMLSelectElement>("mode-select");
const rewriteBtn = requireElement<HTMLButtonElement>("rewrite-btn");
const copyBtn = requireElement<HTMLButtonElement>("copy-btn");
const toggleDiffBtn = requireElement<HTMLButtonElement>("toggle-diff-btn");
const diffView = requireElement<HTMLDivElement>("diff-view");
const loadingMsg = requireElement<HTMLDivElement>("loading");
const settingsBtn = requireElement<HTMLButtonElement>("settings-btn");
const settingsModal = requireElement<HTMLDivElement>("settings-modal");
const closeSettingsBtn = requireElement<HTMLButtonElement>("close-settings-btn");
const saveSettingsBtn = requireElement<HTMLButtonElement>("save-settings-btn");
const apiKeyInput = requireElement<HTMLInputElement>("api-key-input");
const runtimeStatusEl = requireElement<HTMLDivElement>("runtime-status");
const helperBanner = requireElement<HTMLDivElement>("helper-banner");
const helperBannerText = requireElement<HTMLParagraphElement>("helper-banner-text");
const hideToTrayBtn = requireElement<HTMLButtonElement>("hide-to-tray-btn");
const dismissBannerBtn = requireElement<HTMLButtonElement>("dismiss-banner-btn");

let showDiff = false;

function renderRuntimeStatus(status: RuntimeStatus) {
  const metricParts: string[] = [];
  if (status.last_metrics) {
    metricParts.push(`${status.last_metrics.duration_ms} ms`);
    metricParts.push(`${status.last_metrics.input_length} chars`);
    metricParts.push(status.last_metrics.success ? "last run ok" : "last run failed");
    if (status.last_metrics.error_category) {
      metricParts.push(status.last_metrics.error_category);
    }
  }

  const lines = [
    `Hotkey: ${status.hotkey_registered ? "Ctrl + Alt + R active" : "not registered"}`,
    `Window: ${status.background_mode ? "hidden to tray" : "visible"}`,
    `Model: ${status.active_model}`,
    `API key: ${status.api_key_configured ? "configured" : "missing"}`,
    `Rewrites: ${status.rewrite_successes}/${status.rewrite_attempts} successful`,
  ];

  if (metricParts.length > 0) {
    lines.push(`Last run: ${metricParts.join(" | ")}`);
  }

  if (status.last_error) {
    lines.push(`Last error: ${status.last_error}`);
  }

  runtimeStatusEl.textContent = lines.join(" | ");
}

async function refreshRuntimeStatus() {
  try {
    const status = await invoke<RuntimeStatus>("get_runtime_status");
    renderRuntimeStatus(status);
  } catch (error) {
    console.error("Failed to load runtime status", error);
  }
}

function renderDiff(original: string, rewritten: string) {
  const diffResult = diff.diffWords(original, rewritten);
  diffView.innerHTML = "";
  diffResult.forEach((part) => {
    const span = document.createElement("span");
    span.textContent = part.value;
    if (part.added) {
      span.className = "diff-add";
    } else if (part.removed) {
      span.className = "diff-del";
    }
    diffView.appendChild(span);
  });
}

function showBanner(message: string) {
  helperBannerText.textContent = message;
  helperBanner.classList.remove("hidden");
}

function hideBanner() {
  helperBanner.classList.add("hidden");
}

modelSelect.addEventListener("change", async () => {
  try {
    await invoke("set_active_model", { model: modelSelect.value });
    await refreshRuntimeStatus();
  } catch (error) {
    console.error("Failed to sync active model", error);
  }
});

void invoke("set_active_model", { model: modelSelect.value })
  .then(refreshRuntimeStatus)
  .catch((error) => {
    console.error("Failed to initialize active model", error);
  });

settingsBtn.addEventListener("click", () => {
  settingsModal.classList.remove("hidden");
});

closeSettingsBtn.addEventListener("click", () => {
  settingsModal.classList.add("hidden");
});

dismissBannerBtn.addEventListener("click", hideBanner);

hideToTrayBtn.addEventListener("click", async () => {
  try {
    await invoke("hide_main_window");
    hideBanner();
    await refreshRuntimeStatus();
  } catch (error) {
    console.error("Failed to hide main window", error);
  }
});

saveSettingsBtn.addEventListener("click", async () => {
  const key = apiKeyInput.value.trim();
  if (!key) return;

  try {
    saveSettingsBtn.disabled = true;
    saveSettingsBtn.textContent = "Saving...";
    await invoke("save_api_key", { key });
    settingsModal.classList.add("hidden");
    apiKeyInput.value = "";
    await refreshRuntimeStatus();
    alert("API key saved to your system credential store.");
  } catch (err) {
    alert("Error saving API key: " + err);
  } finally {
    saveSettingsBtn.disabled = false;
    saveSettingsBtn.textContent = "Save Settings";
  }
});

async function checkStatus() {
  try {
    const isSet = await invoke<boolean>("get_api_key_status");
    if (!isSet) {
      settingsModal.classList.remove("hidden");
    }
  } catch (error) {
    console.error("Status check failed", error);
  }
}

rewriteBtn.addEventListener("click", async () => {
  const text = inputEl.value;
  if (!text.trim()) return;

  loadingMsg.classList.remove("hidden");
  outputEl.classList.add("hidden");
  diffView.classList.add("hidden");
  rewriteBtn.disabled = true;

  try {
    const mode = modeSelect.value;
    const model = modelSelect.value;
    const result = await invoke<string>("rewrite_text", {
      text,
      modeStr: mode,
      modelStr: model,
    });

    outputEl.value = result;
    renderDiff(text, result);

    if (showDiff) {
      diffView.classList.remove("hidden");
    } else {
      outputEl.classList.remove("hidden");
    }
  } catch (error) {
    console.error("Rewrite failed:", error);
    outputEl.value = `Error: ${error}`;
    outputEl.classList.remove("hidden");
  } finally {
    loadingMsg.classList.add("hidden");
    rewriteBtn.disabled = false;
    await refreshRuntimeStatus();
  }
});

toggleDiffBtn.addEventListener("click", () => {
  showDiff = !showDiff;
  if (showDiff) {
    toggleDiffBtn.textContent = "Show Text";
    outputEl.classList.add("hidden");
    diffView.classList.remove("hidden");
  } else {
    toggleDiffBtn.textContent = "Show Diff";
    diffView.classList.add("hidden");
    outputEl.classList.remove("hidden");
  }
});

copyBtn.addEventListener("click", async () => {
  try {
    await navigator.clipboard.writeText(outputEl.value);
    const originalText = copyBtn.textContent;
    copyBtn.textContent = "Copied!";
    setTimeout(() => {
      copyBtn.textContent = originalText;
    }, 2000);
  } catch (error) {
    console.error("Failed to copy", error);
  }
});

void listen<string>("tray-hint", (event) => {
  showBanner(event.payload);
});

void listen<RuntimeStatus>("runtime-status", (event) => {
  renderRuntimeStatus(event.payload);
});

void Promise.all([checkStatus(), refreshRuntimeStatus()]);
