<script setup lang="ts">
// type
import { withBase } from '~/models/_base'
import type { Person } from '~/models/person'
import type { PersonAddress } from '~/models/person-address'
import type { PersonContact } from '~/models/person-contact'
import type { PersonRelative } from '~/models/person-relative'
import type { ExposedForm } from '~/types/form'
import type { PatientEntity, PatientBase, Patient, genPatientProps } from '~/models/patient'
import { genPatientEntity } from '~/models/patient'

// components
import Action from '~/components/pub/my-ui/nav-footer/ba-dr-su.vue'
import AppPatientEntryForm from '~/components/app/patient/entry-form.vue'
import AppPersonAddressEntryForm from '~/components/app/person-address/entry-form.vue'
import AppPersonAddressEntryFormRelative from '~/components/app/person-address/entry-form-relative.vue'
import AppPersonFamilyParentsForm from '~/components/app/person/family-parents-form.vue'
import AppPersonContactEntryForm from '~/components/app/person-contact/entry-form.vue'
import AppPersonRelativeEntryForm from '~/components/app/person-relative/entry-form.vue'

// helper
import { format, parseISO } from 'date-fns'
import { id as localeID } from 'date-fns/locale'

// services
import { getPatientDetail, uploadAttachment } from '~/services/patient.service'

import { isReadonly, isProcessing, handleActionSave, handleActionEdit } from '~/handlers/patient.handler'

import { toast } from '~/components/pub/ui/toast'

// reverse mapping untuk contact type (backend → UI)
const reverseContactTypeMapping: Record<string, string> = {
  'm-phone': 'phoneNumber',
  phone: 'homePhoneNumber',
  email: 'email',
  fax: 'fax',
}

// #region Props & Emits
const props = defineProps<{
  callbackUrl?: string
  mode: 'add' | 'edit'
  patientId?: string | number
}>()

const residentIdentityFile = ref<File>()
const familyCardFile = ref<File>()

// form related state
const personAddressForm = ref<ExposedForm<any> | null>(null)
const personAddressRelativeForm = ref<ExposedForm<any> | null>(null)
const personContactForm = ref<ExposedForm<any> | null>(null)
const personEmergencyContactRelative = ref<ExposedForm<any> | null>(null)
const personFamilyForm = ref<ExposedForm<any> | null>(null)
const personPatientForm = ref<ExposedForm<any> | null>(null)

// #endregion

// #region State & Computed
const patientDetail = ref(
  withBase<PatientEntity>({
    person: {} as Person,
    personAddresses: [],
    personContacts: [],
    personRelatives: [],
  }),
)

// Key untuk memaksa re-render form saat data berubah
const formKey = ref(0)

// Computed: unwrap patient data untuk form patient
const patientFormInitialValues = computed(() => {
  const p = patientDetail.value
  const person = p.person
  if (!person || !person.name) return undefined

  const hasDisability = String(person?.disability).length > 0
  const date = parseISO(person.birthDate!)
  const birthDate = format(date, 'yyyy-MM-dd', { locale: localeID })

  return {
    identityNumber: person.residentIdentityNumber || undefined,
    drivingLicenseNumber: person.drivingLicenseNumber || undefined,
    passportNumber: person.passportNumber || undefined,
    fullName: person.name || '',
    isNewBorn: p.newBornStatus ? 'yes' : 'no',
    gender: person.gender_code || '',
    birthPlace: person.birthRegency_code || '',
    birthDate: birthDate,
    education: person.education_code || '',
    job: person.occupation_code || '',
    maritalStatus: person?.maritalStatus_code,
    nationality: person?.nationality || 'WNI',
    ethnicity: person?.ethnic_code || '',
    language: person?.language_code || '',
    religion: person?.religion_code || '',
    communicationBarrier: person?.communicationIssueStatus ? 'yes' : 'no',
    disability: hasDisability ? 'yes' : 'no',
    disabilityType: hasDisability ? String(person?.disability) || '' : '',
    note: '',
  }
})

// Computed: unwrap alamat domisili (alamat sekarang)
const addressFormInitialValues = computed(() => {
  if (!patientDetail.value.person?.addresses) return {}

  const addresses = patientDetail.value.person?.addresses
  const domicileAddress = addresses.find((a: PersonAddress) => a.locationType_code === 'domicile')
  if (!domicileAddress) return undefined

  // extract kode wilayah dari preload data
  const village = domicileAddress.postalRegion?.village
  const district = village?.district
  const regency = district?.regency
  const province = regency?.province

  return {
    id: domicileAddress.id || 0,
    locationType_code: 'domicile',
    province_code: province?.code || '',
    regency_code: regency?.code || '',
    district_code: district?.code || '',
    village_code: village?.code || domicileAddress.village_code || '',
    postalRegion_code: domicileAddress.postalRegion_code || '',
    address: domicileAddress.address || '',
    rt: domicileAddress.rt || '',
    rw: domicileAddress.rw || '',
  }
})

// Computed: unwrap alamat KTP (identity)
const addressRelativeFormInitialValues = computed(() => {
  if (!patientDetail.value.person?.addresses) return {}

  const addresses = patientDetail.value.person?.addresses
  const domicileAddress = addresses.find((a: PersonAddress) => a.locationType_code === 'domicile')!
  const identityAddress = addresses.find((a: PersonAddress) => a.locationType_code === 'identity')!

  // extract kode wilayah dari preload data
  const village = identityAddress.postalRegion?.village
  const district = village?.district
  const regency = district?.regency
  const province = regency?.province

  const isSame = domicileAddress.village_code === identityAddress.village_code ? '1' : '0'

  return {
    isSameAddress: isSame,
    id: identityAddress.id || 0,
    locationType_code: 'identity',
    province_code: province?.code || '',
    regency_code: regency?.code || '',
    district_code: district?.code || '',
    village_code: village?.code || identityAddress.village_code || '',
    postalRegion_code: identityAddress.postalRegion_code || '',
    address: identityAddress.address || '',
    rt: identityAddress.rt || '',
    rw: identityAddress.rw || '',
  }
})

// Computed: unwrap data orang tua
const familyFormInitialValues = computed(() => {
  const relatives = patientDetail.value.person?.relatives || patientDetail.value.personRelatives || []
  const parents = relatives.filter(
    (r: PersonRelative) => !r.responsible && (r.relationship_code === 'mother' || r.relationship_code === 'father'),
  )

  if (parents.length === 0) {
    return {
      _shareFamilyData: 'no' as const,
      families: [],
    }
  }

  return {
    _shareFamilyData: 'yes' as const,
    families: parents.map((parent: PersonRelative) => ({
      id: parent.id || 0,
      relation: parent.relationship_code || '',
      name: parent.name || '',
      occupation_code: parent.occupation_code || '',
      education_code: parent.education_code || '',
    })),
  }
})

// Computed: unwrap kontak pasien
const contactFormInitialValues = computed(() => {
  const contacts = patientDetail.value.person?.contacts || patientDetail.value.personContacts || []
  if (contacts.length === 0) return undefined

  return {
    contacts: contacts.map((contact: PersonContact) => ({
      id: contact.id || 0,
      contactType: reverseContactTypeMapping[contact.type_code] || contact.type_code || '',
      contactNumber: contact.value || '',
    })),
  }
})

// Computed: unwrap penanggung jawab
const responsibleFormInitialValues = computed(() => {
  const relatives = patientDetail.value.person?.relatives || patientDetail.value.personRelatives || []
  const responsibles = relatives.filter((r: PersonRelative) => r.responsible === true)

  if (responsibles.length === 0) return undefined

  return {
    contacts: responsibles.map((r: PersonRelative) => ({
      id: r.id || 0,
      relation: r.relationship_code || '',
      name: r.name || '',
      address: r.address || '',
      phone: r.phoneNumber || '',
    })),
  }
})
// #endregion

// #region Lifecycle Hooks
onMounted(async () => {
  // if edit mode, fetch patient detail
  if (props.mode === 'edit' && props.patientId) {
    await loadInitData(props.patientId)
  }
})
// #endregion

// #region Functions
async function composeFormData(): Promise<PatientEntity> {
  const [patient, address, addressRelative, families, contacts, emergencyContact] = await Promise.all([
    personPatientForm.value?.validate(),
    personAddressForm.value?.validate(),
    personAddressRelativeForm.value?.validate(),
    personFamilyForm.value?.validate(),
    personContactForm.value?.validate(),
    personEmergencyContactRelative.value?.validate(),
  ])

  const results = [patient, address, addressRelative, families, contacts, emergencyContact]
  const allValid = results.every((r) => r?.valid)

  console.log(results)
  // exit, if form errors happend during validation
  // for example: dropdown not selected
  if (!allValid) return Promise.reject('Form validation failed')

  const formDataRequest: genPatientProps = {
    patient: {
      ...patient?.values,

      // casting comp. val to backend well known reflect value
      ethnic: patient?.values.nationality === 'WNI' ? patient?.values.ethnic : null,
      birthDate: parseISO(patient?.values.birthDate || ''),
      isNewBorn: patient?.values.isNewBorn === 'yes',
      communicationBarrier: patient?.values.communicationBarrier === 'yes',
      disability: patient?.values.disability === 'yes' ? patient?.values.disabilityType : '',
    },
    residentAddress: address?.values,
    cardAddress: addressRelative?.values,
    familyData: families?.values,
    contacts: contacts?.values,
    responsible: emergencyContact?.values,
  }

  const formData = genPatientEntity(formDataRequest, patientDetail.value)

  if (patient?.values.residentIdentityFile) {
    residentIdentityFile.value = patient?.values.residentIdentityFile
  }

  if (patient?.values.familyIdentityFile) {
    familyCardFile.value = patient?.values.familyIdentityFile
  }

  return new Promise((resolve) => resolve(formData))
}
// #endregion region

// #region Utilities & event handlers
async function loadInitData(id: number | string) {
  isProcessing.value = true
  try {
    const response = await getPatientDetail(id as number)
    if (response.success) {
      patientDetail.value = response.body.data || {}
      // Increment key untuk memaksa re-render form dengan data baru
      formKey.value++
    }

    isProcessing.value = false
  } catch (error) {
    console.error(error)
    toast({
      title: 'Error',
      description: 'Terjadi kesalahan saat mengambil data',
      variant: 'destructive',
    })
  }
}

async function handleActionClick(eventType: string) {
  try {
    if (eventType === 'submit') {
      const patient: Patient = await composeFormData()

      let createdPatientId = 0
      let response: any
      // return
      if (props.mode === 'edit' && props.patientId) {
        response = await handleActionEdit(
          patientDetail.value.id,
          patient,
          () => {},
          () => {},
          toast,
        )
      } else {
        response = await handleActionSave(
          patient,
          () => {},
          () => {},
          toast,
        )
      }

      const data = (response?.body?.data ?? null) as PatientBase | null
      if (!data) return
      createdPatientId = data.id

      if (residentIdentityFile.value) {
        void uploadAttachment(residentIdentityFile.value, createdPatientId, 'ktp')
      }
      if (familyCardFile.value) {
        void uploadAttachment(familyCardFile.value, createdPatientId, 'kk')
      }

      // If has callback provided redirect to callback with patientData
      if (props.callbackUrl && props.callbackUrl.length > 0) {
        await navigateTo(props.callbackUrl + '?patient-id=' + createdPatientId)
        return
      }

      // Navigate to patient list or show success message
      await navigateTo('/client/patient')
      return
    }

    if (eventType === 'cancel') {
      if (props.callbackUrl) {
        await navigateTo(props.callbackUrl)
        return
      }

      // handleCancelForm()
    }
    if (eventType === 'back') {
      await navigateTo({
        name: 'client-patient',
      })
    }
  } catch (error) {
    // Show error toast to user
    if (typeof error === 'string') {
      toast({
        title: 'Error',
        description: error,
        variant: 'destructive',
      })
    } else if (error instanceof Error) {
      toast({
        title: 'Error',
        description: error.message || 'Terjadi kesalahan saat menyimpan data',
        variant: 'destructive',
      })
    } else {
      toast({
        title: 'Error',
        description: 'Terjadi kesalahan saat menyimpan data',
        variant: 'destructive',
      })
    }
  }
}
// #endregion

// #region Watchers
// Helper: Cek apakah isSameAddress aktif
const isSameAddressActive = () => {
  const val = personAddressRelativeForm.value?.values?.isSameAddress
  return val === true || val === '1'
}

// Helper: Sinkronkan alamat sekarang ke alamat KTP
const syncAddressToRelative = () => {
  const source = personAddressForm.value?.values
  const target = personAddressRelativeForm.value

  if (!source || !target) return

  const addressFields = [
    'province_code',
    'regency_code',
    'district_code',
    'village_code',
    'postalRegion_code',
    'address',
    'rt',
    'rw',
  ] as const
  const syncedValues = Object.fromEntries(addressFields.map((key) => [key, source[key] || undefined]))

  target.setValues({ ...target.values, ...syncedValues }, false)
}

// Watcher: Sinkronisasi saat nilai alamat berubah atau isSameAddress diaktifkan
watch(
  [() => personAddressForm.value?.values, () => personAddressRelativeForm.value?.values?.isSameAddress],
  () => {
    if (isSameAddressActive()) {
      syncAddressToRelative()
    }
  },
  { deep: true, immediate: true },
)
// #endregion
</script>

<template>
  <div class="mb-5 border-b border-b-slate-300 pb-3 text-lg font-semibold xl:text-xl">
    {{ mode === 'edit' ? 'Edit Pasien' : 'Tambah Pasien' }}
  </div>
  <AppPatientEntryForm
    :key="`patient-${formKey}`"
    ref="personPatientForm"
    :is-readonly="isProcessing || isReadonly"
    :initial-values="patientFormInitialValues"
  />
  <div class="h-6"></div>
  <AppPersonAddressEntryForm
    :key="`address-${formKey}`"
    ref="personAddressForm"
    title="Alamat Sekarang"
    :is-readonly="isProcessing || isReadonly"
    :initial-values="addressFormInitialValues"
  />
  <div class="h-6"></div>
  <AppPersonAddressEntryFormRelative
    :key="`address-rel-${formKey}`"
    ref="personAddressRelativeForm"
    title="Alamat KTP"
    :is-readonly="isProcessing || isReadonly"
    :initial-values="addressRelativeFormInitialValues"
  />
  <div class="h-6"></div>
  <AppPersonFamilyParentsForm
    :key="`family-${formKey}`"
    ref="personFamilyForm"
    title="Identitas Orang Tua"
    :is-readonly="isProcessing || isReadonly"
    :initial-values="familyFormInitialValues"
  />
  <div class="h-6"></div>
  <AppPersonContactEntryForm
    :key="`contact-${formKey}`"
    ref="personContactForm"
    title="Kontak Pasien"
    :is-readonly="isProcessing || isReadonly"
    :initial-values="contactFormInitialValues"
  />
  <AppPersonRelativeEntryForm
    :key="`responsible-${formKey}`"
    ref="personEmergencyContactRelative"
    title="Penanggung Jawab"
    :is-readonly="isProcessing || isReadonly"
    :initial-values="responsibleFormInitialValues"
  />

  <div class="my-2 flex justify-end py-2">
    <Action @click="handleActionClick" />
  </div>
</template>

<style scoped>
/* component style */
</style>
