<script setup lang="ts">
import {
  AlertTriangle,
  Check,
  CircleAlert,
  CircleCheck,
  Eye,
  EyeOff,
  Link2,
  Loader2,
  Radar,
  RefreshCw,
  ServerCog,
  ShieldCheck,
  Trash2,
} from '@lucide/vue'
import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { useAuthStore } from '../../stores/auth'
import { useOpenClawSetupStore } from '../../stores/openclawSetup'
import type { OpenClawDiscoveryCandidate } from '../../types/openclaw-setup'
import OpenClawSetupWizard from './OpenClawSetupWizard.vue'

const auth = useAuthStore()
const setup = useOpenClawSetupStore()
const endpoint = ref('')
const source = ref('manual')
const tlsFingerprint = ref('')
const bootstrapToken = ref('')
const bootstrapSecretReference = ref('')
const showBootstrapToken = ref(false)
const includeMdns = ref(false)
const confirmRemoval = ref(false)
const endpointInput = ref<HTMLInputElement | null>(null)

const isOwner = computed(() => auth.user?.role === 'owner')
const status = computed(() => setup.status)
const canAttach = computed(() =>
  isOwner.value
  && Boolean(setup.probeResult?.canAttach)
  && !setup.busy,
)
const wizardMethods = [
  'wizard.start',
  'wizard.next',
  'wizard.status',
  'wizard.cancel',
]
const wizardAvailable = computed(() =>
  isOwner.value
  && Boolean(status.value?.managementEnabled)
  && Boolean(status.value?.grantedScopes.includes('operator.admin'))
  && wizardMethods.every(method => status.value?.advertisedMethods.includes(method)),
)
const wizardUnavailableReason = computed(() => {
  if (!isOwner.value) return 'Nur der Nexus-Owner darf den offiziellen OpenClaw-Wizard bedienen.'
  if (!status.value?.hasProfile) {
    return 'Zuerst eine erreichbare OpenClaw-Instanz verbinden und deren Gateway-Vertrag prüfen.'
  }
  if (!status.value.managementEnabled) {
    return 'Zuerst Read-only-Inventar übernehmen und OpenClaw-Verwaltung ausdrücklich freigeben.'
  }
  if (!status.value.grantedScopes.includes('operator.admin')) {
    return 'Der Wizard benötigt ein ausdrücklich freigegebenes operator.admin Scope-Upgrade.'
  }
  const missing = wizardMethods.filter(method => !status.value?.advertisedMethods.includes(method))
  if (missing.length) return `OpenClaw bietet noch nicht alle Wizard-Methoden an: ${missing.join(', ')}.`
  return 'OpenClaw Wizard ist derzeit nicht verfügbar.'
})
const steps = computed(() => {
  const adoption = status.value?.adoptionState ?? 'none'
  const hasProfile = Boolean(status.value?.hasProfile)
  const verified = ['verified', 'adopted'].includes(adoption)
  const adopted = adoption === 'adopted'
  return [
    { label: 'Erkennen', complete: Boolean(setup.probeResult || hasProfile) },
    { label: 'Read-only verbinden', complete: hasProfile },
    { label: 'Inventar prüfen', complete: verified },
    { label: 'Übernehmen', complete: adopted },
    { label: 'Verwalten', complete: Boolean(status.value?.managementEnabled) },
  ]
})

function chooseCandidate(candidate: OpenClawDiscoveryCandidate) {
  endpoint.value = candidate.endpoint
  source.value = candidate.source
  tlsFingerprint.value = ''
  setup.probeResult = null
  void nextTick(() => endpointInput.value?.focus())
}

async function discover() {
  await setup.discover(includeMdns.value).catch(() => undefined)
}

async function probe() {
  if (!endpoint.value.trim()) return
  await setup.probe(
    endpoint.value.trim(),
    tlsFingerprint.value.trim() || null,
  ).catch(() => undefined)
}

async function attach() {
  if (!setup.probeResult) return
  await setup.attach({
    endpoint: setup.probeResult.endpoint,
    discoverySource: source.value,
    tlsCertificateFingerprint: tlsFingerprint.value.trim() || null,
    bootstrapToken: bootstrapToken.value || null,
    bootstrapSecretReference: bootstrapSecretReference.value.trim() || null,
  }).catch(() => undefined)
  bootstrapToken.value = ''
}

async function verify() {
  await setup.verify().catch(() => undefined)
}

async function adopt() {
  await setup.adopt().catch(() => undefined)
}

async function toggleManagement() {
  await setup.setManagement(!status.value?.managementEnabled).catch(() => undefined)
}

async function removeConnection() {
  if (!confirmRemoval.value || !status.value?.endpoint) return
  const removed = await setup.remove(
    status.value.endpoint,
    status.value.deviceId,
  ).then(() => true).catch(() => false)
  if (removed) {
    confirmRemoval.value = false
    endpoint.value = ''
    source.value = 'manual'
  }
}

watch(
  () => setup.status?.endpoint,
  value => {
    if (value && !endpoint.value) endpoint.value = value
  },
)

onMounted(async () => {
  await setup.load()
  if (setup.status?.endpoint) endpoint.value = setup.status.endpoint
})
</script>

<template>
  <section class="setup-center nexus-panel" aria-labelledby="openclaw-setup-title">
    <header class="setup-header">
      <div class="setup-title">
        <span class="setup-icon"><ServerCog :size="19" aria-hidden="true" /></span>
        <div>
          <span class="eyebrow">OPENCLAW CONTROL PLANE</span>
          <h2 id="openclaw-setup-title">Attach &amp; Adopt</h2>
          <p>
            OpenClaw bleibt die Runtime-Autorität. Nexus übernimmt nur Verbindung,
            Inventar, Richtlinien und Audit.
          </p>
        </div>
      </div>
      <button
        type="button"
        class="nexus-button"
        :disabled="setup.busy"
        @click="setup.load()"
      >
        <Loader2 v-if="setup.loading" :size="14" class="spin" aria-hidden="true" />
        <RefreshCw v-else :size="14" aria-hidden="true" />
        Status prüfen
      </button>
    </header>

    <ol class="setup-steps" aria-label="OpenClaw Einrichtungsfortschritt">
      <li v-for="(step, index) in steps" :key="step.label" :class="{ complete: step.complete }">
        <span><Check v-if="step.complete" :size="12" aria-hidden="true" />{{ step.complete ? '' : index + 1 }}</span>
        {{ step.label }}
      </li>
    </ol>

    <div
      v-if="status?.experimentalBlocked"
      class="setup-alert setup-alert--warning"
      role="status"
    >
      <AlertTriangle :size="18" aria-hidden="true" />
      <div>
        <strong>Externe Nexus-Client-ID noch nicht freigegeben</strong>
        <p>
          Nexus imitiert weder OpenClaw CLI noch Control UI. Discovery und Diagnose
          bleiben nutzbar; Attach ist blockiert, bis der gepinnte Gateway-Vertrag
          die Client-ID <code>nexus</code> offiziell akzeptiert.
        </p>
      </div>
    </div>

    <div v-if="setup.loading && !status" class="nexus-state nexus-state--loading" role="status">
      <Loader2 :size="18" class="spin" aria-hidden="true" />
      <p>OpenClaw-Setupstatus wird geladen…</p>
    </div>

    <template v-else>
      <section class="connection-summary" aria-labelledby="connection-summary-title">
        <div class="section-heading">
          <div>
            <span class="eyebrow">CURRENT AUTHORITY</span>
            <h3 id="connection-summary-title">Verbindungszustand</h3>
          </div>
          <span
            class="nexus-status"
            :class="status?.hasProfile ? 'nexus-status--success' : 'nexus-status--warning'"
          >
            <CircleCheck v-if="status?.hasProfile" :size="13" aria-hidden="true" />
            <CircleAlert v-else :size="13" aria-hidden="true" />
            {{ status?.adoptionState || 'Nicht konfiguriert' }}
          </span>
        </div>
        <dl class="setup-facts">
          <div><dt>Endpoint</dt><dd><code>{{ status?.endpoint || 'Noch nicht gewählt' }}</code></dd></div>
          <div><dt>Version</dt><dd>{{ status?.gatewayVersion || 'Nicht geprüft' }}</dd></div>
          <div><dt>Protokoll</dt><dd>{{ status?.protocolVersion ? `v${status.protocolVersion}` : 'Nicht geprüft' }}</dd></div>
          <div><dt>Device</dt><dd><code>{{ status?.deviceId || 'Nicht gepaart' }}</code></dd></div>
          <div><dt>Scopes</dt><dd>{{ status?.grantedScopes.length ?? 0 }} gewährt</dd></div>
          <div><dt>Verwaltung</dt><dd>{{ status?.managementEnabled ? 'Freigegeben' : 'Read-only' }}</dd></div>
        </dl>

        <div v-if="status?.pairingRequired" class="pairing-proof" role="status">
          <ShieldCheck :size="17" aria-hidden="true" />
          <div>
            <strong>Externe Pairing-Freigabe erforderlich</strong>
            <p>Prüfe und genehmige exakt diese aktuelle Request-ID in Baos OpenClaw:</p>
            <code>{{ status.pairingRequestId || 'Keine Request-ID gemeldet' }}</code>
          </div>
        </div>
      </section>

      <OpenClawSetupWizard
        :available="wizardAvailable"
        :unavailable-reason="wizardUnavailableReason"
      />

      <section class="discovery-section" aria-labelledby="discovery-title">
        <div class="section-heading">
          <div>
            <span class="eyebrow">READ-ONLY PREFLIGHT</span>
            <h3 id="discovery-title">Gateway erkennen und prüfen</h3>
          </div>
          <button
            type="button"
            class="nexus-button"
            :disabled="!isOwner || setup.busy"
            @click="discover"
          >
            <Loader2 v-if="setup.action === 'discover'" :size="14" class="spin" aria-hidden="true" />
            <Radar v-else :size="14" aria-hidden="true" />
            Kandidaten erkennen
          </button>
        </div>

        <label class="mdns-toggle">
          <input v-model="includeMdns" type="checkbox" />
          <span>Optional eine ausdrücklich gestartete mDNS-Suche einbeziehen</span>
        </label>

        <div v-if="setup.discovery?.candidates.length" class="candidate-list">
          <button
            v-for="candidate in setup.discovery.candidates"
            :key="`${candidate.source}-${candidate.endpoint}`"
            type="button"
            class="candidate"
            :disabled="!candidate.isValid"
            @click="chooseCandidate(candidate)"
          >
            <span>
              <strong>{{ candidate.source }}</strong>
              <code>{{ candidate.endpoint }}</code>
            </span>
            <span>{{ candidate.requiresTlsFingerprint ? 'TLS-Pin erforderlich' : 'Interner Kandidat' }}</span>
          </button>
        </div>

        <div class="probe-form">
          <label>
            <span>Gateway Endpoint</span>
            <input
              ref="endpointInput"
              v-model="endpoint"
              type="url"
              inputmode="url"
              autocomplete="url"
              placeholder="ws://openclaw-gateway:18789"
            />
          </label>
          <label>
            <span>TLS SHA-256 Fingerprint</span>
            <input
              v-model="tlsFingerprint"
              type="text"
              autocomplete="off"
              placeholder="Nur für externe wss:// Endpoints"
            />
          </label>
          <button
            type="button"
            class="nexus-button nexus-button--primary"
            :disabled="!isOwner || !endpoint.trim() || setup.busy"
            @click="probe"
          >
            <Loader2 v-if="setup.action === 'probe'" :size="14" class="spin" aria-hidden="true" />
            <Link2 v-else :size="14" aria-hidden="true" />
            Read-only prüfen
          </button>
        </div>

        <div v-if="setup.probeResult" class="probe-proof" aria-live="polite">
          <div>
            <span>Endpoint</span>
            <code>{{ setup.probeResult.endpoint }}</code>
          </div>
          <div>
            <span>Gateway</span>
            <strong>{{ setup.probeResult.gatewayVersion || 'Nicht verbunden' }}</strong>
          </div>
          <div>
            <span>Protocol</span>
            <strong>{{ setup.probeResult.protocolVersion ? `v${setup.probeResult.protocolVersion}` : 'Nicht gemeldet' }}</strong>
          </div>
          <div>
            <span>Least privilege</span>
            <strong>{{ setup.probeResult.leastPrivilegeSatisfied ? 'Read-only' : 'Nicht bestätigt' }}</strong>
          </div>
        </div>
      </section>

      <section v-if="setup.probeResult || status?.hasProfile" class="adoption-section" aria-labelledby="adoption-title">
        <div class="section-heading">
          <div>
            <span class="eyebrow">TRUST TRANSITION</span>
            <h3 id="adoption-title">Verbinden und übernehmen</h3>
          </div>
        </div>

        <div v-if="!status?.hasProfile" class="bootstrap-grid">
          <label>
            <span>Einmaliges Bootstrap-Token</span>
            <span class="secret-input">
              <input
                v-model="bootstrapToken"
                :type="showBootstrapToken ? 'text' : 'password'"
                autocomplete="off"
                placeholder="Wird nicht gespeichert"
              />
              <button
                type="button"
                :aria-label="showBootstrapToken ? 'Bootstrap-Token verbergen' : 'Bootstrap-Token anzeigen'"
                @click="showBootstrapToken = !showBootstrapToken"
              >
                <EyeOff v-if="showBootstrapToken" :size="15" aria-hidden="true" />
                <Eye v-else :size="15" aria-hidden="true" />
              </button>
            </span>
          </label>
          <label>
            <span>oder Server-SecretRef</span>
            <input
              v-model="bootstrapSecretReference"
              type="text"
              autocomplete="off"
              placeholder="env:OPENCLAW_GATEWAY_TOKEN"
            />
          </label>
          <button
            type="button"
            class="nexus-button nexus-button--primary"
            :disabled="!canAttach"
            @click="attach"
          >
            <Loader2 v-if="setup.action === 'attach'" :size="14" class="spin" aria-hidden="true" />
            <ShieldCheck v-else :size="14" aria-hidden="true" />
            Read-only verbinden
          </button>
        </div>

        <div v-else class="adoption-actions">
          <button type="button" class="nexus-button" :disabled="setup.busy" @click="verify">
            <Loader2 v-if="setup.action === 'verify'" :size="14" class="spin" aria-hidden="true" />
            <RefreshCw v-else :size="14" aria-hidden="true" />
            Inventar verifizieren
          </button>
          <button
            type="button"
            class="nexus-button nexus-button--primary"
            :disabled="setup.busy || status?.adoptionState === 'adopted'"
            @click="adopt"
          >
            <Check :size="14" aria-hidden="true" />
            Read-only übernehmen
          </button>
          <button
            type="button"
            class="nexus-button"
            :disabled="setup.busy || status?.adoptionState !== 'adopted'"
            @click="toggleManagement"
          >
            <ShieldCheck :size="14" aria-hidden="true" />
            {{ status?.managementEnabled ? 'Verwaltung sperren' : 'Verwaltung anfordern' }}
          </button>
        </div>

        <div v-if="setup.inventory" class="inventory-grid" aria-label="OpenClaw Inventar">
          <div><span>Agenten</span><strong>{{ setup.inventory.agentCount ?? '—' }}</strong></div>
          <div><span>Agent-Dateien</span><strong>{{ setup.inventory.agentFileCount ?? '—' }}</strong></div>
          <div><span>Cronjobs</span><strong>{{ setup.inventory.cronJobCount ?? '—' }}</strong></div>
          <div><span>Modelle</span><strong>{{ setup.inventory.modelCount ?? '—' }}</strong></div>
          <div><span>Channels</span><strong>{{ setup.inventory.channelCount ?? '—' }}</strong></div>
          <div><span>Nodes</span><strong>{{ setup.inventory.nodeCount ?? '—' }}</strong></div>
        </div>
      </section>

      <section v-if="status?.hasProfile" class="danger-zone" aria-labelledby="disconnect-title">
        <div>
          <span class="eyebrow">LOCAL RECOVERY</span>
          <h3 id="disconnect-title">Nexus-Verbindung entfernen</h3>
          <p>OpenClaw selbst, seine Agenten und Cronjobs werden dabei nicht gelöscht.</p>
        </div>
        <label>
          <input v-model="confirmRemoval" type="checkbox" />
          <span>Endpoint und lokale Profilbindung wirklich entfernen</span>
        </label>
        <button
          type="button"
          class="nexus-button setup-danger-button"
          :disabled="!confirmRemoval || setup.busy"
          @click="removeConnection"
        >
          <Trash2 :size="14" aria-hidden="true" />
          Verbindung entfernen
        </button>
      </section>
    </template>

    <div v-if="setup.error" class="setup-alert setup-alert--error" role="alert">
      <CircleAlert :size="18" aria-hidden="true" />
      <div><strong>OpenClaw Setup fehlgeschlagen</strong><p>{{ setup.error }}</p></div>
    </div>
    <p class="nexus-visually-hidden" aria-live="polite">{{ setup.announcement }}</p>
  </section>
</template>

<style scoped>
.setup-center { display: grid; gap: 16px; padding: 18px; }
.setup-header, .setup-title, .section-heading, .adoption-actions, .danger-zone {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
}
.setup-title { align-items: flex-start; justify-content: flex-start; }
.setup-title h2, .section-heading h3, .danger-zone h3 { margin: 3px 0 0; font-family: var(--font-display); }
.setup-title h2 { font-size: 20px; }
.setup-title p, .danger-zone p { margin: 5px 0 0; color: var(--tx-3); line-height: 1.55; }
.setup-icon { width: 40px; height: 40px; display: grid; place-items: center; flex: 0 0 auto; border: 1px solid var(--line-2); border-radius: var(--r-sm); background: var(--grad-soft); color: var(--a-mid); }
.setup-steps { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 6px; margin: 0; padding: 0; list-style: none; }
.setup-steps li { display: flex; align-items: center; gap: 7px; min-height: 38px; padding: 7px 9px; border: 1px solid var(--line); border-radius: var(--r-sm); color: var(--tx-3); font-size: 11px; }
.setup-steps li span { width: 21px; height: 21px; display: grid; place-items: center; flex: 0 0 auto; border: 1px solid var(--line-2); border-radius: 50%; font-family: var(--font-mono-v2); }
.setup-steps li.complete { border-color: color-mix(in srgb, var(--st-work) 28%, var(--line)); color: var(--tx-2); }
.setup-steps li.complete span { background: var(--status-work-bg); color: var(--st-work); }
.connection-summary, .discovery-section, .adoption-section, .danger-zone { display: grid; gap: 13px; padding: 15px; border: 1px solid var(--line); border-radius: var(--r); background: var(--accent-wash); }
.section-heading h3, .danger-zone h3 { font-size: 15px; }
.setup-facts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin: 0; }
.setup-facts div, .probe-proof div, .inventory-grid div { min-width: 0; padding: 10px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--glass); }
.setup-facts dt, .probe-proof span, .inventory-grid span { color: var(--tx-3); font-family: var(--font-mono-v2); font-size: 10px; text-transform: uppercase; }
.setup-facts dd { margin: 4px 0 0; overflow-wrap: anywhere; color: var(--tx-2); }
.setup-facts code, .probe-proof code { font-family: var(--font-mono-v2); font-size: 11px; overflow-wrap: anywhere; }
.setup-alert, .pairing-proof { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 10px; padding: 12px; border: 1px solid var(--line); border-radius: var(--r-sm); }
.setup-alert p, .pairing-proof p { margin: 3px 0 0; color: var(--tx-2); line-height: 1.5; }
.setup-alert--warning, .pairing-proof { border-color: var(--status-queue-line); background: var(--status-queue-bg); color: var(--st-queue); }
.setup-alert--error { border-color: var(--status-block-line); background: var(--status-block-bg); color: var(--st-block); }
.pairing-proof code { display: inline-block; margin-top: 6px; color: var(--tx); overflow-wrap: anywhere; }
.mdns-toggle, .danger-zone label { display: flex; align-items: center; gap: 9px; color: var(--tx-2); }
.candidate-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.candidate { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 52px; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--glass); color: var(--tx-2); text-align: left; cursor: pointer; }
.candidate:hover:not(:disabled) { border-color: var(--line-3); }
.candidate span:first-child { display: grid; gap: 3px; min-width: 0; }
.candidate code { overflow: hidden; color: var(--tx-3); font-family: var(--font-mono-v2); font-size: 10px; text-overflow: ellipsis; white-space: nowrap; }
.candidate > span:last-child { color: var(--tx-3); font-size: 10px; }
.probe-form, .bootstrap-grid { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) auto; gap: 10px; align-items: end; }
.probe-form label, .bootstrap-grid label { display: grid; gap: 6px; min-width: 0; color: var(--tx-3); font-size: 11px; }
.probe-form input, .bootstrap-grid input { width: 100%; min-width: 0; }
.probe-proof { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
.probe-proof div { display: grid; gap: 4px; }
.secret-input { display: flex; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--field-surface); }
.secret-input:focus-within { border-color: var(--a-blue); box-shadow: var(--focus-ring); }
.secret-input input { border: 0; background: transparent; box-shadow: none; }
.secret-input button { width: 38px; border: 0; background: transparent; color: var(--tx-3); cursor: pointer; }
.inventory-grid { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 8px; }
.inventory-grid div { display: grid; gap: 4px; }
.inventory-grid strong { font-family: var(--font-display); font-size: 18px; }
.danger-zone { grid-template-columns: minmax(0, 1fr) auto auto; border-color: color-mix(in srgb, var(--st-block) 24%, var(--line)); }
.setup-danger-button { color: var(--st-block); }
.spin { animation: spin 1s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

@media (max-width: 1024px) {
  .setup-steps { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .setup-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .probe-form, .bootstrap-grid { grid-template-columns: 1fr 1fr; }
  .probe-form .nexus-button, .bootstrap-grid .nexus-button { justify-self: start; }
  .inventory-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .danger-zone { grid-template-columns: 1fr; align-items: start; }
  .danger-zone .nexus-button { justify-self: start; }
}

@media (max-width: 680px) {
  .setup-header, .section-heading, .adoption-actions { align-items: stretch; flex-direction: column; }
  .setup-header .nexus-button, .section-heading .nexus-button, .adoption-actions .nexus-button { align-self: flex-start; }
  .setup-steps, .candidate-list, .setup-facts, .probe-proof, .probe-form, .bootstrap-grid { grid-template-columns: 1fr; }
  .inventory-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .candidate { align-items: flex-start; flex-direction: column; }
  .candidate code { white-space: normal; overflow-wrap: anywhere; }
}
</style>
