<template>
  <div class="space-y-4">
    <form @submit.prevent="submitForm" class="client-form" ref="clientCreateForm">
      <sl-input
        label="Name"
        required
        autocomplete="off"
        help-text="Name of the client, usually the hostname"
        :disabled="isEdit"
        :value="name"
        @blur="checkName"
        @sl-input="name = $event.target.value"
        @input="emit('clearErrors')"
        ref="nameField"
      ></sl-input>
      <br />
      <sl-input
        label="Description"
        autocomplete="off"
        help-text="Optional description of the client"
        :value="description"
        @input="description = $event.target.value"
      ></sl-input>
      <br />
      <sl-input
        label="Permitted Sources"
        :value="sourcePrefix"
        @input="sourcePrefix = $event.target.value"
        @blur="addPolicy"
        @keydown.enter.prevent="addPolicy"
        autocomplete="off"
        help-text="Enter the source IP addresses or IP prefixes that the client may connect from. Press enter or go to next field to add"
        ref="sourceField"
      >
        <sl-icon-button name="plus-circle" slot="suffix" @click="addPolicy"></sl-icon-button>
      </sl-input>
      <div class="my-2 border-b border-gray-100">
        <p>Added sources</p>
        <sl-tag
          class="mr-1 mb-1"
          v-for="(prefix, index) in policies"
          size="small"
          pill
          removable
          @sl-remove="removePolicy(index)"
        >
          {{ prefix }}
        </sl-tag>
      </div>
      <sl-textarea
        label="Public Key"
        autocomplete="off"
        required
        help-text="Enter the RSA public key of the client."
        ref="publicKeyField"
        :value="publicKey"
        @sl-input="publicKey = $event.target.value"
        @blur="validatePublicKey"
      ></sl-textarea>
      <br />
      <sl-button type="submit" variant="primary" v-if="isEdit">Update Client</sl-button>
      <sl-button type="submit" variant="primary" v-else>Create Client</sl-button>
    </form>
  </div>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import type { Ref } from 'vue'
import { isIP } from 'is-ip'
import isCidr from 'is-cidr'

import { setFieldValidation } from '@/api/validation'

import type { ClientCreate } from '@/client/types.gen'

const name = ref('')
const description = ref('')
const sourcePrefix = ref('')
const policies = ref(['0.0.0.0/0', '::/0'])
const publicKey = ref('')

const nameField = ref<HTMLSlInputElement>()
const sourceField = ref<HTMLSlInputElement>()
const publicKeyField = ref<HTMLSlInputElement>()
const clientCreateForm = ref<HTMLElement>()

interface Props {
  client?: Client
  errors?: any[]
}
const props = defineProps<Props>()
const emit = defineEmits<{
  (e: 'submit', data: ClientCreate): void
  (e: 'cancel'): void
  (e: 'clearErrors'): void
}>()

const isEdit = computed(() => !!props.client)

watch(
  () => props.client,
  (client) => {
    if (client) {
      name.value = client.name
      if (client.description) {
        description.value = client.description
      }
      publicKey.value = client.public_key
      policies.value = client.policies
    }
  },
  { immediate: true },
)

function addPolicy() {
  if (!sourcePrefix.value) {
    setFieldValidation(sourceField)
    return
  }
  if (!isIP(sourcePrefix.value) && !isCidr(sourcePrefix.value)) {
    setFieldValidation(sourceField, 'Invalid IP address or prefix')
    return
  }

  setFieldValidation(sourceField)
  if (!policies.value.includes(sourcePrefix.value)) {
    policies.value.push(sourcePrefix.value)
  }
  sourcePrefix.value = ''
}

function removePolicy(index: number) {
  policies.value.splice(index, 1)
  if (policies.value.length == 0) {
    setFieldValidation(sourceField, 'Must have at least one source defined')
  }
}

function checkName() {
  nameField.value.reportValidity()
  emit('clearErrors')
}

function validatePublicKey() {
  const pubkey = publicKey.value
  const defaultError = 'Invalid public key. Must be a valid ssh-rsa key.'
  if (!pubkey.startsWith('ssh-rsa ')) {
    setFieldValidation(publicKeyField, defaultError)
    return
  }
  const parts = publicKey.value.split(' ')
  if (parts.length < 2) {
    setFieldValidation(publicKeyField, defaultError)
    return
  }
  if (parts[1].length < 10) {
    setFieldValidation(publicKeyField, 'SSH Key looks too short to be valid.')
    return
  }

  setFieldValidation(publicKeyField, '')
}

function resetValidation() {
  setFieldValidation(nameField, '')
  setFieldValidation(sourceField, '')
  setFieldValidation(publicKeyField, '')
}

watch(
  () => props.errors,
  (errors) => {
    resetValidation()
    const nameErrors = errors.filter((e) => e.loc.includes('name'))
    const sourceErrors = errors.filter((e) => e.loc.includes('source'))
    const publicKeyError = errors.filter((e) => e.loc.includes('public_key'))
    if (nameErrors.length > 0) {
      setFieldValidation(nameField, nameErrors[0].msg)
    }
    if (sourceErrors.length > 0) {
      setFieldValidation(sourceField, sourceErrors[0].msg)
    }
    if (publicKeyError.length > 0) {
      setFieldValidation(publicKeyField, publicKeyErrors[0].msg)
    }
  },
)

async function submitForm() {
  if (clientCreateForm.value?.checkValidity()) {
    let clientDescription: string | null = null
    if (description.value) {
      clientDescription = description.value
    }
    const clientCreate: ClientCreate = {
      name: name.value,
      description: clientDescription,
      public_key: publicKey.value.trim(),
      sources: [...policies.value],
    }
    emit('submit', clientCreate)
  }
}
</script>
