<script setup lang="ts">
// Components
import { toast } from '~/components/pub/ui/toast'
import { Button } from '~/components/pub/ui/button'
import AppEncounterEntryForm from '~/components/app/encounter/entry-form.vue'
import AppViewPatient from '~/components/app/patient/view-patient.vue'

// Types
import type { DataTableLoader } from '~/components/pub/my-ui/data-table/type'
import type { TreeItem } from '~/components/pub/my-ui/select-tree/type'

// Constants
import { paymentTypes, sepRefTypeCodes, participantGroups } from '~/lib/constants.vclaim'

// Services
import {
  getList as getSpecialistList,
  getValueTreeItems as getSpecialistTreeItems,
} from '~/services/specialist.service'
import { getValueLabelList as getDoctorValueLabelList } from '~/services/doctor.service'
import { create as createEncounter, getDetail as getEncounterDetail, update as updateEncounter } from '~/services/encounter.service'
import { getList as getSepList } from '~/services/vclaim-sep.service'

// Helpers
import { refDebounced } from '@vueuse/core'

// Handlers
import {
  patients,
  selectedPatient,
  selectedPatientObject,
  paginationMeta,
  getPatientsList,
  getPatientCurrent,
  getPatientByIdentifierSearch,
} from '~/handlers/patient.handler'

// Stores
import { useUserStore } from '~/stores/user'

const props = defineProps<{
  id: number
  classCode?: 'ambulatory' | 'emergency' | 'inpatient' | 'outpatient'
  subClassCode?: 'reg' | 'rehab' | 'chemo' | 'emg' | 'eon' | 'op' | 'icu' | 'hcu' | 'vk'
  formType: string
}>()

const route = useRoute()
const userStore = useUserStore()
const openPatient = ref(false)
const isLoading = reactive<DataTableLoader>({
  isTableLoading: false,
})
const paymentsList = ref<Array<{ value: string; label: string }>>([])
const sepsList = ref<Array<{ value: string; label: string }>>([])
const participantGroupsList = ref<Array<{ value: string; label: string }>>([])
const specialistsTree = ref<TreeItem[]>([])
const specialistsData = ref<any[]>([]) // Store full specialist data with id
const doctorsList = ref<Array<{ value: string; label: string }>>([])
const recSelectId = ref<number | null>(null)
const isSaving = ref(false)
const isLoadingDetail = ref(false)
const formRef = ref<InstanceType<typeof AppEncounterEntryForm> | null>(null)
const encounterData = ref<any>(null)
const formObjects = ref<any>({})

// SEP validation state
const isSepValid = ref(false)
const isCheckingSep = ref(false)
const sepNumber = ref('')
const debouncedSepNumber = refDebounced(sepNumber, 500)

// Computed for edit mode
const isEditMode = computed(() => props.id > 0)

// Computed for save button disable state
const isSaveDisabled = computed(() => {
  return !selectedPatient.value || !selectedPatientObject.value || isSaving.value || isLoadingDetail.value
})

function getListPath(): string {
  if (props.classCode === 'ambulatory' && props.subClassCode === 'rehab') {
    return '/rehab/encounter'
  }
  if (props.classCode === 'ambulatory' && props.subClassCode === 'reg') {
    return '/outpatient/encounter'
  }
  if (props.classCode === 'emergency') {
    return '/emergency/encounter'
  }
  if (props.classCode === 'inpatient') {
    return '/inpatient/encounter'
  }
  return '/encounter' // fallback
}

function handleSavePatient() {
  selectedPatientObject.value = null
  setTimeout(() => {
    getPatientCurrent(selectedPatient.value)
  }, 150)
}

function toKebabCase(str: string): string {
  return str.replace(/([A-Z])/g, '-$1').toLowerCase()
}

function toNavigateSep(values: any) {
  const queryParams = new URLSearchParams()
  if (values['subSpecialistCode']) {
    const isSub = isSubspecialist(values['subSpecialistCode'], specialistsTree.value)
    if (!isSub) {
      values['specialistCode'] = values['subSpecialistCode']
      delete values['subSpecialistCode']
    }
  }

  Object.keys(values).forEach((field) => {
    if (values[field]) {
      queryParams.append(toKebabCase(field), values[field])
    }
  })

  navigateTo('/integration/bpjs/sep/add' + `?${queryParams.toString()}`)
}

async function handleSaveEncounter(formValues: any) {
  // Validate patient is selected
  if (!selectedPatient.value || !selectedPatientObject.value) {
    toast({
      title: 'Gagal',
      description: 'Pasien harus dipilih terlebih dahulu',
      variant: 'destructive',
    })
    return
  }

  try {
    isSaving.value = true

    // Get employee_id from user store
    const employeeId = userStore.user?.employee_id || userStore.user?.employee?.id || 0

    // Format date to ISO format
    const formatDate = (dateString: string): string => {
      if (!dateString) return ''
      const date = new Date(dateString)
      return date.toISOString()
    }

    // Get specialist_id and subspecialist_id from TreeSelect value (code)
    const { specialist_id, subspecialist_id } = getSpecialistIdsFromCode(formValues.subSpecialistId || '')

    // Build payload
    const payload: any = {
      patient_id: selectedPatientObject.value?.id || Number(selectedPatient.value),
      registeredAt: formatDate(formValues.registerDate),
      visitDate: formatDate(formValues.registerDate),
      class_code: props.classCode || '',
      subClass_code: props.subClassCode || '',
      infra_id: null,
      unit_id: null,
      appointment_doctor_id: Number(formValues.doctorId),
      responsible_doctor_id: Number(formValues.doctorId),
      paymentType: formValues.paymentType,
      cardNumber: formValues.cardNumber,
      refSource_name: '',
      appointment_id: null,
    }

    if (employeeId && employeeId > 0) {
      payload.adm_employee_id = employeeId
    }

    // Add specialist_id and subspecialist_id if available
    if (specialist_id) {
      payload.specialist_id = specialist_id
    }
    if (subspecialist_id) {
      payload.subspecialist_id = subspecialist_id
    }

    let paymentMethod = 'cash'
    if (formValues.paymentType === 'jkn' || formValues.paymentType === 'jkmm') {
      paymentMethod = 'insurance'
    } else if (formValues.paymentType === 'spm') {
      paymentMethod = 'cash'
    } else if (formValues.paymentType === 'pks') {
      paymentMethod = 'membership'
    }

    if (paymentMethod === 'insurance') {
      payload.paymentMethod_code = paymentMethod
      payload.insuranceCompany_id = null
      payload.member_number = formValues.cardNumber
      payload.ref_number = formValues.sepNumber
    }

    // Add visitMode_code and allocatedVisitCount only if classCode is ambulatory
    if (props.classCode === 'ambulatory') {
      payload.visitMode_code = 'adm'
      payload.allocatedVisitCount = 0
    }

    // Call encounter service - use update if edit mode, create otherwise
    let result
    if (isEditMode.value) {
      result = await updateEncounter(props.id, payload)
    } else {
      result = await createEncounter(payload)
    }

    if (result.success) {
      toast({
        title: 'Berhasil',
        description: isEditMode.value ? 'Kunjungan berhasil diperbarui' : 'Kunjungan berhasil dibuat',
        variant: 'default',
      })
      // Redirect to list page
      await navigateTo(getListPath())
    } else {
      const errorMessage = result.body?.message || (isEditMode.value ? 'Gagal memperbarui kunjungan' : 'Gagal membuat kunjungan')
      toast({
        title: 'Gagal',
        description: errorMessage,
        variant: 'destructive',
      })
    }
  } catch (error: any) {
    console.error('Error saving encounter:', error)
    toast({
      title: 'Gagal',
      description: error?.message || (isEditMode.value ? 'Gagal memperbarui kunjungan' : 'Gagal membuat kunjungan'),
      variant: 'destructive',
    })
  } finally {
    isSaving.value = false
  }
}

async function handleEvent(menu: string, value?: any) {
  if (menu === 'search') {
    getPatientsList({ 'page-size': 10, includes: 'person' }).then(() => {
      openPatient.value = true
    })
  } else if (menu === 'add') {
    navigateTo('/client/patient/add')
  } else if (menu === 'add-sep') {
    // If SEP is already valid, don't navigate
    if (isSepValid.value) {
      return
    }
    recSelectId.value = null
    toNavigateSep({
      isService: 'false',
      sourcePath: route.path,
      resource: `${props.classCode}-${props.subClassCode}`,
      ...value,
    })
  } else if (menu === 'sep-number-changed') {
    // Update sepNumber when it changes in form (only if different to prevent loop)
    // When SEP number changes from child form, trigger validation/getList immediately
    // so parent can react (this will also be picked up by the debounced watcher)
    await validateSepNumber(String(value || ''))
  } else if (menu === 'save') {
    await handleSaveEncounter(value)
  } else if (menu === 'cancel') {
    navigateTo(getListPath())
  }
}

// Handle save button click
function handleSaveClick() {
  console.log('🔵 handleSaveClick called')
  console.log('🔵 formRef:', formRef.value)
  console.log('🔵 isSaveDisabled:', isSaveDisabled.value)
  console.log('🔵 selectedPatient:', selectedPatient.value)
  console.log('🔵 selectedPatientObject:', selectedPatientObject.value)
  console.log('🔵 isSaving:', isSaving.value)
  console.log('🔵 isLoadingDetail:', isLoadingDetail.value)
  
  if (formRef.value && typeof formRef.value.submitForm === 'function') {
    console.log('🔵 Calling formRef.value.submitForm()')
    formRef.value.submitForm()
  } else {
    console.error('❌ formRef.value is null or submitForm is not a function')
  }
}

/**
 * Validate SEP number
 */
async function validateSepNumber(sepNumberValue: string) {
  // Reset validation if SEP number is empty
  if (!sepNumberValue || sepNumberValue.trim() === '') {
    isSepValid.value = false
    isCheckingSep.value = false
    return
  }

  try {
    isSepValid.value = false
    isCheckingSep.value = true
    const result = await getSepList({ number: sepNumberValue.trim() })
    // Check if SEP is found
    // If response is not null, SEP is found
    // If response is null with metaData code "201", SEP is not found
    if (result.success && result.body?.response !== null) {
      isSepValid.value = result.body?.response?.metaData?.code === '200'
    }
  } catch (error) {
    console.error('Error checking SEP:', error)
    isSepValid.value = false
  } finally {
    isCheckingSep.value = false
  }
}

// Watch debounced SEP number to validate
watch(debouncedSepNumber, async (newValue) => {
  await validateSepNumber(newValue)
})

// Watch payment type to reset SEP validation
watch(
  () => formObjects.value?.paymentType,
  (newValue) => {
    isSepValid.value = false
    // If payment type is not JKN, clear SEP number
    if (newValue !== 'jkn') {
      sepNumber.value = ''
    }
  },
)

async function handleFetchSpecialists() {
  try {
    const specialistsResult = await getSpecialistList({ 'page-size': 100, includes: 'subspecialists' })
    if (specialistsResult.success) {
      const specialists = specialistsResult.body?.data || []
      specialistsData.value = specialists // Store full data for mapping
      specialistsTree.value = getSpecialistTreeItems(specialists)
    }
  } catch (error) {
    console.error('Error fetching specialist-subspecialist tree:', error)
  }
}

/**
 * Helper function to check if a value exists in the specialistsTree
 * Returns true if it's a leaf node (subspecialist), false if parent node (specialist)
 */
function isSubspecialist(value: string, items: TreeItem[]): boolean {
  for (const item of items) {
    if (item.value === value) {
      // If this item has children, it's not selected, so skip
      // If this is the selected item, check if it has children in the tree
      return false // This means it's a specialist, not a subspecialist
    }
    if (item.children) {
      for (const child of item.children) {
        if (child.value === value) {
          // This is a subspecialist (leaf node)
          return true
        }
      }
    }
  }
  return false
}

/**
 * Helper function to get specialist/subspecialist code from ID
 * Returns code string or null if not found
 */
function getSpecialistCodeFromId(id: number | null | undefined): string | null {
  if (!id) return null

  // First check if encounter has specialist object with code
  if (encounterData.value?.specialist?.id === id) {
    return encounterData.value.specialist.code || null
  }

  // Search in specialistsData
  for (const specialist of specialistsData.value) {
    if (specialist.id === id) {
      return specialist.code || null
    }
    // Check subspecialists
    if (specialist.subspecialists && Array.isArray(specialist.subspecialists)) {
      for (const subspecialist of specialist.subspecialists) {
        if (subspecialist.id === id) {
          return subspecialist.code || null
        }
      }
    }
  }

  return null
}

/**
 * Helper function to get subspecialist code from ID
 * Returns code string or null if not found
 */
function getSubspecialistCodeFromId(id: number | null | undefined): string | null {
  if (!id) return null

  // First check if encounter has subspecialist object with code
  if (encounterData.value?.subspecialist?.id === id) {
    return encounterData.value.subspecialist.code || null
  }

  // Search in specialistsData
  for (const specialist of specialistsData.value) {
    if (specialist.subspecialists && Array.isArray(specialist.subspecialists)) {
      for (const subspecialist of specialist.subspecialists) {
        if (subspecialist.id === id) {
          return subspecialist.code || null
        }
      }
    }
  }

  return null
}

/**
 * Helper function to find specialist_id and subspecialist_id from TreeSelect value (code)
 * Returns { specialist_id: number | null, subspecialist_id: number | null }
 */
function getSpecialistIdsFromCode(code: string): { specialist_id: number | null; subspecialist_id: number | null } {
  if (!code) {
    return { specialist_id: null, subspecialist_id: null }
  }

  // Check if it's a subspecialist
  const isSub = isSubspecialist(code, specialistsTree.value)

  if (isSub) {
    // Find subspecialist and its parent specialist
    for (const specialist of specialistsData.value) {
      if (specialist.subspecialists && Array.isArray(specialist.subspecialists)) {
        for (const subspecialist of specialist.subspecialists) {
          if (subspecialist.code === code) {
            return {
              specialist_id: specialist.id ? Number(specialist.id) : null,
              subspecialist_id: subspecialist.id ? Number(subspecialist.id) : null,
            }
          }
        }
      }
    }
  } else {
    // It's a specialist
    for (const specialist of specialistsData.value) {
      if (specialist.code === code) {
        return {
          specialist_id: specialist.id ? Number(specialist.id) : null,
          subspecialist_id: null,
        }
      }
    }
  }

  return { specialist_id: null, subspecialist_id: null }
}

async function handleFetchDoctors(subSpecialistId: string | null = null) {
  try {
    // Build filter based on selection type
    const filterParams: any = { 'page-size': 100, includes: 'employee-Person' }

    if (!subSpecialistId) {
      const doctors = await getDoctorValueLabelList(filterParams)
      doctorsList.value = doctors
      return
    }

    // Check if the selectd value is a subspecialist or specialist
    const isSub = isSubspecialist(subSpecialistId, specialistsTree.value)

    if (isSub) {
      // If selected is subspecialist, filter by subspecialist-id
      filterParams['subspecialist-id'] = subSpecialistId
    } else {
      // If selected is specialist, filter by specialist-id
      filterParams['specialist-id'] = subSpecialistId
    }

    const doctors = await getDoctorValueLabelList(filterParams)
    doctorsList.value = doctors
  } catch (error) {
    console.error('Error fetching doctors:', error)
    doctorsList.value = []
  }
}

function handleFetch(value?: any) {
  if (value?.subSpecialistId) {
    // handleFetchDoctors(value.subSpecialistId)
  }
}

async function handleInit() {
  selectedPatientObject.value = null
  paymentsList.value = Object.keys(paymentTypes).map((item) => ({
    value: item.toString(),
    label: paymentTypes[item],
  })) as any
  sepsList.value = Object.keys(sepRefTypeCodes).map((item) => ({
    value: item.toString(),
    label: sepRefTypeCodes[item],
  })) as any
  participantGroupsList.value = Object.keys(participantGroups).map((item) => ({
    value: item.toString(),
    label: participantGroups[item],
  })) as any
  // Fetch tree data
  await handleFetchDoctors()
  await handleFetchSpecialists()
}

/**
 * Load encounter detail data for edit mode
 */
async function loadEncounterDetail() {
  if (!isEditMode.value || props.id <= 0) {
    return
  }

  try {
    isLoadingDetail.value = true
    // Include patient, person, specialist, and subspecialist in the response
    const result = await getEncounterDetail(props.id, {
      includes: 'patient,patient-person,specialist,subspecialist',
    })
    if (result.success && result.body?.data) {
      encounterData.value = result.body.data
      await mapEncounterToForm(encounterData.value)
      // Set loading to false after mapping is complete
      isLoadingDetail.value = false
    } else {
      toast({
        title: 'Gagal',
        description: 'Gagal memuat data kunjungan',
        variant: 'destructive',
      })
      // Redirect to list page if encounter not found
      await navigateTo(getListPath())
    }
  } catch (error: any) {
    console.error('Error loading encounter detail:', error)
    toast({
      title: 'Gagal',
      description: error?.message || 'Gagal memuat data kunjungan',
      variant: 'destructive',
    })
    // Redirect to list page on error
    await navigateTo(getListPath())
  } finally {
    isLoadingDetail.value = false
  }
}

/**
 * Map encounter data to form fields
 */
async function mapEncounterToForm(encounter: any) {
  if (!encounter) return

  // Set patient data - use data from response if available
  if (encounter.patient) {
    selectedPatient.value = String(encounter.patient.id)
    selectedPatientObject.value = encounter.patient
    // Only fetch patient if person data is missing
    if (!encounter.patient.person) {
      await getPatientCurrent(selectedPatient.value)
    }
  }

  // Map form fields
  const formData: any = {}

  // Patient data (readonly, populated from selected patient)
  // Use encounter.patient.person which is already in the response
  if (encounter.patient?.person) {
    formData.patientName = encounter.patient.person.name || ''
    formData.nationalIdentity = encounter.patient.person.residentIdentityNumber || ''
    formData.medicalRecordNumber = encounter.patient.number || ''
  } else if (selectedPatientObject.value?.person) {
    // Fallback to selectedPatientObject if encounter.patient.person is not available
    formData.patientName = selectedPatientObject.value.person.name || ''
    formData.nationalIdentity = selectedPatientObject.value.person.residentIdentityNumber || ''
    formData.medicalRecordNumber = selectedPatientObject.value.number || ''
  }

  // Doctor ID
  const doctorId = encounter.appointment_doctor_id || encounter.responsible_doctor_id
  if (doctorId) {
    formData.doctorId = String(doctorId)
  }

  // Specialist/Subspecialist - use helper function to get code from ID
  // Priority: subspecialist_id > specialist_id
  if (encounter.subspecialist_id) {
    const subspecialistCode = getSubspecialistCodeFromId(encounter.subspecialist_id)
    if (subspecialistCode) {
      formData.subSpecialistId = subspecialistCode
    }
  } else if (encounter.specialist_id) {
    const specialistCode = getSpecialistCodeFromId(encounter.specialist_id)
    if (specialistCode) {
      formData.subSpecialistId = specialistCode
    }
  }
  
  // Fallback: if encounter has specialist/subspecialist object with code
  if (!formData.subSpecialistId) {
    if (encounter.subspecialist?.code) {
      formData.subSpecialistId = encounter.subspecialist.code
    } else if (encounter.specialist?.code) {
      formData.subSpecialistId = encounter.specialist.code
    }
  }

  // Register date
  if (encounter.registeredAt) {
    // Convert ISO date to local date string (YYYY-MM-DD)
    const date = new Date(encounter.registeredAt)
    formData.registerDate = date.toISOString().split('T')[0]
  } else if (encounter.visitDate) {
    const date = new Date(encounter.visitDate)
    formData.registerDate = date.toISOString().split('T')[0]
  }

  // Payment data - use fields directly from encounter
  // Map paymentMethod_code to paymentType
  if (encounter.paymentMethod_code) {
    // Map paymentMethod_code to paymentType
    // 'insurance' typically means JKN/JKMM
    if (encounter.paymentMethod_code === 'insurance') {
      formData.paymentType = 'jkn' // Default to JKN for insurance
    } else {
      // For other payment methods, use the code directly if it matches
      // Otherwise default to 'spm'
      const validPaymentTypes = ['jkn', 'jkmm', 'spm', 'pks']
      if (validPaymentTypes.includes(encounter.paymentMethod_code)) {
        formData.paymentType = encounter.paymentMethod_code
      } else {
        formData.paymentType = 'spm' // Default to SPM
      }
    }
  } else {
    // If paymentMethod_code is empty or null, default to 'spm'
    formData.paymentType = 'spm'
  }

  // Map payment fields directly from encounter
  formData.cardNumber = encounter.member_number || ''
  formData.sepNumber = encounter.ref_number || ''

  // Note: patientCategory and sepType might not be available in the response
  // These fields might need to be set manually or fetched from other sources

  // Set form objects for the form component
  formObjects.value = formData

  // Update sepNumber for validation
  if (formData.sepNumber) {
    sepNumber.value = formData.sepNumber
  }

  // Fetch doctors based on specialist/subspecialist selection
  if (formData.subSpecialistId) {
    await handleFetchDoctors(formData.subSpecialistId)
  }
}

provide('rec_select_id', recSelectId)
provide('table_data_loader', isLoading)

onMounted(async () => {
  await handleInit()
  // Load encounter detail if in edit mode
  if (isEditMode.value) {
    await loadEncounterDetail()
  }
})
</script>

<template>
  <div class="mb-5 border-b border-b-slate-300 pb-3 text-lg xl:text-xl">
    <Icon
      name="i-lucide-user"
      class="me-2"
    />
    <span class="font-semibold">{{ props.formType }}</span>
    Kunjungan
  </div>

  <AppEncounterEntryForm
    ref="formRef"
    :is-loading="isLoadingDetail"
    :is-sep-valid="isSepValid"
    :is-checking-sep="isCheckingSep"
    :payments="paymentsList"
    :seps="sepsList"
    :participant-groups="participantGroupsList"
    :specialists="specialistsTree"
    :doctor="doctorsList"
    :patient="selectedPatientObject"
    :objects="formObjects"
    @event="handleEvent"
    @fetch="handleFetch"
  />

  <AppViewPatient
    v-model:open="openPatient"
    v-model:selected="selectedPatient"
    :patients="patients"
    :pagination-meta="paginationMeta"
    @fetch="
      (value) => {
        if (value.search && value.search.length >= 3) {
          // Use identifier search for specific searches (NIK, RM, etc.)
          getPatientByIdentifierSearch(value.search)
        } else {
          // Use regular search for general searches
          getPatientsList({ ...value, 'page-size': 10, includes: 'person' })
        }
      }
    "
    @save="handleSavePatient"
  />

  <!-- Footer Actions -->
  <div class="mt-6 flex justify-end gap-2 border-t border-t-slate-300 pt-4">
    <Button
      variant="outline"
      type="button"
      class="h-[40px] min-w-[120px] rounded-md border-orange-400 text-orange-400 hover:bg-green-50 hover:text-orange-400"
      @click="handleEvent('cancel')"
    >
      <Icon
        name="i-lucide-x"
        class="h-5 w-5"
      />
      Batal
    </Button>
    <Button
      type="button"
      class="h-[40px] min-w-[120px] text-white"
      :disabled="isSaveDisabled"
      @click="handleSaveClick"
    >
      <Icon
        name="i-lucide-save"
        class="h-5 w-5"
      />
      Simpan
    </Button>
  </div>
</template>
