<template>
  <UContainer class="py-8">
    <div class="mx-auto max-w-5xl space-y-6">
      <div class="space-y-2">
        <UBadge label="Security" color="primary" variant="soft" class="rounded-full" />
        <h1 class="text-3xl font-bold text-highlighted">
          Password and passkey settings
        </h1>
      </div>

      <div class="grid gap-6 lg:grid-cols-[1.05fr_0.95fr]">
        <UCard class="border border-default bg-default">
          <template #header>
            <h2 class="text-xl font-semibold text-highlighted">
              Change password
            </h2>
          </template>

          <UForm :state="passwordForm" :validate="validatePasswordForm" class="space-y-5" @submit="changePassword">
            <UFormField name="currentPassword" label="Current Password" required>
              <UInput v-model="passwordForm.currentPassword" type="password" size="xl" class="w-full" />
            </UFormField>

            <UFormField name="newPassword" label="New Password" required>
              <UInput v-model="passwordForm.newPassword" type="password" size="xl" class="w-full" />
            </UFormField>

            <UFormField name="confirmPassword" label="Confirm New Password" required>
              <UInput v-model="passwordForm.confirmPassword" type="password" size="xl" class="w-full" />
            </UFormField>

            <UButton
              type="submit"
              label="Update Password"
              size="xl"
              class="w-full justify-center"
              :loading="passwordPending"
            />
          </UForm>
        </UCard>

        <UCard class="border border-default bg-default">
          <template #header>
            <h2 class="text-xl font-semibold text-highlighted">
              Passkeys
            </h2>
          </template>

          <div class="space-y-5">
            <div class="rounded-2xl border border-default bg-muted/40 p-4">
              <div class="flex items-center justify-between gap-3">
                <div>
                  <div class="text-sm font-semibold text-highlighted">
                    Registered passkeys
                  </div>
                  <div class="text-sm text-muted">
                    {{ passkeys.length }} passkey{{ passkeys.length === 1 ? '' : 's' }} connected to this account
                  </div>
                </div>

                <UBadge
                  :label="passkeys.length > 0 ? 'Ready' : 'Required'"
                  :color="passkeys.length > 0 ? 'success' : 'warning'"
                  variant="soft"
                />
              </div>
            </div>

            <UButton
              label="Register New Passkey"
              size="xl"
              class="w-full justify-center"
              icon="i-lucide-fingerprint"
              :loading="passkeyPending"
              @click="registerPasskey"
            />

            <div v-if="passkeys.length" class="space-y-3">
              <div
                v-for="passkey in passkeys"
                :key="passkey.id"
                class="rounded-2xl border border-default bg-default px-4 py-4"
              >
                <div class="flex items-center justify-between gap-3">
                  <div>
                    <div class="font-semibold text-highlighted">
                      {{ passkey.label }}
                    </div>
                    <div class="text-sm text-muted">
                      Added {{ formatDate(passkey.createdAt) }}
                    </div>
                  </div>

                  <UBadge :label="passkey.deviceType === 'multiDevice' ? 'Synced' : 'Single Device'" color="neutral" variant="soft" />
                </div>
              </div>
            </div>
          </div>
        </UCard>
      </div>
    </div>
  </UContainer>
</template>

<script lang="ts" setup>
import type { FormError, FormSubmitEvent } from '@nuxt/ui'

import { MIN_PASSWORD_LENGTH, type PasskeySummary } from '~~/shared/auth'

definePageMeta({
  middleware: 'auth'
})

const toast = useToast()
const router = useRouter()
const auth = useAuth()
const apiClient = useApiClient()

const passwordPending = ref(false)
const passkeyPending = ref(false)
const passkeys = ref<PasskeySummary[]>([])

const passwordForm = reactive({
  currentPassword: '',
  newPassword: '',
  confirmPassword: ''
})

await fetchPasskeys()

function validatePasswordForm(state: typeof passwordForm): FormError[] {
  const errors: FormError[] = []

  if (!state.currentPassword.trim()) {
    errors.push({ name: 'currentPassword', message: 'Enter your current password.' })
  }

  if (state.newPassword.trim().length < MIN_PASSWORD_LENGTH) {
    errors.push({ name: 'newPassword', message: `Use at least ${MIN_PASSWORD_LENGTH} characters.` })
  }

  if (state.confirmPassword.trim() !== state.newPassword.trim()) {
    errors.push({ name: 'confirmPassword', message: 'Confirmation does not match the new password.' })
  }

  return errors
}

async function fetchPasskeys() {
  const response = await apiClient<{ passkeys: PasskeySummary[] }>('/api/auth/passkeys')
  passkeys.value = response.passkeys
}

function maybeRedirectAfterOnboarding(previouslyRequired: boolean) {
  if (previouslyRequired && !auth.needsOnboarding.value) {
    router.push(auth.isSuperAdmin.value ? '/management/users' : '/security')
  }
}

async function changePassword(event: FormSubmitEvent<typeof passwordForm>) {
  event.preventDefault()

  if (passwordPending.value) {
    return
  }

  passwordPending.value = true
  const previouslyRequired = auth.needsOnboarding.value

  try {
    const response = await apiClient<{ user: typeof auth.user.value }>('/api/auth/change-password', {
      method: 'POST',
      body: {
        currentPassword: passwordForm.currentPassword,
        newPassword: passwordForm.newPassword
      }
    })

    auth.setUser(response.user)
    passwordForm.currentPassword = ''
    passwordForm.newPassword = ''
    passwordForm.confirmPassword = ''

    toast.add({
      title: 'Password updated',
      description: 'Your account password has been changed.',
      color: 'success',
      icon: 'i-lucide-check-circle-2'
    })

    maybeRedirectAfterOnboarding(previouslyRequired)
  } catch (error: any) {
    toast.add({
      title: 'Password update failed',
      description: error?.data?.statusMessage || 'Unable to update your password.',
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
  } finally {
    passwordPending.value = false
  }
}

async function registerPasskey() {
  if (passkeyPending.value) {
    return
  }

  passkeyPending.value = true
  const previouslyRequired = auth.needsOnboarding.value

  try {
    const { browserSupportsWebAuthn, startRegistration } = await import('@simplewebauthn/browser')

    if (!browserSupportsWebAuthn()) {
      throw new Error('This browser does not support passkey registration.')
    }

    const optionsResponse = await apiClient<{ options: Record<string, any> }>('/api/auth/passkey/register/options', {
      method: 'POST'
    })
    const credential = await startRegistration({
      optionsJSON: optionsResponse.options
    })
    const verification = await apiClient<{
      user: typeof auth.user.value
      passkeys: PasskeySummary[]
    }>('/api/auth/passkey/register/verify', {
      method: 'POST',
      body: {
        response: credential
      }
    })

    auth.setUser(verification.user)
    passkeys.value = verification.passkeys

    toast.add({
      title: 'Passkey registered',
      description: 'You can now use this passkey to sign in.',
      color: 'success',
      icon: 'i-lucide-check-circle-2'
    })

    maybeRedirectAfterOnboarding(previouslyRequired)
  } catch (error: any) {
    toast.add({
      title: 'Passkey registration failed',
      description: error?.data?.statusMessage || error?.message || 'Unable to register a passkey.',
      color: 'error',
      icon: 'i-lucide-circle-alert'
    })
  } finally {
    passkeyPending.value = false
  }
}

function formatDate(value: string | null) {
  if (!value) {
    return 'Not available'
  }

  return new Intl.DateTimeFormat('en-MY', {
    dateStyle: 'medium',
    timeStyle: 'short'
  }).format(new Date(value))
}
</script>
