<script setup lang="ts">
import { computed, onMounted, reactive, ref, watch } from "vue";
import { storeToRefs } from "pinia";
import {
  Save,
  RotateCcw,
  RefreshCw,
  KeyRound,
  AlertTriangle,
  TerminalSquare,
  ShieldCheck,
  Sparkles,
  Upload,
  Download,
  Copy,
} from "lucide-vue-next";
import { api, type NetworkKeyStatus } from "@/lib/api";
import { useConfigStore } from "@/stores/config";
import { useNodeStore } from "@/stores/node";

const configStore = useConfigStore();
const node = useNodeStore();
const { config } = storeToRefs(configStore);
const { phase, status } = storeToRefs(node);

// Local working copy for the form. We mirror the canonical config and
// synchronise on every write to the store.
const draft = reactive({
  peers: "",
  tunName: "",
  noTun: false,
  networkName: "",
  tcpListenPort: "",
  quicListenPort: "",
});
const dirty = ref(false);

function loadDraft() {
  draft.peers = config.value.peers.join("\n");
  draft.tunName = config.value.tunName ?? "";
  draft.noTun = config.value.noTun;
  draft.networkName = config.value.networkName ?? "";
  draft.tcpListenPort = config.value.tcpListenPort?.toString() ?? "";
  draft.quicListenPort = config.value.quicListenPort?.toString() ?? "";
  dirty.value = false;
}

function parsePort(s: string): number | null {
  const t = s.trim();
  if (!t) return null;
  const n = Number(t);
  if (!Number.isInteger(n) || n < 1 || n > 65535) return null;
  return n;
}

watch(config, loadDraft, { immediate: true });

watch(
  draft,
  () => {
    dirty.value = true;
  },
  { deep: true },
);

async function save() {
  await configStore.save({
    peers: draft.peers
      .split(/\r?\n/)
      .map((s) => s.trim())
      .filter(Boolean),
    tunName: draft.tunName.trim() || null,
    noTun: draft.noTun,
    networkName: draft.networkName.trim() || null,
    tcpListenPort: parsePort(draft.tcpListenPort),
    quicListenPort: parsePort(draft.quicListenPort),
  });
  dirty.value = false;
}

async function reset() {
  if (!confirm("Restore default daemon configuration?")) return;
  await configStore.reset();
}

// ─── Identity ───────────────────────────────────────────────────────────────

const regenBusy = ref(false);
const regenError = ref<string | null>(null);

async function regenerate() {
  if (
    !confirm(
      "Regenerate identity? This deletes the current private key. Your overlay IPv6 and public key will change. Anyone with peers configured by your old IP/key will have to update them.",
    )
  ) {
    return;
  }
  regenBusy.value = true;
  regenError.value = null;
  try {
    await api.regenerateIdentity();
  } catch (e) {
    regenError.value = String(e);
  } finally {
    regenBusy.value = false;
  }
}

// ─── Logs ───────────────────────────────────────────────────────────────────

const logs = ref<string[]>([]);
const logsLoading = ref(false);

async function refreshLogs() {
  logsLoading.value = true;
  try {
    logs.value = await api.sidecarLogs();
  } finally {
    logsLoading.value = false;
  }
}

const isReady = computed(() => phase.value === "ready");

// ─── Private network key ────────────────────────────────────────────────────

const keyStatus = ref<NetworkKeyStatus | null>(null);
const keyBusy = ref(false);
const keyError = ref<string | null>(null);
const importHex = ref("");
const exportedHex = ref<string | null>(null);

async function refreshKeyStatus() {
  try {
    keyStatus.value = await api.networkKeyStatus();
  } catch (e) {
    keyError.value = String(e);
  }
}

async function generateKey() {
  const overwrite = keyStatus.value?.exists ?? false;
  if (
    overwrite &&
    !confirm(
      "Replace the existing network key? Every node currently using it will be cut off until they're given the new one.",
    )
  ) {
    return;
  }
  keyBusy.value = true;
  keyError.value = null;
  exportedHex.value = null;
  try {
    keyStatus.value = await api.networkKeyGenerate(overwrite);
  } catch (e) {
    keyError.value = String(e);
  } finally {
    keyBusy.value = false;
  }
}

async function importKey() {
  const hex = importHex.value.trim();
  if (!/^[0-9a-fA-F]{64}$/.test(hex)) {
    keyError.value = "Network key must be exactly 64 hex characters (32 bytes).";
    return;
  }
  const overwrite = keyStatus.value?.exists ?? false;
  if (
    overwrite &&
    !confirm("Replace the existing network key with the one you pasted?")
  ) {
    return;
  }
  keyBusy.value = true;
  keyError.value = null;
  try {
    keyStatus.value = await api.networkKeyImport(hex, overwrite);
    importHex.value = "";
  } catch (e) {
    keyError.value = String(e);
  } finally {
    keyBusy.value = false;
  }
}

async function exportKey() {
  keyError.value = null;
  try {
    exportedHex.value = await api.networkKeyExport();
  } catch (e) {
    keyError.value = String(e);
  }
}

async function copyExported() {
  if (!exportedHex.value) return;
  try {
    await navigator.clipboard.writeText(exportedHex.value);
  } catch {
    /* clipboard unavailable */
  }
}

async function deleteKey() {
  if (
    !confirm(
      "Delete the network key? You won't be able to start the daemon until you generate or import a new one.",
    )
  ) {
    return;
  }
  keyBusy.value = true;
  keyError.value = null;
  try {
    await api.networkKeyDelete();
    exportedHex.value = null;
    await refreshKeyStatus();
  } catch (e) {
    keyError.value = String(e);
  } finally {
    keyBusy.value = false;
  }
}

onMounted(async () => {
  await refreshKeyStatus();
  if (isReady.value) await refreshLogs();
});
</script>

<template>
  <div class="grid gap-4 lg:grid-cols-2">
    <!-- Private network ────────────────────────────────────────────────── -->
    <section class="rounded-lg border border-amber-500/40 bg-amber-500/5 lg:col-span-2">
      <header class="border-b border-amber-500/30 px-4 py-3">
        <h2 class="flex items-center gap-2 text-sm font-medium">
          <ShieldCheck class="h-4 w-4 text-amber-500" /> Private network
        </h2>
        <p class="mt-0.5 text-xs text-muted-foreground">
          A private overlay is identified by a <strong>name</strong> (public, agreed
          across nodes) and a 32-byte <strong>shared key</strong> (secret, distributed
          out-of-band). Both must match exactly across every node that
          should be on the same overlay.
        </p>
      </header>
      <div class="space-y-4 px-4 py-4">
        <div>
          <label class="text-xs uppercase tracking-wide text-muted-foreground">
            Network name (UTF-8, 2–64 bytes — public)
          </label>
          <input
            v-model="draft.networkName"
            type="text"
            spellcheck="false"
            placeholder="acme-corp-private"
            class="mt-1 w-full rounded-md border border-input bg-background px-3 py-1.5 font-mono text-sm focus:outline-none focus:ring-2 focus:ring-ring"
          />
        </div>

        <div>
          <div class="flex items-center justify-between">
            <label class="text-xs uppercase tracking-wide text-muted-foreground">
              Network key file
            </label>
            <span
              class="text-xs"
              :class="keyStatus?.exists ? 'text-emerald-500' : 'text-destructive'"
            >
              {{ keyStatus?.exists ? "configured" : "missing" }}
            </span>
          </div>
          <div class="mt-1 break-all rounded-md border border-border bg-muted/40 p-2 font-mono text-[11px]">
            {{ keyStatus?.path ?? "(unknown)" }}
          </div>
        </div>

        <div class="flex flex-wrap gap-2">
          <button
            class="inline-flex items-center gap-1.5 rounded-md bg-amber-500 px-3 py-1.5 text-xs font-medium text-amber-950 hover:opacity-90 disabled:opacity-50"
            :disabled="keyBusy"
            @click="generateKey"
          >
            <Sparkles class="h-3 w-3" />
            {{ keyStatus?.exists ? "Re-generate" : "Generate 32-byte key" }}
          </button>
          <button
            class="inline-flex items-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-xs hover:bg-secondary disabled:opacity-50"
            :disabled="keyBusy || !keyStatus?.exists"
            @click="exportKey"
          >
            <Download class="h-3 w-3" />
            Reveal hex
          </button>
          <button
            class="inline-flex items-center gap-1.5 rounded-md border border-destructive px-3 py-1.5 text-xs text-destructive hover:bg-destructive hover:text-destructive-foreground disabled:opacity-50"
            :disabled="keyBusy || !keyStatus?.exists"
            @click="deleteKey"
          >
            <RotateCcw class="h-3 w-3" />
            Delete
          </button>
        </div>

        <div v-if="exportedHex" class="space-y-2">
          <label class="text-xs uppercase tracking-wide text-muted-foreground">
            Hex export — share over a secure channel only
          </label>
          <div class="flex gap-2">
            <input
              :value="exportedHex"
              readonly
              class="flex-1 rounded-md border border-input bg-background px-3 py-1.5 font-mono text-xs focus:outline-none"
            />
            <button
              class="inline-flex items-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-xs hover:bg-secondary"
              @click="copyExported"
            >
              <Copy class="h-3 w-3" />
              Copy
            </button>
          </div>
        </div>

        <div class="space-y-2">
          <label class="text-xs uppercase tracking-wide text-muted-foreground">
            Import a key from another node (64 hex characters)
          </label>
          <div class="flex gap-2">
            <input
              v-model="importHex"
              type="text"
              spellcheck="false"
              placeholder="0a1b2c3d…"
              class="flex-1 rounded-md border border-input bg-background px-3 py-1.5 font-mono text-xs focus:outline-none focus:ring-2 focus:ring-ring"
            />
            <button
              class="inline-flex items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:opacity-90 disabled:opacity-50"
              :disabled="keyBusy || !importHex.trim()"
              @click="importKey"
            >
              <Upload class="h-3 w-3" />
              Import
            </button>
          </div>
        </div>

        <p v-if="keyError" class="text-xs text-destructive">{{ keyError }}</p>
      </div>
    </section>

    <!-- Daemon configuration ───────────────────────────────────────────── -->
    <section class="rounded-lg border border-border bg-card">
      <header class="border-b border-border px-4 py-3">
        <h2 class="text-sm font-medium">Daemon configuration</h2>
        <p class="mt-0.5 text-xs text-muted-foreground">
          Applied next time the daemon starts. Currently running daemon
          isn't reconfigured live.
        </p>
      </header>
      <div class="space-y-4 px-4 py-4">
        <div>
          <label class="text-xs uppercase tracking-wide text-muted-foreground">
            Bootstrap peers (one per line)
          </label>
          <textarea
            v-model="draft.peers"
            rows="6"
            spellcheck="false"
            class="mt-1 w-full resize-y rounded-md border border-input bg-background px-3 py-2 font-mono text-xs focus:outline-none focus:ring-2 focus:ring-ring"
            placeholder="tcp://your-node.example.org:9651"
          />
          <p class="mt-1 text-[11px] text-muted-foreground">
            Private overlays don't have a Threefold-operated seed — point at
            your own VPS or other trusted nodes that already share the network
            name and key.
          </p>
        </div>
        <div class="flex items-center gap-3">
          <input
            id="no-tun"
            v-model="draft.noTun"
            type="checkbox"
            class="h-4 w-4"
          />
          <label for="no-tun" class="text-sm">
            Disable TUN interface (<code class="font-mono text-xs">--no-tun</code>)
          </label>
        </div>
        <div>
          <label class="text-xs uppercase tracking-wide text-muted-foreground">
            TUN interface name (optional)
          </label>
          <input
            v-model="draft.tunName"
            type="text"
            class="mt-1 w-full rounded-md border border-input bg-background px-3 py-1.5 font-mono text-sm focus:outline-none focus:ring-2 focus:ring-ring"
            placeholder="mycelium0"
            :disabled="draft.noTun"
          />
        </div>

        <div class="grid gap-3 sm:grid-cols-2">
          <div>
            <label class="text-xs uppercase tracking-wide text-muted-foreground">
              TCP listen port
            </label>
            <input
              v-model="draft.tcpListenPort"
              type="text"
              inputmode="numeric"
              class="mt-1 w-full rounded-md border border-input bg-background px-3 py-1.5 font-mono text-sm focus:outline-none focus:ring-2 focus:ring-ring"
              placeholder="ephemeral"
            />
          </div>
          <div>
            <label class="text-xs uppercase tracking-wide text-muted-foreground">
              QUIC listen port
            </label>
            <input
              v-model="draft.quicListenPort"
              type="text"
              inputmode="numeric"
              class="mt-1 w-full rounded-md border border-input bg-background px-3 py-1.5 font-mono text-sm focus:outline-none focus:ring-2 focus:ring-ring"
              placeholder="ephemeral"
            />
          </div>
        </div>
        <p class="text-[11px] text-muted-foreground">
          Pin these to a fixed value (e.g. <code class="font-mono">9651</code> for
          TCP, <code class="font-mono">9652</code> for QUIC) when you port-forward
          a stable port on your router so other private-network nodes can dial in
          across daemon restarts. Leave empty to keep using a different ephemeral
          port at every start.
        </p>
        <div class="flex justify-end gap-2 pt-2">
          <button
            class="inline-flex items-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-xs hover:bg-secondary"
            @click="reset"
          >
            <RotateCcw class="h-3 w-3" />
            Defaults
          </button>
          <button
            class="inline-flex items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:opacity-90 disabled:opacity-50"
            :disabled="!dirty"
            @click="save"
          >
            <Save class="h-3 w-3" />
            Save
          </button>
        </div>
      </div>
    </section>

    <!-- Identity ───────────────────────────────────────────────────────── -->
    <section class="rounded-lg border border-border bg-card">
      <header class="border-b border-border px-4 py-3">
        <h2 class="flex items-center gap-2 text-sm font-medium">
          <KeyRound class="h-4 w-4" /> Identity
        </h2>
      </header>
      <div class="space-y-3 px-4 py-4 text-sm">
        <div>
          <div class="text-xs uppercase tracking-wide text-muted-foreground">
            Key file
          </div>
          <div class="mt-1 break-all font-mono text-xs">
            {{ status?.keyPath ?? "(unknown — start the daemon at least once)" }}
          </div>
        </div>
        <div class="rounded-md border border-destructive/30 bg-destructive/5 p-3 text-xs">
          <div class="flex items-start gap-2">
            <AlertTriangle class="mt-0.5 h-4 w-4 shrink-0 text-destructive" />
            <div class="space-y-2">
              <p>
                Regenerating discards your current private key, which means a
                new overlay IPv6 and public key. This is irreversible.
              </p>
              <button
                class="inline-flex items-center gap-1.5 rounded-md border border-destructive px-3 py-1 text-destructive hover:bg-destructive hover:text-destructive-foreground disabled:opacity-50"
                :disabled="regenBusy"
                @click="regenerate"
              >
                <RotateCcw class="h-3 w-3" />
                Regenerate identity
              </button>
              <p v-if="regenError" class="text-destructive">{{ regenError }}</p>
            </div>
          </div>
        </div>
      </div>
    </section>

    <!-- Logs ──────────────────────────────────────────────────────────── -->
    <section class="rounded-lg border border-border bg-card lg:col-span-2">
      <header class="flex items-center justify-between border-b border-border px-4 py-3">
        <h2 class="flex items-center gap-2 text-sm font-medium">
          <TerminalSquare class="h-4 w-4" /> Sidecar logs
        </h2>
        <button
          class="inline-flex items-center gap-1.5 rounded-md border border-border px-3 py-1 text-xs hover:bg-secondary disabled:opacity-50"
          :disabled="logsLoading"
          @click="refreshLogs"
        >
          <RefreshCw
            class="h-3 w-3"
            :class="logsLoading ? 'animate-spin' : ''"
          />
          Refresh
        </button>
      </header>
      <div
        class="max-h-72 overflow-y-auto bg-background px-4 py-2 font-mono text-[11px]"
      >
        <p v-if="!logs.length" class="py-4 text-center text-muted-foreground">
          No log entries — start the daemon to begin capturing.
        </p>
        <pre v-else class="whitespace-pre-wrap break-all">{{ logs.join("\n") }}</pre>
      </div>
    </section>

    <!-- About ────────────────────────────────────────────────────────── -->
    <section class="rounded-lg border border-border bg-card lg:col-span-2">
      <header class="border-b border-border px-4 py-3">
        <h2 class="text-sm font-medium">About</h2>
      </header>
      <dl class="grid grid-cols-1 gap-4 px-4 py-4 text-sm sm:grid-cols-3">
        <div>
          <dt class="text-xs uppercase tracking-wide text-muted-foreground">
            App
          </dt>
          <dd class="mt-1 font-mono text-xs">mycellium-ui 0.1.0</dd>
        </div>
        <div>
          <dt class="text-xs uppercase tracking-wide text-muted-foreground">
            Mycelium daemon
          </dt>
          <dd class="mt-1 font-mono text-xs">v0.6.1 (bundled)</dd>
        </div>
        <div>
          <dt class="text-xs uppercase tracking-wide text-muted-foreground">
            API endpoint
          </dt>
          <dd class="mt-1 font-mono text-xs">
            {{ status?.apiUrl ?? "—" }}
          </dd>
        </div>
      </dl>
    </section>
  </div>
</template>
