<template>
  <div class="h-full min-h-0 flex flex-col">
    <AppHeader title="FHIR Explorer">
      <template #actions>
        <button type="button" class="btn-secondary text-sm" @click="openMetadata">
          CapabilityStatement
        </button>
      </template>
    </AppHeader>

    <div class="flex-1 overflow-y-auto p-4 sm:p-6 lg:p-8">
      <div class="max-w-7xl mx-auto space-y-6">
        <div>
          <h1 class="text-[1.75rem] font-semibold text-ink-strong leading-tight tracking-tight">
            FHIR R4 Explorer
          </h1>
          <p class="mt-1 text-sm text-ink-secondary">
            Read-only inspection of exposed FHIR resources for administrators and integration staff.
          </p>
        </div>

        <!-- Resource browser -->
        <section class="card space-y-5">
          <header>
            <h2 class="text-base font-semibold text-ink-strong">Resource Browser</h2>
            <p class="text-sm text-ink-secondary mt-1">
              Search Patient, Encounter, or Observation, then inspect raw JSON.
            </p>
          </header>

          <div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
            <div>
              <label for="fhir-resource-type" class="block text-sm font-semibold text-ink mb-2">
                Resource Type
              </label>
              <select
                id="fhir-resource-type"
                v-model="resourceType"
                class="form-input"
                @change="clearResults"
              >
                <option value="Patient">Patient</option>
                <option value="Encounter">Encounter</option>
                <option value="Observation">Observation</option>
              </select>
            </div>

            <template v-if="resourceType === 'Patient'">
              <div>
                <label class="block text-sm font-semibold text-ink mb-2">Name</label>
                <input
                  v-model="patientSearch.name"
                  type="text"
                  class="form-input"
                  placeholder="e.g. Santos"
                />
              </div>
              <div>
                <label class="block text-sm font-semibold text-ink mb-2">Birth Date</label>
                <input v-model="patientSearch.birthdate" type="date" class="form-input" />
              </div>
              <div>
                <label class="block text-sm font-semibold text-ink mb-2">MRN (identifier)</label>
                <input
                  v-model="patientSearch.identifier"
                  type="text"
                  class="form-input font-mono"
                  placeholder="VCR-000001"
                />
              </div>
            </template>

            <template v-else-if="resourceType === 'Encounter'">
              <div>
                <label class="block text-sm font-semibold text-ink mb-2">Patient ID</label>
                <input
                  v-model="encounterSearch.patient"
                  type="text"
                  class="form-input font-mono"
                />
              </div>
              <div>
                <label class="block text-sm font-semibold text-ink mb-2">Status</label>
                <select v-model="encounterSearch.status" class="form-input">
                  <option value="">Any</option>
                  <option value="in-progress">in-progress</option>
                  <option value="finished">finished</option>
                </select>
              </div>
              <div>
                <label class="block text-sm font-semibold text-ink mb-2">Date</label>
                <input v-model="encounterSearch.date" type="date" class="form-input" />
              </div>
            </template>

            <template v-else>
              <div>
                <label class="block text-sm font-semibold text-ink mb-2">Patient ID</label>
                <input
                  v-model="observationSearch.patient"
                  type="text"
                  class="form-input font-mono"
                />
              </div>
              <div>
                <label class="block text-sm font-semibold text-ink mb-2">LOINC Code</label>
                <input
                  v-model="observationSearch.code"
                  type="text"
                  class="form-input font-mono"
                  placeholder="8867-4"
                />
              </div>
              <div>
                <label class="block text-sm font-semibold text-ink mb-2">Category</label>
                <select v-model="observationSearch.category" class="form-input">
                  <option value="">Any</option>
                  <option value="vital-signs">vital-signs</option>
                  <option value="laboratory">laboratory</option>
                </select>
              </div>
              <div>
                <label class="block text-sm font-semibold text-ink mb-2">Date (FHIR prefix)</label>
                <input
                  v-model="observationSearch.date"
                  type="text"
                  class="form-input font-mono"
                  placeholder="ge2026-06-20"
                />
              </div>
            </template>
          </div>

          <button
            type="button"
            class="btn-primary"
            :disabled="searching"
            @click="runSearch"
          >
            {{ searching ? 'Searching...' : 'Search' }}
          </button>

          <InlineError
            v-if="searchError"
            title="FHIR search failed"
            :message="searchError"
            preserved="Your search fields were kept."
            retry-label="Retry search"
            @retry="runSearch"
          />

          <div v-if="searchResults.length > 0" class="overflow-x-auto">
            <p class="text-sm text-ink-secondary mb-2">
              {{ searchTotal }} result(s)
            </p>
            <table class="w-full min-w-[640px] text-sm">
              <thead>
                <tr class="border-b border-line text-left text-ink-secondary">
                  <th class="py-2 px-3 font-medium">ID</th>
                  <th v-if="resourceType === 'Patient'" class="py-2 px-3 font-medium">Name</th>
                  <th v-if="resourceType === 'Patient'" class="py-2 px-3 font-medium">MRN</th>
                  <th v-if="resourceType === 'Patient'" class="py-2 px-3 font-medium">DOB</th>
                  <th v-if="resourceType === 'Encounter'" class="py-2 px-3 font-medium">Status</th>
                  <th v-if="resourceType === 'Encounter'" class="py-2 px-3 font-medium">Patient</th>
                  <th v-if="resourceType === 'Observation'" class="py-2 px-3 font-medium">Code</th>
                  <th v-if="resourceType === 'Observation'" class="py-2 px-3 font-medium">Value</th>
                  <th v-if="resourceType === 'Observation'" class="py-2 px-3 font-medium">Recorded</th>
                </tr>
              </thead>
              <tbody>
                <tr
                  v-for="entry in searchResults"
                  :key="String(entry.resource?.id)"
                  class="border-b border-line hover:bg-primary-50 cursor-pointer"
                  :class="{ 'bg-primary-50': selectedResource?.id === entry.resource?.id }"
                  @click="selectResource(entry.resource)"
                >
                  <td class="py-2 px-3 font-mono text-xs text-ink-strong">
                    {{ entry.resource?.id }}
                  </td>
                  <td v-if="resourceType === 'Patient'" class="py-2 px-3">
                    {{ patientDisplayName(entry.resource) }}
                  </td>
                  <td v-if="resourceType === 'Patient'" class="py-2 px-3 font-mono text-xs">
                    {{ patientMrn(entry.resource) }}
                  </td>
                  <td v-if="resourceType === 'Patient'" class="py-2 px-3">
                    {{ entry.resource?.birthDate ?? '—' }}
                  </td>
                  <td v-if="resourceType === 'Encounter'" class="py-2 px-3">
                    {{ entry.resource?.status ?? '—' }}
                  </td>
                  <td v-if="resourceType === 'Encounter'" class="py-2 px-3 font-mono text-xs">
                    {{ subjectReference(entry.resource) }}
                  </td>
                  <td v-if="resourceType === 'Observation'" class="py-2 px-3">
                    {{ observationCodeDisplay(entry.resource) }}
                  </td>
                  <td v-if="resourceType === 'Observation'" class="py-2 px-3">
                    {{ observationValueDisplay(entry.resource) }}
                  </td>
                  <td v-if="resourceType === 'Observation'" class="py-2 px-3 text-ink-secondary">
                    {{ observationEffective(entry.resource) }}
                  </td>
                </tr>
              </tbody>
            </table>
          </div>

          <EmptyState
            v-else-if="searched && !searching && !searchError"
            title="No matching FHIR resources."
            description="Adjust search parameters and try again."
          />

          <div v-if="selectedResource">
            <h3 class="text-sm font-semibold text-ink-strong mb-2">Resource JSON</h3>
            <pre class="fhir-json-panel">{{ formattedSelectedResource }}</pre>
          </div>
        </section>

        <!-- Patient $everything -->
        <section class="card space-y-4">
          <header>
            <h2 class="text-base font-semibold text-ink-strong">Patient $everything</h2>
            <p class="text-sm text-ink-secondary mt-1">
              Load all FHIR resources for a patient in one Bundle.
            </p>
          </header>

          <div class="max-w-md">
            <label class="block text-sm font-semibold text-ink mb-2">Patient</label>
            <PatientSearch v-model="everythingPatientId" />
          </div>

          <button
            type="button"
            class="btn-primary"
            :disabled="!everythingPatientId || everythingLoading"
            @click="loadEverything"
          >
            {{ everythingLoading ? 'Loading...' : 'Load All Data' }}
          </button>

          <InlineError
            v-if="everythingError"
            title="Failed to load patient Bundle"
            :message="everythingError"
            preserved="Your patient selection was kept."
            retry-label="Retry"
            @retry="loadEverything"
          />

          <div v-if="everythingBundle" class="space-y-5 pt-2">
            <div
              v-if="everythingPatient"
              class="rounded-input border border-primary-100 bg-primary-50 p-4"
            >
              <h3 class="text-sm font-semibold text-ink-strong mb-2">Patient</h3>
              <dl class="grid grid-cols-1 sm:grid-cols-2 gap-2 text-sm">
                <div>
                  <dt class="text-ink-secondary">Name</dt>
                  <dd class="text-ink-strong">{{ patientDisplayName(everythingPatient) }}</dd>
                </div>
                <div>
                  <dt class="text-ink-secondary">MRN</dt>
                  <dd class="font-mono text-ink-strong">{{ patientMrn(everythingPatient) }}</dd>
                </div>
                <div>
                  <dt class="text-ink-secondary">DOB</dt>
                  <dd class="text-ink">{{ everythingPatient.birthDate ?? '—' }}</dd>
                </div>
                <div>
                  <dt class="text-ink-secondary">Gender</dt>
                  <dd class="text-ink">{{ everythingPatient.gender ?? '—' }}</dd>
                </div>
              </dl>
            </div>

            <div v-if="everythingEncounters.length > 0">
              <h3 class="text-sm font-semibold text-ink-strong mb-2">
                Encounters ({{ everythingEncounters.length }})
              </h3>
              <ul class="space-y-2">
                <li
                  v-for="enc in everythingEncounters"
                  :key="String(enc.id)"
                  class="text-sm border border-line rounded-input px-3 py-2 bg-surface"
                >
                  <span class="font-mono text-xs text-ink-secondary">{{ enc.id }}</span>
                  — {{ enc.status }}
                  <span v-if="enc.period?.start" class="text-ink-secondary">
                    · {{ enc.period.start }}
                  </span>
                </li>
              </ul>
            </div>

            <div v-if="everythingObservations.length > 0">
              <h3 class="text-sm font-semibold text-ink-strong mb-2">
                Observations ({{ everythingObservations.length }})
              </h3>
              <div class="overflow-x-auto">
                <table class="w-full min-w-[480px] text-sm">
                  <thead>
                    <tr class="border-b border-line text-left text-ink-secondary">
                      <th class="py-2 px-2 font-medium">ID</th>
                      <th class="py-2 px-2 font-medium">Code</th>
                      <th class="py-2 px-2 font-medium">Value</th>
                      <th class="py-2 px-2 font-medium">Recorded</th>
                    </tr>
                  </thead>
                  <tbody>
                    <tr
                      v-for="obs in everythingObservations"
                      :key="String(obs.id)"
                      class="border-b border-line"
                    >
                      <td class="py-2 px-2 font-mono text-xs text-ink-secondary">{{ obs.id }}</td>
                      <td class="py-2 px-2">{{ observationCodeDisplay(obs) }}</td>
                      <td class="py-2 px-2">{{ observationValueDisplay(obs) }}</td>
                      <td class="py-2 px-2 text-ink-secondary">
                        {{ observationEffective(obs) }}
                      </td>
                    </tr>
                  </tbody>
                </table>
              </div>
            </div>
          </div>
        </section>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, reactive, ref } from 'vue'
import AppHeader from '../components/AppHeader.vue'
import PatientSearch from '../components/PatientSearch.vue'
import InlineError from '../components/InlineError.vue'
import EmptyState from '../components/EmptyState.vue'
import {
  fhirGet,
  openFhirJsonInNewTab,
  type FhirBundle,
  type FhirBundleEntry,
  type FhirResource,
} from '../api/fhirClient'

type ResourceType = 'Patient' | 'Encounter' | 'Observation'

const resourceType = ref<ResourceType>('Patient')
const searching = ref(false)
const searched = ref(false)
const searchError = ref('')
const searchResults = ref<FhirBundleEntry[]>([])
const searchTotal = ref(0)
const selectedResource = ref<FhirResource | null>(null)

const patientSearch = reactive({ name: '', birthdate: '', identifier: '' })
const encounterSearch = reactive({ patient: '', status: '', date: '' })
const observationSearch = reactive({ patient: '', code: '', category: '', date: '' })

const everythingPatientId = ref<string | undefined>()
const everythingLoading = ref(false)
const everythingError = ref('')
const everythingBundle = ref<FhirBundle | null>(null)

const formattedSelectedResource = computed(() =>
  selectedResource.value ? JSON.stringify(selectedResource.value, null, 2) : ''
)

const everythingPatient = computed(() =>
  everythingBundle.value?.entry
    ?.map((e) => e.resource)
    .find((r) => r?.resourceType === 'Patient') as FhirPatientResource | undefined
)

const everythingEncounters = computed(() =>
  (everythingBundle.value?.entry ?? [])
    .map((e) => e.resource)
    .filter((r): r is FhirEncounterResource => r?.resourceType === 'Encounter')
)

const everythingObservations = computed(() =>
  (everythingBundle.value?.entry ?? [])
    .map((e) => e.resource)
    .filter((r): r is FhirObservationResource => r?.resourceType === 'Observation')
)

type FhirPatientResource = FhirResource & {
  name?: { text?: string; family?: string }[]
  identifier?: { value?: string }[]
  birthDate?: string
  gender?: string
}

type FhirEncounterResource = FhirResource & {
  status?: string
  subject?: { reference?: string }
  period?: { start?: string }
}

type FhirObservationResource = FhirResource & {
  code?: { coding?: { code?: string; display?: string }[]; text?: string }
  valueQuantity?: { value?: number; unit?: string }
  effectiveDateTime?: string
  effective?: string
}

function clearResults(): void {
  searchResults.value = []
  searchTotal.value = 0
  selectedResource.value = null
  searchError.value = ''
  searched.value = false
}

function selectResource(resource: FhirResource | undefined): void {
  selectedResource.value = resource ?? null
}

function patientDisplayName(resource: FhirResource | undefined): string {
  if (!resource) return '—'
  const patient = resource as FhirPatientResource
  const name = patient.name?.[0]
  return name?.text ?? name?.family ?? '—'
}

function patientMrn(resource: FhirResource | undefined): string {
  if (!resource) return '—'
  const patient = resource as FhirPatientResource
  return patient.identifier?.[0]?.value ?? '—'
}

function subjectReference(resource: FhirResource | undefined): string {
  if (!resource) return '—'
  return (resource as FhirEncounterResource).subject?.reference ?? '—'
}

function observationCodeDisplay(resource: FhirResource | undefined): string {
  if (!resource) return '—'
  const obs = resource as FhirObservationResource
  const coding = obs.code?.coding?.[0]
  return coding?.display ?? coding?.code ?? obs.code?.text ?? '—'
}

function observationValueDisplay(resource: FhirResource | undefined): string {
  if (!resource) return '—'
  const qty = (resource as FhirObservationResource).valueQuantity
  if (!qty) return '—'
  return `${qty.value ?? '—'} ${qty.unit ?? ''}`.trim()
}

function observationEffective(resource: FhirResource | undefined): string {
  if (!resource) return '—'
  const obs = resource as FhirObservationResource
  return obs.effectiveDateTime ?? obs.effective ?? '—'
}

function buildSearchParams(): Record<string, string | number | undefined> {
  if (resourceType.value === 'Patient') {
    return {
      name: patientSearch.name || undefined,
      birthdate: patientSearch.birthdate || undefined,
      identifier: patientSearch.identifier || undefined,
      _count: 20,
    }
  }

  if (resourceType.value === 'Encounter') {
    return {
      patient: encounterSearch.patient || undefined,
      status: encounterSearch.status || undefined,
      date: encounterSearch.date || undefined,
      _count: 20,
    }
  }

  return {
    patient: observationSearch.patient || undefined,
    code: observationSearch.code || undefined,
    category: observationSearch.category || undefined,
    date: observationSearch.date || undefined,
    _count: 50,
  }
}

async function runSearch(): Promise<void> {
  searching.value = true
  searched.value = true
  searchError.value = ''
  selectedResource.value = null

  try {
    const bundle = await fhirGet<FhirBundle>(`/${resourceType.value}`, buildSearchParams())
    searchResults.value = bundle.entry ?? []
    searchTotal.value = bundle.total ?? searchResults.value.length
  } catch (err: unknown) {
    searchResults.value = []
    searchTotal.value = 0
    searchError.value = err instanceof Error ? err.message : 'FHIR search failed'
  } finally {
    searching.value = false
  }
}

async function loadEverything(): Promise<void> {
  if (!everythingPatientId.value) return

  everythingLoading.value = true
  everythingError.value = ''
  everythingBundle.value = null

  try {
    everythingBundle.value = await fhirGet<FhirBundle>(
      `/Patient/${everythingPatientId.value}/$everything`
    )
  } catch (err: unknown) {
    everythingError.value = err instanceof Error ? err.message : 'Failed to load patient Bundle'
  } finally {
    everythingLoading.value = false
  }
}

async function openMetadata(): Promise<void> {
  try {
    const metadata = await fhirGet('/metadata')
    openFhirJsonInNewTab(metadata, 'capability-statement.json')
  } catch (err: unknown) {
    searchError.value = err instanceof Error ? err.message : 'Failed to load metadata'
  }
}
</script>
