<script setup>
import { ref, computed, onMounted, reactive } from "vue";
import { request } from "../api";
import { t } from "../i18n";

// Superadmin-only. Each plugin advertises its own config fields (Descriptor.
// ConfigFields), so the form below is generated rather than hard-coded — that's
// what lets an external plugin be added without touching this panel.
const plugins = ref([]);
const error = ref("");
const busy = ref(false);
const open = ref(null); // name of the expanded plugin
const drafts = reactive({}); // name -> { key: value }
const rowNotice = reactive({}); // name -> string
const showRegister = ref(false);
const reg = ref({ name: "", baseURL: "", provider: "" });
const tab = ref(""); // selected category tab; empty falls back to the first one

// Tab order, mirroring the Category* constants in internal/plugins/plugin.go. A
// plugin whose category is empty or unknown to this panel lands under the
// external-APIs tab rather than disappearing.
const CATEGORIES = ["vehicles", "chargers", "notifications", "apis-external", "drives-external", "drives-local"];
const categoryOf = (p) => (CATEGORIES.includes(p.category) ? p.category : "apis-external");

// Only categories that actually have a plugin get a tab — a fresh install with
// two builtins shows two tabs, not five empty ones.
const groups = computed(() => {
  const by = {};
  for (const p of plugins.value) (by[categoryOf(p)] ||= []).push(p);
  return CATEGORIES.filter((c) => by[c]).map((c) => ({
    id: c,
    label: t(`plugins.categories.${c}`),
    plugins: by[c],
  }));
});

// Falling back to the first group keeps the card populated when the selected
// tab's last plugin is removed (or a reload drops it).
const activeGroup = computed(() => groups.value.find((g) => g.id === tab.value) || groups.value[0]);
const shown = computed(() => activeGroup.value?.plugins || []);

async function load() {
  try {
    const out = await request("/api/admin/plugins");
    plugins.value = out.plugins || [];
    error.value = "";
  } catch (e) {
    error.value = e.message;
  }
}
onMounted(load);

function expand(p) {
  if (open.value === p.name) {
    open.value = null;
    return;
  }
  // Seed the draft from the (secret-masked) stored config plus field defaults.
  const d = {};
  for (const f of p.configFields || []) d[f.key] = p.config?.[f.key] ?? "";
  drafts[p.name] = d;
  open.value = p.name;
}

async function save(p, enabled) {
  busy.value = true;
  rowNotice[p.name] = "";
  try {
    const out = await request(`/api/admin/plugins/${encodeURIComponent(p.name)}`, {
      method: "PUT",
      body: { enabled, config: drafts[p.name] ?? {} },
    });
    // A save can succeed while Init fails (e.g. bad credentials) — the server
    // returns the saved plugin plus a warning.
    rowNotice[p.name] = out.warning || t("plugins.saved");
    await load();
  } catch (e) {
    rowNotice[p.name] = e.message;
  } finally {
    busy.value = false;
  }
}

async function health(p) {
  busy.value = true;
  rowNotice[p.name] = t("plugins.checking");
  try {
    const out = await request(`/api/admin/plugins/${encodeURIComponent(p.name)}/health`, {
      method: "POST",
    });
    rowNotice[p.name] = `${out.health.status}${out.health.detail ? " — " + out.health.detail : ""}`;
    await load();
  } catch (e) {
    rowNotice[p.name] = e.message;
  } finally {
    busy.value = false;
  }
}

async function remove(p) {
  if (!confirm(t("plugins.confirmRemove", { name: p.name }))) return;
  busy.value = true;
  try {
    await request(`/api/admin/plugins/${encodeURIComponent(p.name)}`, { method: "DELETE" });
    if (open.value === p.name) open.value = null;
    await load();
  } catch (e) {
    rowNotice[p.name] = e.message;
  } finally {
    busy.value = false;
  }
}

async function registerExternal() {
  busy.value = true;
  error.value = "";
  try {
    await request("/api/admin/plugins", { method: "POST", body: reg.value });
    reg.value = { name: "", baseURL: "", provider: "" };
    showRegister.value = false;
    await load();
  } catch (e) {
    error.value = e.message;
  } finally {
    busy.value = false;
  }
}

const healthClass = (s) =>
  s === "ok"
    ? "bg-success-soft text-success"
    : s === "degraded"
      ? "bg-warning-soft text-warning"
      : "bg-danger-soft text-danger";
</script>

<template>
  <div class="dh-card overflow-hidden">
    <div class="flex items-center justify-between border-b border-subtle px-5 py-4">
      <div>
        <div class="text-base font-bold tracking-[-0.02em] text-strong">{{ t("plugins.title") }}</div>
        <p class="mt-0.5 text-xs text-muted">{{ t("plugins.subtitle") }}</p>
      </div>
      <button class="dh-btn-ghost" @click="showRegister = !showRegister">
        {{ showRegister ? t("plugins.cancelRegister") : t("plugins.registerExternal") }}
      </button>
    </div>

    <!-- Register an external (remote HTTP) plugin — the no-rebuild path. -->
    <div v-if="showRegister" class="border-b border-subtle bg-sunken px-5 py-4">
      <div class="grid gap-3 sm:grid-cols-3">
        <div>
          <label class="dh-label">{{ t("plugins.name") }}</label>
          <input v-model="reg.name" class="dh-input" placeholder="acme-parts" />
        </div>
        <div>
          <label class="dh-label">{{ t("plugins.baseUrl") }}</label>
          <input v-model="reg.baseURL" class="dh-input" placeholder="http://127.0.0.1:9100" />
        </div>
        <div>
          <label class="dh-label">{{ t("plugins.provider") }}</label>
          <input v-model="reg.provider" class="dh-input" placeholder="ACME Corp" />
        </div>
      </div>
      <button
        class="dh-btn mt-3"
        :disabled="busy || !reg.name || !reg.baseURL"
        @click="registerExternal"
      >
        {{ t("plugins.register") }}
      </button>
    </div>

    <p v-if="error" class="border-b border-subtle px-5 py-3 text-xs text-danger">{{ error }}</p>

    <p v-if="!plugins.length" class="px-5 py-6 text-center text-sm text-muted">
      {{ t("plugins.empty") }}
    </p>

    <!-- Category tabs: car manufacturers, EV chargers, everything else. -->
    <nav v-if="groups.length > 1" class="flex flex-wrap gap-1.5 border-b border-subtle px-5 py-3">
      <button
        v-for="g in groups"
        :key="g.id"
        class="dh-btn-ghost"
        :class="activeGroup?.id === g.id ? 'border-accent text-brandtext' : ''"
        @click="tab = g.id"
      >
        {{ g.label }}
        <span class="dh-pill bg-sunken text-muted">{{ g.plugins.length }}</span>
      </button>
    </nav>

    <div v-for="p in shown" :key="p.name" class="border-t border-subtle first:border-t-0">
      <!-- Summary row -->
      <div class="flex items-center gap-3 px-5 py-3">
        <button class="flex flex-1 items-center gap-3 text-left" @click="expand(p)">
          <span class="font-semibold text-strong">{{ p.name }}</span>
          <span class="dh-pill bg-sunken text-muted">{{ p.kind || t("plugins.builtin") }}</span>
          <span v-if="p.provider" class="text-xs text-muted">{{ p.provider }}</span>
          <span v-if="p.health" class="dh-pill" :class="healthClass(p.health.status)">
            <span class="h-1.5 w-1.5 rounded-full bg-current"></span>{{ p.health.status }}
          </span>
        </button>
        <span class="dh-pill" :class="p.enabled ? 'bg-success-soft text-success' : 'bg-sunken text-muted'">
          {{ p.enabled ? t("plugins.enabled") : t("plugins.disabled") }}
        </span>
        <button class="dh-btn-ghost" :disabled="busy" @click="health(p)">{{ t("plugins.health") }}</button>
        <button
          class="dh-btn-ghost"
          :disabled="busy"
          @click="expand(p)"
        >
          {{ open === p.name ? t("plugins.close") : t("plugins.configure") }}
        </button>
      </div>

      <!-- Expanded config: generated from the plugin's declared fields. -->
      <div v-if="open === p.name" class="bg-sunken px-5 py-4">
        <div v-if="p.baseURL" class="data mb-3 text-xs text-muted">{{ p.baseURL }}</div>

        <div v-if="(p.configFields || []).length" class="grid gap-3 sm:grid-cols-2">
          <div v-for="f in p.configFields" :key="f.key">
            <label class="dh-label">
              {{ f.label || f.key }}<span v-if="f.required" class="text-danger"> *</span>
            </label>
            <select v-if="f.type === 'select'" v-model="drafts[p.name][f.key]" class="dh-select">
              <!-- A non-required select can be left unset (empty), so the global
                   layer abstains and lower layers (org / user) may choose. -->
              <option v-if="!f.required" value="">{{ t("plugins.notSet") }}</option>
              <option v-for="o in f.options || []" :key="o.value" :value="o.value">
                {{ o.label || o.value }}
              </option>
            </select>
            <input
              v-else
              v-model="drafts[p.name][f.key]"
              class="dh-input"
              :type="f.type === 'password' ? 'password' : f.type === 'number' ? 'number' : 'text'"
              :placeholder="f.default || ''"
              autocomplete="off"
            />
            <p v-if="f.help" class="mt-1 text-xs text-muted">{{ f.help }}</p>
          </div>
        </div>
        <p v-else class="text-xs text-muted">{{ t("plugins.noConfig") }}</p>

        <div v-if="(p.capabilities || []).length" class="mt-4">
          <div class="eyebrow mb-1.5">{{ t("plugins.capabilities") }}</div>
          <ul class="data flex flex-col gap-1 text-xs text-muted">
            <li v-for="c in p.capabilities" :key="c.id">
              <span class="text-strong">{{ c.id }}</span>
              <span v-if="c.method || c.endpoint"> — {{ c.method }} {{ c.endpoint }}</span>
              <span v-if="c.description"> · {{ c.description }}</span>
            </li>
          </ul>
        </div>

        <p v-if="rowNotice[p.name]" class="data mt-3 text-xs text-body">{{ rowNotice[p.name] }}</p>

        <div class="mt-4 flex items-center gap-2">
          <button class="dh-btn" :disabled="busy" @click="save(p, true)">
            {{ p.enabled ? t("plugins.save") : t("plugins.saveEnable") }}
          </button>
          <button v-if="p.enabled" class="dh-btn-ghost" :disabled="busy" @click="save(p, false)">
            {{ t("plugins.disable") }}
          </button>
          <span class="flex-1"></span>
          <button
            v-if="p.kind === 'external'"
            class="dh-btn-danger"
            :disabled="busy"
            @click="remove(p)"
          >
            {{ t("plugins.remove") }}
          </button>
        </div>
        <p v-if="p.kind !== 'external'" class="eyebrow mt-2">
          {{ t("plugins.builtinNote") }}
        </p>
      </div>
    </div>
  </div>
</template>
