<script lang="ts" setup>
import { computed, onMounted, reactive, ref } from 'vue'
import * as select from '@zag-js/select'
import Swal from 'sweetalert2'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Box } from '@/components/ui/box'
import { Field, FieldError, FieldGroup, FieldLabel } from '@/components/ui/field'
import { Input } from '@/components/ui/input'
import { Lucide } from '@/components/ui/lucide'
import {
  SelectRoot,
  SelectControl,
  SelectTrigger,
  SelectValueText,
  SelectContent,
  SelectItemGroup,
  SelectItemGroupLabel,
  SelectItem,
  SelectItemText,
} from '@/components/ui/select'
import { getApiErrorMessage, getApiValidationErrors } from '@/core/utils/getApiErrorMessage'
import { listActiveBanks } from '@/modules/profile/services/bank.service'
import {
  createBankDetail,
  deleteBankDetail,
  listBankDetails,
  updateBankDetail,
} from '@/modules/profile/services/bankDetail.service'
import type { Bank } from '@/modules/profile/types/bank.types'
import type { BankDetail, BankDetailPayload } from '@/modules/profile/types/bankDetail.types'

defineProps<{
  embedded?: boolean
}>()

const banks = ref<Bank[]>([])
const loadingBanks = ref(false)
const bankDetails = ref<BankDetail[]>([])
const loadingBankDetails = ref(false)
const savingBankDetail = ref(false)
const deletingBankDetailId = ref<string | null>(null)
const editingBankDetailId = ref<string | null>(null)

type BankDetailFieldKey = 'bank_id' | 'account_name' | 'account_number' | 'account_type'

const BANK_DETAIL_FIELD_KEYS: BankDetailFieldKey[] = [
  'bank_id',
  'account_name',
  'account_number',
  'account_type',
]

const bankDetailErrors = reactive<Partial<Record<BankDetailFieldKey, string>>>({})

type SelectOption = { label: string; value: string }

const ACCOUNT_TYPE_OPTIONS: SelectOption[] = [
  { label: 'Simpanan', value: 'Saving' },
  { label: 'Semasa', value: 'Current' },
]

function createSelectCollection(options: SelectOption[]) {
  return select.collection({
    items: options,
    itemToValue: (item) => item.label,
  })
}

function labelToApiValue(options: SelectOption[], label: string | undefined): string | null {
  if (!label) return null
  return options.find((option) => option.label === label)?.value ?? null
}

function apiValueToLabel(options: SelectOption[], value: string | null | undefined): string[] {
  if (!value) return []
  const option = options.find((item) => item.value === value)
  return option ? [option.label] : [value]
}

const bankOptions = computed<SelectOption[]>(() =>
  banks.value.map((bank) => ({
    label: `${bank.name} (${bank.code})`,
    value: bank.id,
  })),
)

const bankCollection = computed(() => createSelectCollection(bankOptions.value))
const accountTypeCollection = createSelectCollection(ACCOUNT_TYPE_OPTIONS)

const bankValue = ref<string[]>([])
const bankInitial = ref<string[]>([])
const accountTypeValue = ref<string[]>([])
const accountTypeInitial = ref<string[]>([])

const bankNameById = computed(() =>
  Object.fromEntries(banks.value.map((bank) => [bank.id, `${bank.name} (${bank.code})`])),
)

const accountTypeLabel = computed(() =>
  Object.fromEntries(ACCOUNT_TYPE_OPTIONS.map((option) => [option.value, option.label])),
)

const isEditingBankDetail = computed(() => editingBankDetailId.value !== null)

function emptyBankDetailForm() {
  return {
    bank_id: '',
    account_name: '',
    account_number: '',
    account_type: '',
  }
}

const bankDetailForm = reactive(emptyBankDetailForm())

function clearBankDetailFieldError(field: BankDetailFieldKey) {
  delete bankDetailErrors[field]
}

function clearBankDetailErrors() {
  for (const field of BANK_DETAIL_FIELD_KEYS) {
    delete bankDetailErrors[field]
  }
}

function setBankDetailErrorsFromApi(error: unknown): boolean {
  const apiErrors = getApiValidationErrors(error)
  if (!apiErrors) return false

  for (const [field, messages] of Object.entries(apiErrors)) {
    if (BANK_DETAIL_FIELD_KEYS.includes(field as BankDetailFieldKey) && messages[0]) {
      bankDetailErrors[field as BankDetailFieldKey] = messages[0]
    }
  }

  return Object.keys(bankDetailErrors).length > 0
}

function setBankValue(details: { value: string[] }) {
  bankValue.value = details.value
  clearBankDetailFieldError('bank_id')
  bankDetailForm.bank_id = labelToApiValue(bankOptions.value, details.value[0]) ?? ''
}

function setAccountTypeValue(details: { value: string[] }) {
  accountTypeValue.value = details.value
  clearBankDetailFieldError('account_type')
  bankDetailForm.account_type =
    labelToApiValue(ACCOUNT_TYPE_OPTIONS, details.value[0]) ?? ''
}

function syncBankDetailSelectValues() {
  bankValue.value = apiValueToLabel(bankOptions.value, bankDetailForm.bank_id)
  bankInitial.value = [...bankValue.value]
  accountTypeValue.value = apiValueToLabel(ACCOUNT_TYPE_OPTIONS, bankDetailForm.account_type)
  accountTypeInitial.value = [...accountTypeValue.value]
}

function resetBankDetailForm() {
  Object.assign(bankDetailForm, emptyBankDetailForm())
  editingBankDetailId.value = null
  clearBankDetailErrors()
  syncBankDetailSelectValues()
}

function validateBankDetailForm(): boolean {
  clearBankDetailErrors()

  let valid = true

  if (!bankValue.value[0] || !labelToApiValue(bankOptions.value, bankValue.value[0])) {
    bankDetailErrors.bank_id = 'Bank diperlukan.'
    valid = false
  }

  if (!bankDetailForm.account_name.trim()) {
    bankDetailErrors.account_name = 'Nama akaun diperlukan.'
    valid = false
  }

  if (!bankDetailForm.account_number.trim()) {
    bankDetailErrors.account_number = 'Nombor akaun diperlukan.'
    valid = false
  }

  if (
    !accountTypeValue.value[0] ||
    !labelToApiValue(ACCOUNT_TYPE_OPTIONS, accountTypeValue.value[0])
  ) {
    bankDetailErrors.account_type = 'Jenis akaun diperlukan.'
    valid = false
  }

  return valid
}

function buildBankDetailPayload(): BankDetailPayload {
  return {
    bank_id: labelToApiValue(bankOptions.value, bankValue.value[0]) ?? bankDetailForm.bank_id,
    account_name: bankDetailForm.account_name.trim(),
    account_number: bankDetailForm.account_number.trim(),
    account_type:
      labelToApiValue(ACCOUNT_TYPE_OPTIONS, accountTypeValue.value[0]) ??
      bankDetailForm.account_type,
  }
}

async function fetchActiveBanks() {
  loadingBanks.value = true

  try {
    const res = await listActiveBanks()
    banks.value = res.data
  } catch (error) {
    await Swal.fire({
      icon: 'error',
      title: 'Ralat',
      text: getApiErrorMessage(error, 'Gagal memuatkan senarai bank.'),
    })
  } finally {
    loadingBanks.value = false
  }
}

async function fetchBankDetails() {
  loadingBankDetails.value = true

  try {
    const res = await listBankDetails()
    bankDetails.value = res.data
  } catch (error) {
    await Swal.fire({
      icon: 'error',
      title: 'Ralat',
      text: getApiErrorMessage(error, 'Gagal memuatkan akaun bank.'),
    })
  } finally {
    loadingBankDetails.value = false
  }
}

function startEditBankDetail(bankDetail: BankDetail) {
  clearBankDetailErrors()
  editingBankDetailId.value = bankDetail.id
  bankDetailForm.bank_id = bankDetail.bank_id
  bankDetailForm.account_name = bankDetail.account_name
  bankDetailForm.account_number = bankDetail.account_number
  bankDetailForm.account_type = bankDetail.account_type
  syncBankDetailSelectValues()
}

async function onSaveBankDetail() {
  if (!validateBankDetailForm()) {
    return
  }

  savingBankDetail.value = true
  const wasEditing = isEditingBankDetail.value
  const payload = buildBankDetailPayload()

  try {
    const res = wasEditing
      ? await updateBankDetail(editingBankDetailId.value!, payload)
      : await createBankDetail(payload)

    if (!res.success) {
      throw new Error(res.message ?? 'Gagal menyimpan akaun bank.')
    }

    await fetchBankDetails()
    resetBankDetailForm()

    await Swal.fire({
      toast: true,
      position: 'top-end',
      icon: 'success',
      title: wasEditing ? 'Akaun bank berjaya dikemas kini.' : 'Akaun bank berjaya ditambah.',
      showConfirmButton: false,
      timer: 3000,
    })
  } catch (error) {
    if (!setBankDetailErrorsFromApi(error)) {
      await Swal.fire({
        icon: 'error',
        title: 'Ralat',
        text: getApiErrorMessage(error, 'Gagal menyimpan akaun bank.'),
      })
    }
  } finally {
    savingBankDetail.value = false
  }
}

async function onDeleteBankDetail(bankDetail: BankDetail) {
  const result = await Swal.fire({
    icon: 'warning',
    title: 'Padam akaun bank?',
    text: 'Tindakan ini tidak boleh dibatalkan.',
    showCancelButton: true,
    confirmButtonText: 'Padam',
    cancelButtonText: 'Batal',
  })

  if (!result.isConfirmed) return

  deletingBankDetailId.value = bankDetail.id

  try {
    const res = await deleteBankDetail(bankDetail.id)

    if (!res.success) {
      throw new Error(res.message ?? 'Gagal memadam akaun bank.')
    }

    if (editingBankDetailId.value === bankDetail.id) {
      resetBankDetailForm()
    }

    await fetchBankDetails()

    await Swal.fire({
      toast: true,
      position: 'top-end',
      icon: 'success',
      title: 'Akaun bank berjaya dipadam.',
      showConfirmButton: false,
      timer: 3000,
    })
  } catch (error) {
    await Swal.fire({
      icon: 'error',
      title: 'Ralat',
      text: getApiErrorMessage(error, 'Gagal memadam akaun bank.'),
    })
  } finally {
    deletingBankDetailId.value = null
  }
}

onMounted(async () => {
  await fetchActiveBanks()
  syncBankDetailSelectValues()
  await fetchBankDetails()
})
</script>

<template>
  <div :class="embedded ? '' : 'mt-5'">
    <Box raised="single" class="p-6">
      <div class="space-y-6">
        <div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
          <div>
            <h3 class="text-lg font-semibold text-slate-900">Akaun Bank</h3>
            <p class="mt-1 text-sm text-slate-500">
              Urus maklumat akaun bank anda.
            </p>
          </div>
        </div>

        <div v-if="loadingBankDetails" class="text-sm text-slate-500">
          Memuatkan akaun bank...
        </div>

        <div v-else-if="bankDetails.length" class="space-y-3">
          <div
            v-for="bankDetail in bankDetails"
            :key="bankDetail.id"
            class="flex flex-col gap-4 rounded-lg border border-foreground/10 p-4 sm:flex-row sm:items-start sm:justify-between"
          >
            <div class="min-w-0 flex-1">
              <div class="flex flex-wrap items-center gap-2">
                <span class="font-medium text-slate-900">
                  {{ bankNameById[bankDetail.bank_id] ?? bankDetail.bank_id }}
                </span>
                <Badge look="outline">
                  {{ accountTypeLabel[bankDetail.account_type] ?? bankDetail.account_type }}
                </Badge>
              </div>
              <p class="mt-1 text-sm font-medium text-slate-700">{{ bankDetail.account_name }}</p>
              <p class="mt-1 text-sm text-slate-500">{{ bankDetail.account_number }}</p>
            </div>
            <div class="flex shrink-0 gap-2">
              <Button
                type="button"
                variant="ghost"
                class="border border-foreground/15 shadow-none"
                :disabled="deletingBankDetailId === bankDetail.id"
                @click="startEditBankDetail(bankDetail)"
              >
                <Lucide class="mr-2 size-4" icon="Pencil" />
                Kemaskini
              </Button>
              <Button
                type="button"
                variant="ghost"
                class="border border-foreground/15 shadow-none text-danger"
                :disabled="deletingBankDetailId === bankDetail.id"
                @click="onDeleteBankDetail(bankDetail)"
              >
                <Lucide
                  class="mr-2 size-4"
                  :icon="deletingBankDetailId === bankDetail.id ? 'LoaderCircle' : 'Trash'"
                  :class="{ 'animate-spin': deletingBankDetailId === bankDetail.id }"
                />
                Padam
              </Button>
            </div>
          </div>
        </div>

        <div
          v-else
          class="rounded-lg border border-dashed border-foreground/15 p-6 text-center text-sm text-slate-500"
        >
          Tiada akaun bank direkodkan.
        </div>

        <form class="space-y-6 border-t border-foreground/10 pt-6" @submit.prevent="onSaveBankDetail">
          <div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
            <div>
              <h4 class="text-base font-semibold text-slate-900">
                {{ isEditingBankDetail ? 'Kemaskini Akaun Bank' : 'Tambah Akaun Bank' }}
              </h4>
              <p class="mt-1 text-sm text-slate-500">
                {{
                  isEditingBankDetail
                    ? 'Kemas kini maklumat akaun bank yang dipilih.'
                    : 'Tambah akaun bank baharu ke profil anda.'
                }}
              </p>
            </div>
            <div class="flex gap-2">
              <Button
                v-if="isEditingBankDetail"
                type="button"
                variant="ghost"
                class="border border-foreground/15 shadow-none"
                :disabled="savingBankDetail"
                @click="resetBankDetailForm"
              >
                Batal
              </Button>
              <Button type="submit" variant="primary" :disabled="savingBankDetail || loadingBanks">
                {{ savingBankDetail ? 'Menyimpan...' : isEditingBankDetail ? 'Kemaskini' : 'Tambah' }}
              </Button>
            </div>
          </div>

          <FieldGroup>
            <div class="grid grid-cols-1 gap-4 md:grid-cols-2">
              <Field>
                <FieldLabel>Bank</FieldLabel>
                <SelectRoot
                  :key="`bank-${editingBankDetailId ?? 'new'}-${banks.length}`"
                  class="w-full"
                  :collection="bankCollection"
                  :default-value="bankInitial"
                  :disabled="savingBankDetail || loadingBanks"
                  @value-change="setBankValue"
                >
                  <SelectControl>
                    <SelectTrigger :aria-invalid="!!bankDetailErrors.bank_id">
                      <SelectValueText placeholder="Pilih bank" />
                    </SelectTrigger>
                  </SelectControl>
                  <SelectContent>
                    <SelectItemGroup>
                      <SelectItemGroupLabel>Bank</SelectItemGroupLabel>
                      <SelectItem
                        v-for="item in bankCollection.items"
                        :key="item.label"
                        :item="item"
                      >
                        <SelectItemText>{{ item.label }}</SelectItemText>
                      </SelectItem>
                    </SelectItemGroup>
                  </SelectContent>
                </SelectRoot>
                <FieldError v-if="bankDetailErrors.bank_id">{{ bankDetailErrors.bank_id }}</FieldError>
              </Field>
              <Field>
                <FieldLabel>Jenis Akaun</FieldLabel>
                <SelectRoot
                  :key="`account-type-${editingBankDetailId ?? 'new'}`"
                  class="w-full"
                  :collection="accountTypeCollection"
                  :default-value="accountTypeInitial"
                  :disabled="savingBankDetail"
                  @value-change="setAccountTypeValue"
                >
                  <SelectControl>
                    <SelectTrigger :aria-invalid="!!bankDetailErrors.account_type">
                      <SelectValueText placeholder="Pilih jenis akaun" />
                    </SelectTrigger>
                  </SelectControl>
                  <SelectContent>
                    <SelectItemGroup>
                      <SelectItemGroupLabel>Jenis Akaun</SelectItemGroupLabel>
                      <SelectItem
                        v-for="item in accountTypeCollection.items"
                        :key="item.label"
                        :item="item"
                      >
                        <SelectItemText>{{ item.label }}</SelectItemText>
                      </SelectItem>
                    </SelectItemGroup>
                  </SelectContent>
                </SelectRoot>
                <FieldError v-if="bankDetailErrors.account_type">
                  {{ bankDetailErrors.account_type }}
                </FieldError>
              </Field>
              <Field>
                <FieldLabel for="bank-account-name">Nama Akaun</FieldLabel>
                <Input
                  id="bank-account-name"
                  v-model="bankDetailForm.account_name"
                  type="text"
                  placeholder="Nama pemegang akaun"
                  :aria-invalid="!!bankDetailErrors.account_name"
                  @input="clearBankDetailFieldError('account_name')"
                />
                <FieldError v-if="bankDetailErrors.account_name">
                  {{ bankDetailErrors.account_name }}
                </FieldError>
              </Field>
              <Field>
                <FieldLabel for="bank-account-number">Nombor Akaun</FieldLabel>
                <Input
                  id="bank-account-number"
                  v-model="bankDetailForm.account_number"
                  type="text"
                  placeholder="Nombor akaun"
                  :aria-invalid="!!bankDetailErrors.account_number"
                  @input="clearBankDetailFieldError('account_number')"
                />
                <FieldError v-if="bankDetailErrors.account_number">
                  {{ bankDetailErrors.account_number }}
                </FieldError>
              </Field>
            </div>
          </FieldGroup>
        </form>
      </div>
    </Box>
  </div>
</template>
