<script setup>
import { ref, computed, onMounted } from "vue";
import { theme, toggleTheme } from "./theme";
import { t, lang, setLang, TRANSLATED_LANGUAGES } from "./i18n";
import { me, token, restore, logout, isManager, isSuperadmin } from "./api";
import Logo from "./components/Logo.vue";
import LoginView from "./components/LoginView.vue";
import StatusCard from "./components/StatusCard.vue";
import PocketBaseCard from "./components/PocketBaseCard.vue";
import ServerCard from "./components/ServerCard.vue";
import WebAppCard from "./components/WebAppCard.vue";
import PluginsCard from "./components/PluginsCard.vue";
import UsersCard from "./components/UsersCard.vue";
import OrgsCard from "./components/OrgsCard.vue";
import EndpointTable from "./components/EndpointTable.vue";

const booting = ref(true);
const section = ref("overview");

onMounted(async () => {
  await restore(); // re-validate a token kept from a previous visit
  booting.value = false;
});

// Cards are gated by role: management needs a manager, PocketBase, Web App +
// plugins need a superadmin. The server enforces the same rules — this only
// hides what the caller could not use anyway.
const sections = computed(() => {
  const out = [{ id: "overview", label: t("sections.overview") }];
  if (isManager.value) {
    out.push({ id: "users", label: t("sections.users") });
  }
  // Organizations is open to everyone: a manager sees the org(s) they manage,
  // and an org-less user gets the form that stands one up (making them its
  // admin). Only a caller with neither an org nor a manager role sees nothing
  // to do there, and they still get the create form.
  if (isManager.value || me.value) {
    out.push({ id: "orgs", label: t("sections.orgs") });
  }
  if (isSuperadmin.value) {
    out.push(
      { id: "server", label: t("sections.server") },
      { id: "pocketbase", label: t("sections.pocketbase") },
      { id: "webapp", label: t("sections.webapp") },
      { id: "plugins", label: t("sections.plugins") },
    );
  }
  out.push({ id: "api", label: t("sections.api") });
  return out;
});

// The language picker offers only the languages that have a real file. English
// names ("Polski", "Dansk") come from Intl so each reads in its own language.
const languageOptions = computed(() =>
  TRANSLATED_LANGUAGES.map((code) => {
    let label = code;
    try {
      label = new Intl.DisplayNames([code], { type: "language" }).of(code) || code;
    } catch {
      /* runtime without DisplayNames — the code is still selectable */
    }
    return { code, label: label.charAt(0).toUpperCase() + label.slice(1) };
  }),
);

function signOut() {
  logout();
  section.value = "overview";
}

// The DriverVault REST surface, grouped by resource. Mirrors the routes
// registered in internal/api/server.go.
const publicApi = [
  { method: "GET", path: "/api/health", desc: "Liveness probe (no auth)" },
  { method: "GET", path: "/healthz", desc: "The same probe under the conventional container path" },
  { method: "GET", path: "/api/status", desc: "Health of PocketBase + Web App" },
  { method: "POST", path: "/api/auth/login", desc: "Exchange email + password for a PocketBase token" },
  { method: "GET", path: "/api/auth/validate", desc: "Check whether a token is still valid" },
];

const identityApi = [
  { method: "GET", path: "/api/auth/me", desc: "Identity of the bearer token" },
  { method: "GET", path: "/api/identity", desc: "Identity incl. role + organization" },
];

const carsApi = [
  { method: "GET", path: "/api/cars", desc: "List owned + shared cars" },
  { method: "POST", path: "/api/cars", desc: "Create a car" },
  { method: "GET", path: "/api/cars/{id}", desc: "Fetch one car" },
  { method: "PATCH", path: "/api/cars/{id}", desc: "Update a car" },
  { method: "PUT", path: "/api/cars/{id}/view", desc: "Save the car's layout — hidden tabs/fields/columns and their order" },
  { method: "DELETE", path: "/api/cars/{id}", desc: "Delete a car (owner only)" },
  { method: "GET", path: "/api/cars/{id}/service-records", desc: "A car's service history" },
  { method: "GET", path: "/api/cars/{id}/technical-checks", desc: "A car's roadworthiness inspections" },
  { method: "GET", path: "/api/cars/{id}/parts", desc: "A car's parts catalog" },
  { method: "GET", path: "/api/cars/{id}/fuel-entries", desc: "A car's refuelings" },
  { method: "GET", path: "/api/cars/{id}/fuel-stats", desc: "Consumption + cost totals from the fuel log" },
  { method: "GET", path: "/api/cars/{id}/charging-sessions", desc: "A car's charging sessions" },
  { method: "GET", path: "/api/cars/{id}/charging-stats", desc: "Energy + cost totals from the charging log" },
  { method: "GET", path: "/api/cars/{id}/maintenance", desc: "A car's maintenance log" },
  { method: "GET", path: "/api/cars/{id}/documents", desc: "A car's documents" },
  { method: "GET", path: "/api/cars/{id}/reminders", desc: "A car's reminders" },
  { method: "GET", path: "/api/cars/{id}/provider", desc: "Live snapshot from the car's connected service" },
  { method: "POST", path: "/api/cars/{id}/provider", desc: "Link the car to a provider vehicle (an empty provider unlinks)" },
  { method: "POST", path: "/api/cars/{id}/provider/sync", desc: "Re-apply the provider's data to the car" },
  { method: "GET", path: "/api/cars/{id}/shares", desc: "Who a car is shared with (owner)" },
  { method: "POST", path: "/api/cars/{id}/shares", desc: "Share a car by email (owner)" },
  { method: "DELETE", path: "/api/cars/{id}/shares/{userId}", desc: "Revoke a share (owner)" },
];

const serviceApi = [
  { method: "GET", path: "/api/service-records", desc: "List one car's service records (?car={id})" },
  { method: "POST", path: "/api/service-records", desc: "Log a service record" },
  { method: "GET", path: "/api/service-records/{id}", desc: "Fetch one record" },
  { method: "PATCH", path: "/api/service-records/{id}", desc: "Update a record" },
  { method: "DELETE", path: "/api/service-records/{id}", desc: "Delete a record" },
];

const technicalApi = [
  { method: "GET", path: "/api/technical-checks", desc: "List one car's inspections (?car={id})" },
  { method: "POST", path: "/api/technical-checks", desc: "Log an inspection" },
  { method: "GET", path: "/api/technical-checks/{id}", desc: "Fetch one inspection" },
  { method: "PATCH", path: "/api/technical-checks/{id}", desc: "Update an inspection" },
  { method: "DELETE", path: "/api/technical-checks/{id}", desc: "Delete an inspection" },
];

const partsApi = [
  { method: "GET", path: "/api/parts", desc: "List one car's parts (?car={id})" },
  { method: "POST", path: "/api/parts", desc: "Add a part" },
  { method: "GET", path: "/api/parts/{id}", desc: "Fetch one part" },
  { method: "PATCH", path: "/api/parts/{id}", desc: "Update a part" },
  { method: "DELETE", path: "/api/parts/{id}", desc: "Delete a part" },
];

const fuelApi = [
  { method: "GET", path: "/api/fuel-entries", desc: "List one car's refuelings (?car={id})" },
  { method: "POST", path: "/api/fuel-entries", desc: "Log a refueling" },
  { method: "GET", path: "/api/fuel-entries/{id}", desc: "Fetch one refueling" },
  { method: "PATCH", path: "/api/fuel-entries/{id}", desc: "Update a refueling" },
  { method: "DELETE", path: "/api/fuel-entries/{id}", desc: "Delete a refueling" },
];

const chargingApi = [
  { method: "GET", path: "/api/charging-sessions", desc: "List one car's charging sessions (?car={id})" },
  { method: "POST", path: "/api/charging-sessions", desc: "Log a charging session" },
  { method: "GET", path: "/api/charging-sessions/{id}", desc: "Fetch one session" },
  { method: "PATCH", path: "/api/charging-sessions/{id}", desc: "Update a session" },
  { method: "DELETE", path: "/api/charging-sessions/{id}", desc: "Delete a session" },
];

const maintenanceApi = [
  { method: "GET", path: "/api/maintenance", desc: "List one car's maintenance jobs (?car={id})" },
  { method: "POST", path: "/api/maintenance", desc: "Log a maintenance job" },
  { method: "GET", path: "/api/maintenance/{id}", desc: "Fetch one job" },
  { method: "PATCH", path: "/api/maintenance/{id}", desc: "Update a job" },
  { method: "DELETE", path: "/api/maintenance/{id}", desc: "Delete a job" },
];

const documentsApi = [
  { method: "GET", path: "/api/car-documents", desc: "List one car's documents (?car={id})" },
  { method: "POST", path: "/api/car-documents", desc: "Add a document" },
  { method: "GET", path: "/api/car-documents/{id}", desc: "Fetch one document" },
  { method: "PATCH", path: "/api/car-documents/{id}", desc: "Update a document" },
  { method: "DELETE", path: "/api/car-documents/{id}", desc: "Delete a document" },
];

const remindersApi = [
  { method: "GET", path: "/api/reminders", desc: "List one car's reminders (?car={id})" },
  { method: "POST", path: "/api/reminders", desc: "Create a reminder" },
  { method: "GET", path: "/api/reminders/{id}", desc: "Fetch one reminder" },
  { method: "PATCH", path: "/api/reminders/{id}", desc: "Update a reminder" },
  { method: "DELETE", path: "/api/reminders/{id}", desc: "Delete a reminder" },
  { method: "POST", path: "/api/reminders/{id}/complete", desc: "Mark done — a recurring reminder rolls forward from now" },
];

// One optional file per record, on identical terms for every collection that
// takes one, so the three routes are documented once with {records} standing
// for any of them (see the note under the table).
const attachmentsApi = [
  { method: "POST", path: "/api/{records}/{id}/file", desc: "Attach a file, replacing any previous one (PDF or image, max 10 MB)" },
  { method: "GET", path: "/api/{records}/{id}/file", desc: "Download the attachment (car access re-checked on every request)" },
  { method: "DELETE", path: "/api/{records}/{id}/file", desc: "Detach and delete the file" },
];

const attachmentsNote =
  "{records} is any of service-records, technical-checks, maintenance, fuel-entries, charging-sessions, car-documents, parts.";

const providersApi = [
  { method: "GET", path: "/api/vehicle-providers", desc: "Registered providers and whether the caller can use each one" },
  { method: "GET", path: "/api/vehicle-providers/{provider}/vehicles", desc: "Vehicles on the caller's provider account, as importable cars" },
  { method: "POST", path: "/api/vehicle-providers/{provider}/import", desc: "Create a car from a provider vehicle (409 if already imported)" },
];

const integrationsApi = [
  { method: "GET", path: "/api/integrations/toyota", desc: "Resolved Toyota Connected settings (secrets masked)" },
  { method: "PUT", path: "/api/integrations/toyota", desc: "Save the caller's own layer (user or org scope)" },
  { method: "POST", path: "/api/integrations/toyota/health", desc: "Live probe with the resolved credentials" },
  { method: "GET", path: "/api/integrations/toyota/vehicles", desc: "Raw MyToyota vehicle payload" },
  { method: "GET", path: "/api/integrations/anker-solix", desc: "Resolved Anker Solix settings (secrets masked)" },
  { method: "PUT", path: "/api/integrations/anker-solix", desc: "Save the caller's own layer (user or org scope)" },
  { method: "POST", path: "/api/integrations/anker-solix/health", desc: "Live probe with the resolved credentials" },
  { method: "GET", path: "/api/integrations/anker-solix/chargers", desc: "EV chargers on the linked Anker account" },
  { method: "GET", path: "/api/integrations/anker-solix/chargers/{sn}/control", desc: "Control mode, token state and live session or Modbus snapshot for one charger" },
  { method: "POST", path: "/api/integrations/anker-solix/chargers/{sn}/control/token", desc: "(Re)issue the charger's control token" },
  { method: "DELETE", path: "/api/integrations/anker-solix/chargers/{sn}/control/token", desc: "Revoke the control token" },
  { method: "PUT", path: "/api/integrations/anker-solix/chargers/{sn}/control/address", desc: "Save the charger's local Modbus TCP address" },
  { method: "DELETE", path: "/api/integrations/anker-solix/chargers/{sn}/control/address", desc: "Forget the charger's local Modbus TCP address" },
  { method: "POST", path: "/api/integrations/anker-solix/chargers/{sn}/{action}", desc: "One control command. Over OCPP: start, stop, limit, clear-limit, availability, reset, unlock, trigger, config. Over Modbus TCP: start, stop, limit, boost, phase, timeout, status" },
  { method: "GET", path: "/api/integrations/greencell", desc: "Resolved Greencell settings (secrets masked)" },
  { method: "PUT", path: "/api/integrations/greencell", desc: "Save the caller's own layer (user or org scope)" },
  { method: "POST", path: "/api/integrations/greencell/health", desc: "Live probe: connect to the broker and broadcast for devices" },
  { method: "GET", path: "/api/integrations/greencell/chargers", desc: "Greencell chargers answering on the configured MQTT broker" },
  { method: "GET", path: "/api/integrations/greencell/chargers/{sn}/state", desc: "Live EVSE state, access level, power and per-phase current/voltage" },
];

const ocppApi = [
  { method: "GET", path: "/ocpp/{serial}", desc: "WebSocket the charger dials out to; OCPP Basic auth with the serial + its control token" },
];

const accountApi = [
  { method: "GET", path: "/api/me", desc: "Current user profile" },
  { method: "PATCH", path: "/api/me", desc: "Update profile" },
  { method: "POST", path: "/api/me/password", desc: "Change password" },
  { method: "POST", path: "/api/me/avatar", desc: "Upload avatar" },
  { method: "GET", path: "/api/me/avatar", desc: "Fetch avatar" },
  { method: "DELETE", path: "/api/me/avatar", desc: "Remove avatar" },
  { method: "POST", path: "/api/me/verify/request", desc: "Request email verification" },
  { method: "GET", path: "/api/me/export", desc: "Export your data" },
  { method: "POST", path: "/api/me/import", desc: "Import data" },
  { method: "POST", path: "/api/me/delete", desc: "Request account deletion" },
  { method: "POST", path: "/api/me/delete/cancel", desc: "Cancel deletion request" },
  { method: "DELETE", path: "/api/me", desc: "Finalize account deletion" },
];

const managementApi = [
  { method: "GET", path: "/api/users", desc: "List users (scoped by role)" },
  { method: "POST", path: "/api/users", desc: "Create a user" },
  { method: "PATCH", path: "/api/users/{id}", desc: "Update email / name / role / org / password" },
  { method: "DELETE", path: "/api/users/{id}", desc: "Delete a user" },
  { method: "GET", path: "/api/orgs", desc: "List organizations" },
  { method: "POST", path: "/api/orgs", desc: "Create an organization (any user without one; creator becomes admin)" },
  { method: "PATCH", path: "/api/orgs/{id}", desc: "Rename an organization (own org; any as superadmin)" },
  { method: "DELETE", path: "/api/orgs/{id}", desc: "Delete an organization (own org; any as superadmin)" },
];

const superadminApi = [
  { method: "GET", path: "/api/admin/pb-config", desc: "PocketBase connection + live probe" },
  { method: "POST", path: "/api/admin/pb-config/test", desc: "Probe a candidate connection" },
  { method: "PUT", path: "/api/admin/pb-config", desc: "Apply + persist a connection" },
  { method: "GET", path: "/api/admin/server-config", desc: "This server's display name" },
  { method: "PUT", path: "/api/admin/server-config", desc: "Rename this server (applied + persisted)" },
  { method: "GET", path: "/api/admin/webapp-config", desc: "Web App URL + CORS origins, with a live probe" },
  { method: "POST", path: "/api/admin/webapp-config/test", desc: "Probe a candidate Web App address" },
  { method: "PUT", path: "/api/admin/webapp-config", desc: "Apply + persist Web App settings" },
  { method: "GET", path: "/api/admin/plugins", desc: "List plugins (secrets masked)" },
  { method: "POST", path: "/api/admin/plugins", desc: "Register an external plugin" },
  { method: "GET", path: "/api/admin/plugins/{name}", desc: "Fetch one plugin" },
  { method: "PUT", path: "/api/admin/plugins/{name}", desc: "Enable/disable + configure" },
  { method: "DELETE", path: "/api/admin/plugins/{name}", desc: "Remove an external plugin" },
  { method: "POST", path: "/api/admin/plugins/{name}/health", desc: "Run a health check now" },
];

</script>

<template>
  <div class="mx-auto flex max-w-[1368px] flex-col gap-6 px-6 pt-12 pb-16">
    <!-- Header -->
    <div class="flex items-center gap-3">
      <!-- The shared DriverVault lockup, on-dark on the dark shell. -->
      <Logo :on-dark="theme === 'dark'" class="h-7" />
      <span class="eyebrow mt-1.5">{{ t("app.apiServer") }}</span>
      <div class="flex-1"></div>

      <span v-if="me" class="data hidden text-xs text-muted sm:inline">
        {{ me.email }}<span v-if="me.organizationName"> · {{ me.organizationName }}</span>
      </span>
      <span v-if="me" class="dh-pill bg-info-soft text-info">{{ me.role }}</span>

      <select
        class="dh-select !w-auto !py-1 !text-xs"
        :value="lang"
        :aria-label="t('sections.overview')"
        @change="setLang($event.target.value)"
      >
        <option v-for="l in languageOptions" :key="l.code" :value="l.code">{{ l.label }}</option>
      </select>

      <button
        class="dh-btn-ghost"
        :title="theme === 'dark' ? t('app.switchToLight') : t('app.switchToDark')"
        @click="toggleTheme"
      >
        <svg
          v-if="theme === 'dark'"
          xmlns="http://www.w3.org/2000/svg"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="1.75"
          class="h-4 w-4"
        >
          <circle cx="12" cy="12" r="4" />
          <path stroke-linecap="round" d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4" />
        </svg>
        <svg
          v-else
          xmlns="http://www.w3.org/2000/svg"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="1.75"
          class="h-4 w-4"
        >
          <path stroke-linecap="round" stroke-linejoin="round" d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8Z" />
        </svg>
        {{ t("app.theme") }}
      </button>
      <button v-if="token" class="dh-btn-ghost" @click="signOut">{{ t("app.signOut") }}</button>
    </div>

    <p v-if="booting" class="eyebrow py-16 text-center">{{ t("app.loading") }}</p>

    <!-- Unauthenticated: the login gate is the whole console. -->
    <LoginView v-else-if="!token" />

    <template v-else>
      <!-- Section nav -->
      <nav class="flex flex-wrap gap-1.5">
        <button
          v-for="s in sections"
          :key="s.id"
          class="dh-btn-ghost"
          :class="section === s.id ? 'border-accent text-brandtext' : ''"
          @click="section = s.id"
        >
          {{ s.label }}
        </button>
      </nav>

      <StatusCard v-if="section === 'overview'" />
      <UsersCard v-else-if="section === 'users'" />
      <OrgsCard v-else-if="section === 'orgs'" />
      <ServerCard v-else-if="section === 'server'" />
      <PocketBaseCard v-else-if="section === 'pocketbase'" />
      <WebAppCard v-else-if="section === 'webapp'" />
      <PluginsCard v-else-if="section === 'plugins'" />

      <template v-else-if="section === 'api'">
        <EndpointTable :title="t('api.groupPublic')" :auth="t('api.authNone')" :endpoints="publicApi" />
        <EndpointTable :title="t('api.groupIdentity')" :auth="t('api.authBearer')" :endpoints="identityApi" />
        <EndpointTable :title="t('api.groupCars')" :auth="t('api.authBearer')" :endpoints="carsApi" />
        <EndpointTable :title="t('api.groupService')" :auth="t('api.authBearer')" :endpoints="serviceApi" />
        <EndpointTable :title="t('api.groupTechnical')" :auth="t('api.authBearer')" :endpoints="technicalApi" />
        <EndpointTable :title="t('api.groupParts')" :auth="t('api.authBearer')" :endpoints="partsApi" />
        <EndpointTable :title="t('api.groupFuel')" :auth="t('api.authBearer')" :endpoints="fuelApi" />
        <EndpointTable :title="t('api.groupCharging')" :auth="t('api.authBearer')" :endpoints="chargingApi" />
        <EndpointTable :title="t('api.groupMaintenance')" :auth="t('api.authBearer')" :endpoints="maintenanceApi" />
        <EndpointTable :title="t('api.groupDocuments')" :auth="t('api.authBearer')" :endpoints="documentsApi" />
        <EndpointTable :title="t('api.groupReminders')" :auth="t('api.authBearer')" :endpoints="remindersApi" />
        <EndpointTable
          :title="t('api.groupAttachments')"
          :auth="t('api.authBearer')"
          :endpoints="attachmentsApi"
          :note="attachmentsNote"
        />
        <EndpointTable :title="t('api.groupProviders')" :auth="t('api.authBearer')" :endpoints="providersApi" />
        <EndpointTable :title="t('api.groupIntegrations')" :auth="t('api.authBearer')" :endpoints="integrationsApi" />
        <EndpointTable :title="t('api.groupOcpp')" :auth="t('api.authCharger')" :endpoints="ocppApi" />
        <EndpointTable :title="t('api.groupAccount')" :auth="t('api.authBearer')" :endpoints="accountApi" />
        <EndpointTable :title="t('api.groupManagement')" :auth="t('api.authManager')" :endpoints="managementApi" />
        <EndpointTable :title="t('api.groupSuperadmin')" :auth="t('api.authSuperadmin')" :endpoints="superadminApi" />
      </template>

      <p v-if="!isManager && section === 'overview'" class="eyebrow text-center">
        {{ t("app.standardUserNote") }}
      </p>
    </template>

    <p class="eyebrow text-center">{{ t("app.footer") }}</p>
  </div>
</template>
