<!--
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 { onMounted, ref } from 'vue'

import { Runtime } from '@/api/common/omni.pb'
import { Code } from '@/api/google/rpc/code.pb'
import type { Resource } from '@/api/grpc'
import { ResourceService } from '@/api/grpc'
import type { EtcdBackupOverallStatusSpec, EtcdBackupS3ConfSpec } from '@/api/omni/specs/omni.pb'
import { withRuntime } from '@/api/options'
import {
  DefaultNamespace,
  EtcdBackupOverallStatusID,
  EtcdBackupOverallStatusType,
  EtcdBackupS3ConfID,
  EtcdBackupS3ConfType,
  MetricsNamespace,
} from '@/api/resources'
import TButton from '@/components/Button/TButton.vue'
import PageContainer from '@/components/PageContainer/PageContainer.vue'
import PageHeader from '@/components/PageHeader.vue'
import TSpinner from '@/components/Spinner/TSpinner.vue'
import TAlert from '@/components/TAlert.vue'
import TInput from '@/components/TInput/TInput.vue'
import { getDocsLink } from '@/methods'
import { usePermissions } from '@/methods/auth'
import { showError, showSuccess } from '@/notification'

definePage({
  name: 'BackupStorage',
})

const { canManageBackupStore } = usePermissions()

const ready = ref(false)
const s3Spec = ref<EtcdBackupS3ConfSpec>({})
const store = ref('unknown')
const error = ref<string>()
const saving = ref(false)

const s3ConfigMetadata = {
  id: EtcdBackupS3ConfID,
  type: EtcdBackupS3ConfType,
  namespace: DefaultNamespace,
}

onMounted(async () => {
  const status: Resource<EtcdBackupOverallStatusSpec> = await ResourceService.Get(
    {
      id: EtcdBackupOverallStatusID,
      type: EtcdBackupOverallStatusType,
      namespace: MetricsNamespace,
    },
    withRuntime(Runtime.Omni),
  )

  store.value = status.spec.configuration_name!

  if (status.spec.configuration_name !== 's3') {
    ready.value = true

    return
  }

  try {
    const config = await ResourceService.Get(s3ConfigMetadata, withRuntime(Runtime.Omni))

    s3Spec.value = config?.spec
  } catch (e) {
    if (e.code !== Code.NOT_FOUND) {
      error.value = e.message
    }

    s3Spec.value = {}
  } finally {
    ready.value = true
  }
})

const resetConfig = async () => {
  saving.value = true

  try {
    await ResourceService.Delete(s3ConfigMetadata, withRuntime(Runtime.Omni))
  } catch (e) {
    if (e.code !== Code.NOT_FOUND) {
      showError('Failed to Update Storage Config', e.message)

      saving.value = false

      return
    }
  } finally {
    saving.value = false
  }

  s3Spec.value = {}

  showSuccess('The Backup Storage Config was Reset')
}

const updateConfig = async () => {
  if (Object.keys(s3Spec.value).length === 0) {
    showError('Failed to Update Storage Config', 'No configuration was specified')

    return
  }

  saving.value = true
  let config: Resource<EtcdBackupS3ConfSpec> = {
    metadata: s3ConfigMetadata,
    spec: {},
  }

  let exists: boolean = false

  try {
    config = await ResourceService.Get(s3ConfigMetadata, withRuntime(Runtime.Omni))

    exists = true
  } catch (e) {
    if (e.code !== Code.NOT_FOUND) {
      showError('Failed to Update Storage Config', e.message)

      saving.value = false

      return
    }
  }

  config.spec = s3Spec.value

  try {
    if (exists) {
      await ResourceService.Update(config, config.metadata.version || 0, withRuntime(Runtime.Omni))
    } else {
      await ResourceService.Create(config, withRuntime(Runtime.Omni))
    }
  } catch (e) {
    showError('Failed to Update Storage Config', e.message)

    return
  } finally {
    saving.value = false
  }

  showSuccess('The Backup Storage Config was Updated')
}
</script>

<template>
  <PageContainer class="flex h-full flex-col gap-4">
    <div class="flex items-start gap-1">
      <PageHeader title="Settings" subtitle="Backup Storage" class="flex-1" />
    </div>

    <div class="flex grow flex-col gap-5">
      <!-- prettier-ignore -->
      <p class="text-sm">
      Automatically back up your Kubernetes etcd databases to the configured endpoint and bucket. To
      restore a cluster backup please follow the
      <a
        class="link-primary"
        :href="
          getDocsLink(
            'omni',
            '/cluster-management/restore-etcd-of-a-cluster-managed-by-cluster-templates#finding-the-snapshot-to-restore',
          )
        "
        target="_blank"
        rel="noopener noreferrer"
      >
        etcd restore documentation
      </a>.
      To back up a self-hosted Omni database please see
      <a
        class="link-primary"
        :href="getDocsLink('omni', '/self-hosted/back-up-omni-db')"
        target="_blank"
        rel="noopener noreferrer"
      >
        how to back up your Omni database
      </a>.
    </p>

      <TAlert v-if="error" title="Failed to Fetch Current Storage State" type="error">
        {{ error }}
      </TAlert>
      <div v-else-if="ready && !saving" class="flex flex-col gap-5" @keydown.enter="updateConfig">
        <div class="font-bold text-naturals-n14">
          Storage Type {{ `${store.charAt(0).toUpperCase()}${store.slice(1)}` }}
        </div>
        <template v-if="s3Spec">
          <TInput
            title="Endpoint"
            :model-value="s3Spec.endpoint || ''"
            @update:model-value="(value) => (s3Spec.endpoint = value)"
          />
          <TInput
            title="Bucket"
            :model-value="s3Spec.bucket || ''"
            @update:model-value="(value) => (s3Spec.bucket = value)"
          />
          <TInput
            title="Region"
            :model-value="s3Spec.region || ''"
            @update:model-value="(value) => (s3Spec.region = value)"
          />
          <div class="flex gap-5">
            <TInput
              title="Access Key ID"
              type="password"
              class="flex-shrinl flex-1"
              :model-value="s3Spec.access_key_id || ''"
              @update:model-value="(value) => (s3Spec.access_key_id = value)"
            />
            <TInput
              title="Secret Access Key"
              type="password"
              class="flex-1"
              :model-value="s3Spec.secret_access_key || ''"
              @update:model-value="(value) => (s3Spec.secret_access_key = value)"
            />
          </div>
          <TInput
            title="Session Token"
            :model-value="s3Spec.session_token || ''"
            @update:model-value="(value) => (s3Spec.session_token = value)"
          />
          <div class="flex gap-2 place-self-end">
            <TButton :disabled="!canManageBackupStore" @click="resetConfig">Reset</TButton>
            <TButton :disabled="!canManageBackupStore" variant="highlighted" @click="updateConfig">
              Save
            </TButton>
          </div>
        </template>
      </div>
      <div v-else class="flex flex-1 items-center justify-center">
        <TSpinner class="h-6 w-6" />
      </div>
    </div>
  </PageContainer>
</template>
