<template>
  <section id="platform" class="platform-section" aria-labelledby="platform-title">
    <div class="section-kicker mono">Live technical platform</div>
    <div class="section-heading">
      <div>
        <h2 id="platform-title">Titan Lab: Live Platform</h2>
        <p>
          Titan Lab is the live infrastructure behind bstein.dev. It demonstrates the same disciplines I apply
          professionally: automation, testing, GitOps delivery, identity, observability, service operations, and
          infrastructure troubleshooting.
        </p>
      </div>
      <div class="section-actions">
        <a class="section-link mono" href="https://metrics.bstein.dev" target="_blank" rel="noopener noreferrer">
          Public metrics
        </a>
        <a class="section-link mono" href="https://scm.bstein.dev/bstein/titan-iac" target="_blank" rel="noopener noreferrer">
          IaC source
        </a>
      </div>
    </div>

    <div class="platform-dashboard glass">
      <div class="dashboard-copy">
        <p class="eyebrow">Titan Lab</p>
        <h3>Atlas services and production-minded operations</h3>
        <p class="lede">
          Atlas runs the member platform, service catalog, identity, delivery automation, observability, source control,
          container delivery, storage, and onboarding workflows that support bstein.dev.
        </p>
        <div class="bullets">
          <div :class="['pill', 'mono', atlasPillClass]">Atlas: flux-managed k3s</div>
          <div class="pill mono">Public metrics</div>
          <div class="pill mono">Member platform</div>
        </div>
        <div class="live-status" aria-live="polite">
          <div v-if="error" class="status">{{ error }}</div>
          <div v-else class="status mono">
            {{ loading ? "Loading..." : labStatus?.connected ? "Live data connected" : "Live data unavailable" }}
          </div>
          <p v-if="error || (!loading && !labStatus?.connected)" class="fallback-copy">
            Live status is temporarily unavailable. The platform overview remains available below.
          </p>
        </div>
      </div>

      <div class="availability dashboard-panel">
        <div class="availability-grid">
          <div class="availability-panel">
            <div class="panel-title availability-title">
              <div>
                <h4>Atlas availability</h4>
                <p class="panel-subtitle mono">k3s cluster</p>
              </div>
            </div>
            <iframe
              title="Atlas public availability panel"
              src="https://metrics.bstein.dev/d-solo/atlas-overview/atlas-overview?from=now-24h&to=now&refresh=1m&theme=dark&panelId=27&__feature.dashboardSceneSolo"
              width="100%"
              height="180"
              frameborder="0"
              loading="lazy"
            ></iframe>
          </div>
          <div class="availability-panel">
            <div class="panel-title availability-title">
              <div>
                <h4>Dedicated host availability</h4>
                <p :class="['panel-subtitle', 'mono', dedicatedHostsPillClass]">{{ dedicatedHostsLabel }}</p>
              </div>
            </div>
            <div class="host-status-list">
              <div v-for="host in dedicatedHostItems" :key="host.label" class="host-status-row">
                <span class="host-dot" :class="host.state" aria-hidden="true"></span>
                <div>
                  <div class="host-label">{{ host.label }}</div>
                  <div class="host-value mono">{{ host.value }}</div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <MetricRow class="dashboard-metrics" :items="metricItems" />
    </div>
  </section>

  <section class="platform-subsection platform-services-section" aria-labelledby="platform-services-title">
    <div class="section-heading platform-subheading">
      <div>
        <p class="section-kicker mono">Live service surface</p>
        <h2 id="platform-services-title">Service Overview</h2>
        <p>
          A compact surface of public and member-facing services that shows how Titan Lab is delivered, routed,
          observed, and maintained as a running platform.
        </p>
      </div>
    </div>
    <div class="platform-card card">
      <div class="section-head">
        <h3>Selected Services</h3>
        <span class="pill mono">identity + ci/cd + registry + observability + product services</span>
      </div>
      <ServiceGrid :services="featuredServices" />
    </div>
  </section>

  <section class="platform-subsection platform-architecture-section" aria-labelledby="platform-architecture-title">
    <div class="section-heading platform-subheading">
      <div>
        <p class="section-kicker mono">Architecture</p>
        <h2 id="platform-architecture-title">How It All Fits Together</h2>
        <p>
          A high-level operating map for public edge traffic, Kubernetes services, delivery flow, and non-cluster host
          responsibilities. Internal addresses, credentials, and member-only details stay private.
        </p>
      </div>
    </div>
    <div class="architecture-board card" aria-label="Titan Lab operating map">
      <div class="board-head">
        <div>
          <h3>Atlas operating map</h3>
          <p>Public edge, Kubernetes services, delivery machinery, and the two dedicated hosts that sit outside the cluster.</p>
        </div>
        <span class="pill mono">public abstraction</span>
      </div>

      <div class="architecture-map">
        <div class="map-column edge-column">
          <div class="map-node edge-node">
            <span class="map-label mono">public edge</span>
            <strong>DNS / TLS / Traefik</strong>
            <p>Routes public pages, safe dashboards, and auth-gated member services.</p>
          </div>
          <div class="map-node access-node">
            <span class="map-label mono">access</span>
            <strong>Keycloak + OAuth gates</strong>
            <p>OIDC, SSO, account flows, request access, verification, and service boundaries.</p>
          </div>
        </div>

        <div class="map-arrow" aria-hidden="true">-&gt;</div>

        <div class="map-node cluster-node">
          <span class="map-label mono">atlas k3s</span>
          <strong>Flux-managed service platform</strong>
          <div class="node-grid" aria-label="Atlas cluster layers and operating systems">
            <span>
              <b>Control plane</b>
              HA k3s backed by the dedicated database node.
            </span>
            <span>
              <b>Workloads</b>
              Member apps, source control, CI, registry, media, and AI services.
            </span>
            <span>
              <b>State + storage</b>
              Longhorn volumes, storage pools, and PVC-backed workloads.
            </span>
            <span>
              <b>Secrets + identity</b>
              Vault, Keycloak, OAuth gates, and scoped configuration.
            </span>
            <span>
              <b>Observability</b>
              Prometheus, Grafana, OpenSearch, and Fluent Bit logs.
            </span>
            <span>
              <b>Backups + restore</b>
              Backup policies, restore checks, freshness reports, and recovery paths.
            </span>
            <span class="delivery-layer">
              <b>Delivery</b>
              <span class="delivery-chain mono">Gitea -> Jenkins -> Harbor -> Flux</span>
            </span>
          </div>
        </div>

        <div class="map-arrow" aria-hidden="true">&lt;-&gt;</div>

        <div class="map-column ops-column">
          <div class="map-node ops-node">
            <span class="map-label mono">operations</span>
            <strong>Operator tools + recovery</strong>
            <p>Remote control, backup status, node rebuild paths, restore checks, and operational reports.</p>
          </div>
          <div class="map-node dedicated-node">
            <span class="map-label mono">outside cluster</span>
            <strong>Dedicated hosts</strong>
            <p>Database node plus jumphost / KVM landing host for operations and controlled access.</p>
          </div>
        </div>
      </div>
    </div>

    <details class="source-diagrams">
      <summary>
        <span>
          <strong>Source diagrams</strong>
          <small>Mermaid views for full-screen inspection.</small>
        </span>
        <span class="pill mono">Open diagrams</span>
      </summary>

      <div class="platform-diagrams" aria-label="Titan Lab architecture diagrams">
        <MermaidCard
          class="architecture-primary"
          title="Atlas layout"
          description="Control plane, workers, accelerators, and edge assets."
          :diagram="hardwareDiagram"
          card-id="hardware-home"
        />
        <MermaidCard
          title="Build + deploy flow"
          description="Gitea to Jenkins to Harbor to Flux."
          :diagram="pipelineDiagram"
          card-id="pipeline-home"
        />
        <MermaidCard
          title="Ingress flow"
          description="DNS to Traefik to workloads backed by Longhorn."
          :diagram="networkDiagram"
          card-id="network-home"
        />
      </div>
    </details>
  </section>
</template>

<script setup>
import { computed } from "vue";
import MetricRow from "./MetricRow.vue";
import ServiceGrid from "./ServiceGrid.vue";
import MermaidCard from "./MermaidCard.vue";
import { buildHardwareDiagram, buildNetworkDiagram, buildPipelineDiagram, fallbackServices } from "../data/sample.js";

const props = defineProps({
  labData: Object,
  labStatus: Object,
  serviceData: Object,
  networkData: Object,
  metricsData: Object,
  loading: Boolean,
  error: String,
});

const atlasPillClass = computed(() => (props.labStatus?.atlas?.up ? "pill-ok" : "pill-bad"));
const dedicatedHosts = computed(() => props.labStatus?.dedicated_hosts || null);
const dedicatedHostItems = computed(() => {
  const hosts = dedicatedHosts.value?.hosts?.length
    ? dedicatedHosts.value.hosts
    : [
      { label: "Database node", known: false, up: false },
      { label: "Jumphost", known: false, up: false },
    ];
  return hosts.map((host) => {
    const state = hostState(host);
    return {
      label: host.label || "Dedicated host",
      state,
      value: statusLabelFrom(state),
    };
  });
});
const dedicatedHostsPillClass = computed(() => {
  if (!dedicatedHosts.value?.known) return "";
  return dedicatedHosts.value.up ? "pill-ok" : "pill-bad";
});
const dedicatedHostsLabel = computed(() => {
  if (!dedicatedHosts.value?.known) return "status pending";
  const total = Number.isFinite(dedicatedHosts.value.total) ? dedicatedHosts.value.total : 2;
  const upCount = Number.isFinite(dedicatedHosts.value.up_count) ? dedicatedHosts.value.up_count : 0;
  return `${upCount}/${total} responding`;
});

const metricItems = computed(() => {
  const items = props.metricsData?.items?.length
    ? props.metricsData.items
    : [
      {
        label: "Lab nodes",
        value: "26",
        note: "Atlas includes the main service fleet plus Oceanus and Tethys as cluster members.\nDedicated non-cluster hosts: titan-db and titan-jh.",
      },
      {
        label: "CPU cores",
        value: "142",
        note: "Mixed arm64 and amd64 capacity for platform, storage, media, and AI workloads.",
      },
      {
        label: "Memory",
        value: "552 GB",
        note: "Raspberry Pi, NVIDIA Jetson, and AMD64 systems across service and platform tiers.",
      },
      {
        label: "Storage",
        value: "80 TB",
        note: "Longhorn-backed storage pools for system and user workloads.",
      },
    ];
  return items.map((item) => ({
    ...item,
    note: item.note ? item.note.replaceAll("\t", "    ") : "",
  }));
});

const displayServices = computed(() => {
  const services = props.serviceData?.services || fallbackServices().services;
  return services
    .filter((svc) => svc.status !== "planned")
    .map((svc) => sanitizeService({
      ...svc,
      icon: svc.icon || pickIcon(svc.name),
    }))
    .sort((a, b) => serviceRank(a) - serviceRank(b) || a.name.localeCompare(b.name));
});
const featuredServices = computed(() => displayServices.value.slice(0, 12));

const hardwareDiagram = computed(() => buildHardwareDiagram(props.labData || {}));
const networkDiagram = computed(() => buildNetworkDiagram(props.networkData || {}));
const pipelineDiagram = computed(() => buildPipelineDiagram());

function sanitizeService(service) {
  const host = typeof service.host === "string" ? service.host : "";
  const link = typeof service.link === "string" ? service.link : "";
  const hostLooksInternal = host.includes(".svc.cluster.local") || (host && !host.includes(".") && !host.includes("/"));
  if (!hostLooksInternal) return service;
  return { ...service, host: link.startsWith("/") ? link : "" };
}

function pickIcon(name) {
  const h = name.toLowerCase();
  if (h.includes("nextcloud")) return "NC";
  if (h.includes("jellyfin")) return "JF";
  if (h.includes("matrix")) return "MX";
  if (h.includes("element")) return "EL";
  if (h.includes("livekit")) return "LK";
  if (h.includes("coturn") || h.includes("turn")) return "TN";
  if (h.includes("mail")) return "ML";
  if (h.includes("vaultwarden")) return "VW";
  if (h.includes("vault")) return "VT";
  if (h.includes("gitea")) return "GT";
  if (h.includes("jenkins")) return "JK";
  if (h.includes("harbor")) return "HB";
  if (h.includes("flux")) return "FX";
  if (h.includes("monero")) return "XM";
  if (h.includes("sui")) return "SU";
  if (h.includes("keycloak")) return "KC";
  if (h.includes("translation")) return "TR";
  if (h.includes("grafana")) return "GF";
  if (h.includes("pegasus")) return "PG";
  if (h.includes("cassandra")) return "CA";
  if (h.includes("ai chat")) return "AI";
  if (h.includes("ai image") || h.includes("vision")) return "VI";
  if (h.includes("ai speech")) return "SP";
  return "TL";
}

function serviceRank(service) {
  const priority = [
    "Keycloak",
    "Gitea",
    "Jenkins",
    "Harbor",
    "Flux",
    "Grafana",
    "OpenSearch",
    "Cassandra",
    "Nextcloud",
    "Outline",
    "Planka",
    "VaultWarden",
    "Vault",
  ];
  const index = priority.indexOf(service.name);
  return index === -1 ? priority.length : index;
}

function hostState(host) {
  if (!host || host.known === false) return "unknown";
  return host.up ? "ok" : "bad";
}

function statusLabelFrom(state) {
  if (state === "ok") return "Responding";
  if (state === "bad") return "Needs attention";
  return "Unknown";
}
</script>

<style scoped>
.platform-section,
.platform-subsection {
  box-sizing: border-box;
  scroll-margin-top: 0;
  margin-top: 0;
  padding: clamp(44px, 6vh, 72px) 0;
}

.platform-subsection {
  display: grid;
  align-content: start;
}

.section-kicker {
  color: var(--accent-cyan);
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 8px;
}

.section-heading {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: 18px;
}

.section-heading h2 {
  margin: 0;
  font-size: clamp(30px, 5vw, 44px);
}

.section-heading p {
  max-width: 760px;
  margin: 10px 0 0;
  font-size: 17px;
  line-height: 1.65;
}

.section-link {
  flex: 0 0 auto;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  padding: 10px 14px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 10px;
  color: var(--text-strong);
}

.section-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 10px;
}

.platform-subheading {
  margin-bottom: 18px;
}

.platform-dashboard {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(430px, 1.1fr);
  gap: 14px;
  padding: 18px;
  margin-bottom: 0;
  background:
    radial-gradient(circle at 82% 10%, rgba(0, 229, 197, 0.1), transparent 28%),
    linear-gradient(135deg, rgba(255, 255, 255, 0.045), rgba(5, 12, 28, 0.72));
}

.dashboard-copy,
.dashboard-panel {
  min-width: 0;
}

.eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  margin: 0 0 6px;
  font-size: 13px;
}

h3,
h4 {
  margin-top: 0;
}

.platform-dashboard h3 {
  font-size: clamp(24px, 3vw, 32px);
  margin-bottom: 8px;
}

.lede {
  margin: 0 0 12px;
  color: var(--text-muted);
  max-width: 650px;
  line-height: 1.62;
}

.bullets {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin: 10px 0 12px;
}

.live-status {
  margin-top: 18px;
}

.status {
  color: var(--text-muted);
}

.fallback-copy {
  margin: 8px 0 0;
}

.availability {
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--radius);
  padding: 10px;
  background: rgba(255, 255, 255, 0.03);
  min-width: 0;
}

.dashboard-metrics {
  grid-column: 1 / -1;
  margin: 2px 0 0;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

.dashboard-metrics :deep(.metric-card) {
  min-height: 116px;
  padding: 13px;
}

.availability-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.availability-panel {
  min-width: 0;
  display: grid;
  gap: 10px;
  align-content: start;
  padding: 10px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.025);
}

.availability-panel iframe {
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius-sm);
}

.availability-title {
  align-items: flex-start;
  margin-bottom: 0;
}

.availability-title h4 {
  margin: 0;
}

.panel-subtitle {
  margin: 3px 0 0;
  color: var(--text-muted);
  font-size: 13px;
}

.panel-subtitle.pill-ok {
  color: var(--accent-cyan);
}

.panel-subtitle.pill-bad {
  color: var(--accent-rose);
}

.placeholder {
  border: 1px dashed rgba(255, 255, 255, 0.2);
  border-radius: var(--radius-sm);
  padding: 12px;
  color: var(--text-muted);
  text-align: center;
  min-height: 180px;
  display: grid;
  place-items: center;
}

.host-status-list {
  min-height: 180px;
  display: grid;
  align-content: center;
  gap: 10px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius-sm);
  padding: 12px;
  background: rgba(255, 255, 255, 0.025);
}

.host-status-row {
  display: grid;
  grid-template-columns: 12px 1fr;
  gap: 10px;
  align-items: start;
  padding: 10px;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.035);
}

.host-dot {
  width: 10px;
  height: 10px;
  border-radius: 999px;
  margin-top: 5px;
  background: var(--text-muted);
}

.host-dot.ok {
  background: var(--accent-cyan);
}

.host-dot.bad {
  background: var(--accent-rose);
}

.host-label {
  color: var(--text-strong);
  font-weight: 800;
}

.host-value {
  color: var(--text-muted);
  font-size: 13px;
}

.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}

.platform-card {
  margin-top: 0;
}

.platform-services-section .platform-card {
  overflow: visible;
}

.architecture-board {
  padding: 20px;
  margin-bottom: 14px;
}

.board-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 18px;
}

.board-head h3 {
  margin: 0 0 6px;
  font-size: clamp(22px, 3vw, 30px);
}

.board-head p {
  max-width: 720px;
  margin: 0;
  color: var(--text-muted);
  line-height: 1.55;
}

.architecture-map {
  display: grid;
  grid-template-columns: minmax(210px, 0.82fr) 28px minmax(450px, 1.65fr) 28px minmax(220px, 0.9fr);
  gap: 12px;
  align-items: stretch;
}

.map-column {
  min-width: 0;
  display: grid;
  gap: 12px;
  align-content: stretch;
}

.map-node {
  min-width: 0;
  display: grid;
  align-content: start;
  gap: 8px;
  padding: 16px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.035);
}

.map-node strong {
  color: var(--text-strong);
  font-size: 17px;
}

.map-node p {
  margin: 0;
  color: var(--text-muted);
  line-height: 1.5;
}

.map-label {
  width: fit-content;
  padding: 5px 8px;
  border-radius: 999px;
  color: var(--accent-cyan);
  background: rgba(0, 229, 197, 0.08);
  border: 1px solid rgba(0, 229, 197, 0.18);
  font-size: 11px;
  text-transform: uppercase;
}

.edge-node {
  border-color: rgba(0, 229, 197, 0.16);
}

.access-node {
  border-color: rgba(0, 229, 197, 0.12);
}

.cluster-node {
  border-color: rgba(120, 180, 255, 0.18);
  background:
    linear-gradient(135deg, rgba(0, 229, 197, 0.05), rgba(120, 180, 255, 0.08)),
    rgba(255, 255, 255, 0.035);
}

.ops-node {
  border-color: rgba(127, 124, 255, 0.2);
}

.dedicated-node {
  border-color: rgba(255, 220, 120, 0.22);
}

.map-arrow {
  display: grid;
  place-items: center;
  color: var(--accent-cyan);
  font-weight: 800;
  opacity: 0.8;
  align-self: center;
  min-height: 100%;
}

.node-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  margin-top: 8px;
}

.node-grid > span {
  min-height: 92px;
  display: grid;
  align-content: start;
  gap: 4px;
  padding: 12px;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  background: rgba(3, 8, 18, 0.55);
  color: var(--text-muted);
  font-size: 14px;
  line-height: 1.42;
}

.node-grid > span b {
  display: block;
  color: var(--text-strong);
}

.delivery-chain {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--text-primary);
  white-space: nowrap;
}

.delivery-layer {
  grid-column: 1 / -1;
  min-height: 74px;
}

.source-diagrams {
  margin-top: 14px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.025);
  overflow: hidden;
}

.source-diagrams summary {
  min-height: 50px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  cursor: pointer;
  list-style: none;
}

.source-diagrams summary::-webkit-details-marker {
  display: none;
}

.source-diagrams small {
  display: inline;
  margin-left: 4px;
  color: var(--text-muted);
  font-weight: 400;
}

.platform-diagrams {
  padding: 0 14px 14px;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  align-items: stretch;
}

.platform-diagrams :deep(.mermaid-card) {
  min-height: 260px;
}

.platform-diagrams :deep(.diagram) {
  min-height: 150px;
}

.platform-diagrams :deep(.diagram svg) {
  min-width: 0;
  max-width: 100%;
}

@media (max-width: 980px) {
  .section-heading,
  .platform-dashboard {
    grid-template-columns: 1fr;
  }

  .section-heading {
    display: grid;
  }

  .platform-section,
  .platform-subsection {
    padding: 44px 0;
  }

  .dashboard-metrics {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .platform-diagrams {
    grid-template-columns: 1fr;
  }

  .architecture-map {
    grid-template-columns: 1fr;
  }

  .map-arrow {
    min-height: 18px;
    transform: rotate(90deg);
  }

  .platform-diagrams :deep(.diagram) {
    min-height: 220px;
  }

  .section-actions {
    justify-content: flex-start;
  }
}

@media (max-width: 720px) {
  .availability-grid {
    grid-template-columns: 1fr;
  }

  .platform-dashboard {
    padding: 14px;
  }

  .dashboard-metrics {
    grid-template-columns: 1fr;
  }

  .board-head,
  .source-diagrams summary {
    display: grid;
  }

  .node-grid {
    grid-template-columns: 1fr;
  }

  .section-head {
    align-items: flex-start;
    flex-direction: column;
  }
}
</style>
