<!--
Copyright (c) 2026 Sidero Labs, Inc.

Use of this software is governed by the Business Source License
included in the LICENSE file.
-->
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useRouter } from 'vue-router'

import { Runtime } from '@/api/common/omni.pb'
import type { Resource } from '@/api/grpc'
import type { MachineStatusLinkSpec } from '@/api/omni/specs/ephemeral.pb'
import type { InfraProviderStatusSpec } from '@/api/omni/specs/infra.pb'
import type { MachineStatusMetricsSpec } from '@/api/omni/specs/omni.pb'
import {
  EphemeralNamespace,
  InfraProviderNamespace,
  InfraProviderStatusType,
  LabelInfraProviderID,
  LabelIsManagedByStaticInfraProvider,
  LabelMachineRequest,
  LabelsCompletionType,
  MachineStatusLinkType,
  MachineStatusMetricsID,
  MachineStatusMetricsType,
  MachineStatusType,
  MetricsNamespace,
  VirtualNamespace,
} from '@/api/resources'
import TButton from '@/components/Button/TButton.vue'
import TList from '@/components/List/TList.vue'
import PageContainer from '@/components/PageContainer/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue'
import StatsItem from '@/components/Stats/StatsItem.vue'
import TAlert from '@/components/TAlert.vue'
import { getDocsLink } from '@/methods'
import type { Label } from '@/methods/labels'
import { addLabel, selectors as labelsToSelectors } from '@/methods/labels'
import { MachineFilterOption } from '@/methods/machine'
import { useResourceWatch } from '@/methods/useResourceWatch'
import LabelsInput from '@/views/ItemLabels/LabelsInput.vue'
import AddingMachinesTutorial from '@/views/Machines/components/AddingMachinesTutorial.vue'
import MachineDetailsPanel from '@/views/Machines/MachineDetailsPanel.vue'
import MachineItem from '@/views/Machines/MachineItem.vue'

const { filter, provider } = defineProps<{
  filter?: MachineFilterOption
  provider?: string
}>()

const router = useRouter()

const { data: infraProviderStatuses } = useResourceWatch<InfraProviderStatusSpec>({
  resource: {
    type: InfraProviderStatusType,
    namespace: InfraProviderNamespace,
  },
  runtime: Runtime.Omni,
})

const selectors = computed(() => {
  const selectors: string[] = []

  switch (filter) {
    case MachineFilterOption.Manual:
      selectors.push(`!${LabelMachineRequest},!${LabelIsManagedByStaticInfraProvider}`)
      break
    case MachineFilterOption.Managed:
      selectors.push(`${LabelMachineRequest}`, `${LabelIsManagedByStaticInfraProvider}`)
      break
  }

  if (provider) {
    selectors.push(`${LabelInfraProviderID}=${provider}`)
  }

  const q = labelsToSelectors(filterLabels.value)?.join(',')

  if (!q) return selectors
  if (!selectors.length) return [q]

  return selectors.map((item) => `${item},${q}`)
})

const getCapacity = (item?: Resource<MachineStatusMetricsSpec>) => {
  const registered = item?.spec.registered_machines_count ?? 0
  if (registered === 0) {
    return 0
  }

  const allocated = item?.spec.allocated_machines_count ?? 0
  const free = registered - allocated

  return parseInt(((free / registered) * 100).toFixed(0))
}

const sortOptions = [
  { id: 'machine_created_at', desc: 'Creation Time ⬆', descending: true },
  { id: 'machine_created_at', desc: 'Creation Time ⬇' },
  { id: 'hostname', desc: 'Hostname ⬆' },
  { id: 'hostname', desc: 'Hostname ⬇', descending: true },
  { id: 'id', desc: 'ID ⬆' },
  { id: 'id', desc: 'ID ⬇', descending: true },
  { id: 'last_alive', desc: 'Last Active Time ⬆', descending: true },
  { id: 'last_alive', desc: 'Last Active Time ⬇' },
]

const filterLabels = ref<Label[]>([])
const filterValue = ref('')

const docsLink = getDocsLink('omni', '/explanation/infrastructure-providers')
const openDocs = () => window.open(docsLink, '_blank')?.focus()

const { data: machineStatusMetrics } = useResourceWatch<MachineStatusMetricsSpec>({
  resource: {
    type: MachineStatusMetricsType,
    id: MachineStatusMetricsID,
    namespace: EphemeralNamespace,
  },
  runtime: Runtime.Omni,
})

function deleteItems() {
  const machines = [...selectedMachines.value.keys()]
  const clusters = [...selectedMachines.value.values()]
    .map((m) => m.spec.message_status?.cluster)
    .filter((m) => typeof m === 'string')

  router.push({
    query: {
      modal: 'machineRemove',
      machine: machines,
      cluster: [...new Set(clusters)],
    },
  })
}

const selectedMachines = ref<Map<string, Resource<MachineStatusLinkSpec>>>(new Map())
function updateSelected(machine: Resource<MachineStatusLinkSpec>, v?: boolean) {
  const id = machine.metadata.id ?? ''

  if (v) {
    selectedMachines.value.set(id, machine)
  } else {
    selectedMachines.value.delete(id)
  }
}
</script>

<template>
  <PageContainer class="h-full">
    <TList
      :opts="{
        runtime: Runtime.Omni,
        resource: {
          type: MachineStatusLinkType,
          namespace: MetricsNamespace,
        },
        selectors,
        selectUsingOR: true,
      }"
      search
      pagination
      :sort-options="sortOptions"
      :filter-value="filterValue"
    >
      <template #norecords>
        <TAlert
          v-if="filter === MachineFilterOption.Managed && infraProviderStatuses.length === 0"
          type="info"
          title="No Infrastructure Providers Connected"
        >
          <div class="flex gap-1">
            Check the
            <TButton variant="subtle" size="xs" @click="openDocs">documentation</TButton>
            on how to configure and use infrastructure providers.
          </div>
        </TAlert>

        <TAlert
          v-else-if="filter === MachineFilterOption.Manual"
          type="info"
          title="No Machines Found"
        >
          <div class="flex gap-1">
            Download and boot the
            <TButton
              is="router-link"
              :to="{ name: 'InstallationMedia' }"
              variant="subtle"
              size="xs"
            >
              installation media
            </TButton>
            to connect machines to your Omni instance.
          </div>
        </TAlert>

        <TAlert v-else type="info" title="No Machines Found">
          <div class="flex gap-1">
            No entries of the requested resource type are found on the server.
          </div>
        </TAlert>

        <AddingMachinesTutorial class="mt-4" />
      </template>

      <template #header="{ itemsCount, filtered }">
        <div v-if="!filter" class="flex flex-wrap items-center gap-x-6 gap-y-2">
          <h1 class="text-xl font-medium text-naturals-n14 max-md:basis-full">Machines</h1>

          <StatsItem title="Total" :value="itemsCount" icon="nodes" />

          <template v-if="!filtered">
            <StatsItem
              title="Allocated"
              :value="machineStatusMetrics?.spec.allocated_machines_count ?? 0"
              icon="arrow-right-square"
            />

            <StatsItem
              title="Capacity"
              :value="`${getCapacity(machineStatusMetrics)}%`"
              icon="box"
            />
          </template>
        </div>

        <PageHeader
          v-else-if="filter === MachineFilterOption.Manual"
          title="Manually Joined Machines"
        />

        <PageHeader
          v-else-if="filter === MachineFilterOption.Managed"
          title="Machines Managed by the Infrastructure Providers"
        />

        <PageHeader
          v-else-if="provider"
          :title="`Machines Managed by the Infrastructure Provider ${provider}`"
        />
      </template>

      <template #input>
        <LabelsInput
          v-model:filter-labels="filterLabels"
          v-model:filter-value="filterValue"
          :completions-resource="{
            id: MachineStatusType,
            type: LabelsCompletionType,
            namespace: VirtualNamespace,
          }"
          class="w-full"
          placeholder="Search ..."
        />
      </template>

      <template #extra-controls>
        <TButton
          variant="primary"
          icon="delete"
          :disabled="!selectedMachines.size"
          @click="deleteItems"
        >
          <span class="contents max-md:hidden">Delete selected</span>
        </TButton>
      </template>

      <template
        #default="{ items, searchQuery, sidePanelOpen, sidePanelSelectedItemId, openPanel }"
      >
        <MachineItem
          v-for="item in items"
          :key="item.metadata.id"
          :machine="item"
          :search-query="searchQuery"
          :panel-open="sidePanelOpen && item.metadata.id === sidePanelSelectedItemId"
          :selected="selectedMachines.has(item.metadata.id ?? '')"
          @update:selected="(v) => updateSelected(item, v)"
          @open-panel="openPanel(item.metadata.id ?? '')"
          @filter-labels="(label) => addLabel(filterLabels, label)"
        />
      </template>

      <template #sidePanel="{ items, searchQuery, sidePanelSelectedItemId, closePanel }">
        <MachineDetailsPanel
          :machine="items.find((i) => i.metadata.id === sidePanelSelectedItemId)"
          :search-query="searchQuery"
          class="h-full"
          @close="closePanel"
        />
      </template>
    </TList>
  </PageContainer>
</template>
