import { create } from 'zustand';
import type { Condition, ConditionCategory, EventType, TimelineEvent, Patient, Appointment, Medication, LabResult, VitalSign, HealthMetric, ImagingEvent } from '../types/medical';
import {
  fetchDiagnoses, fetchHospitalVisits, fetchPrescriptions, fetchTests, fetchVaccinations, fetchRadiologyImages,
  createDiagnosis, updateDiagnosis, deleteDiagnosis,
  createHospitalVisit, updateHospitalVisit, deleteHospitalVisit,
  createPrescription, updatePrescription, deletePrescription,
  type APIDiagnosis, type APIHospitalVisit, type APIPrescription, type APITest, type APIVaccination, type APIRadiologyImage
} from '../services/medical';

// ── Body region positions ──
// Every localized bodyRegion from the diseases collection, with vertex-centroid positions on Male.OBJ
// Model raw coords: Y=-5.55 (feet) → Y=1.48 (head top), X=±1.89, Z=-0.48 (back) → 0.81 (front)

type Pos = { x: number; y: number; z: number };
interface BodyRegionEntry { positions: Pos[]; label: string }

export const BODY_REGION_POSITIONS: Record<string, BodyRegionEntry> = {
  // Head
  'brain':           { positions: [{ x:  0.00, y:  1.10, z:  0.28 }], label: 'Brain' },
  'brain/nerves':    { positions: [{ x:  0.00, y:  1.01, z:  0.32 }], label: 'Brain / Nerves' },
  'head':            { positions: [{ x:  0.00, y:  1.19, z:  0.22 }], label: 'Head' },
  'head/neck':       { positions: [{ x:  0.00, y:  0.54, z:  0.20 }], label: 'Head / Neck' },
  // Face
  'eye':             { positions: [{ x:  0.00, y:  0.93, z:  0.46 }], label: 'Eye' },
  'ear':             { positions: [{ x: -0.32, y:  0.89, z:  0.11 }, { x:  0.34, y:  0.93, z:  0.11 }], label: 'Ear' },
  'mouth':           { positions: [{ x:  0.00, y:  0.64, z:  0.51 }], label: 'Mouth' },
  'mouth/teeth':     { positions: [{ x:  0.00, y:  0.64, z:  0.51 }], label: 'Mouth / Teeth' },
  // Neck / Throat
  'neck':            { positions: [{ x:  0.00, y:  0.22, z:  0.05 }], label: 'Neck' },
  'throat':          { positions: [{ x:  0.00, y:  0.24, z:  0.25 }], label: 'Throat' },
  'throat/nose':     { positions: [{ x:  0.00, y:  0.51, z:  0.33 }], label: 'Throat / Nose' },
  'thyroid':         { positions: [{ x:  0.00, y:  0.11, z:  0.34 }], label: 'Thyroid' },
  // Chest
  'heart':           { positions: [{ x:  0.27, y: -0.25, z:  0.53 }], label: 'Heart' },
  'heart/lungs':     { positions: [{ x: -0.15, y: -0.29, z:  0.54 }, { x: -0.29, y: -0.51, z:  0.49 }, { x:  0.31, y: -0.32, z:  0.62 }, { x:  0.40, y: -0.63, z:  0.43 }], label: 'Heart / Lungs' },
  'lungs':           { positions: [{ x:  0.27, y: -0.81, z:  0.49 }], label: 'Lungs' },
  'chest':           { positions: [{ x: -0.43, y: -0.02, z:  0.38 }, { x:  0.45, y: -0.01, z:  0.37 }, { x:  0.57, y: -0.45, z:  0.26 }, { x: -0.52, y: -0.44, z:  0.39 }], label: 'Chest' },
  'breast':          { positions: [{ x:  0.21, y: -0.70, z:  0.52 }], label: 'Breast' },
  // Upper abdomen
  'stomach':         { positions: [{ x: -0.21, y: -0.92, z:  0.56 }, { x:  0.31, y: -0.94, z:  0.50 }, { x:  0.42, y: -1.43, z:  0.44 }, { x: -0.29, y: -1.37, z:  0.51 }], label: 'Stomach' },
  'liver':           { positions: [{ x:  0.23, y: -1.30, z:  0.53 }], label: 'Liver' },
  'gallbladder':     { positions: [{ x:  0.29, y: -1.44, z:  0.51 }], label: 'Gallbladder' },
  'pancreas':        { positions: [{ x:  0.00, y: -1.52, z:  0.58 }], label: 'Pancreas' },
  'digestive':       { positions: [{ x:  0.00, y: -1.53, z:  0.56 }], label: 'Digestive' },
  // Lower abdomen
  'abdomen':         { positions: [{ x: -0.01, y: -1.38, z:  0.60 }], label: 'Abdomen' },
  'intestines':      { positions: [{ x:  0.07, y: -1.12, z:  0.59 }], label: 'Intestines' },
  // Kidneys (posterior)
  'kidneys':         { positions: [{ x: -0.18, y: -1.34, z:  0.57 }, { x:  0.29, y: -1.30, z:  0.63 }], label: 'Kidneys' },
  // Pelvis
  'bladder/urinary': { positions: [{ x: -0.03, y: -2.02, z:  0.56 }], label: 'Bladder / Urinary' },
  'urinary':         { positions: [{ x:  0.06, y: -2.03, z:  0.63 }], label: 'Urinary' },
  'reproductive':    { positions: [{ x: -0.04, y: -1.89, z:  0.57 }], label: 'Reproductive' },
  // Back
  'spine':           { positions: [{ x:  0.00, y: -1.57, z: -0.18 }, { x: -0.02, y: -1.06, z: -0.17 }, { x: -0.01, y: -0.45, z: -0.37 }], label: 'Spine' },
  // Limbs
  'shoulder/arm':    { positions: [{ x:  0.91, y: -0.14, z:  0.17 }, { x: -0.75, y: -0.02, z:  0.21 }, { x: -1.22, y: -1.18, z:  0.12 }, { x:  0.85, y:  0.04, z:  0.15 }, { x:  1.37, y: -1.13, z:  0.20 }], label: 'Shoulder / Arm' },
  'elbow/forearm':   { positions: [{ x: -1.39, y: -1.42, z:  0.24 }, { x:  1.53, y: -1.53, z:  0.26 }], label: 'Elbow / Forearm' },
  'wrist/hand':      { positions: [{ x: -1.67, y: -2.33, z:  0.46 }, { x:  1.75, y: -2.29, z:  0.43 }], label: 'Wrist / Hand' },
  'hip/thigh':       { positions: [{ x:  0.47, y: -2.58, z:  0.51 }, { x: -0.39, y: -2.59, z:  0.53 }], label: 'Hip / Thigh' },
  'knee/leg':        { positions: [{ x: -0.59, y: -4.25, z:  0.27 }, { x:  0.56, y: -4.19, z:  0.29 }], label: 'Knee / Leg' },
  'ankle/foot':      { positions: [{ x: -0.61, y: -5.38, z:  0.56 }, { x:  0.68, y: -5.38, z:  0.52 }], label: 'Ankle / Foot' },
};

// Systemic body regions — no 3D marker, shown in legend only
const SYSTEMIC_REGIONS = new Set([
  'skin', 'skin/nerves', 'bones', 'joints', 'muscles', 'muscles/nerves',
  'tendons', 'musculoskeletal', 'soft tissue', 'nerves',
  'blood', 'blood/lymph', 'blood vessels',
  'endocrine', 'whole body', 'unspecified',
]);

// Map bodyRegion → ConditionCategory
const REGION_TO_CATEGORY: Record<string, ConditionCategory> = {
  'brain': 'neurological', 'brain/nerves': 'neurological', 'head': 'neurological',
  'head/neck': 'neurological', 'nerves': 'neurological', 'muscles/nerves': 'neurological',
  'eye': 'ophthalmologic',
  'ear': 'otological',
  'jaw': 'musculoskeletal', 'mouth': 'gastrointestinal', 'mouth/teeth': 'gastrointestinal',
  'neck': 'musculoskeletal', 'throat': 'respiratory', 'throat/nose': 'respiratory',
  'thyroid': 'endocrine', 'endocrine': 'endocrine', 'pancreas': 'endocrine',
  'heart': 'cardiovascular', 'heart/lungs': 'cardiovascular', 'blood vessels': 'cardiovascular',
  'lungs': 'respiratory', 'chest': 'respiratory',
  'breast': 'reproductive', 'reproductive': 'reproductive',
  'stomach': 'gastrointestinal', 'liver': 'gastrointestinal', 'gallbladder': 'gastrointestinal',
  'abdomen': 'gastrointestinal', 'intestines': 'gastrointestinal', 'digestive': 'gastrointestinal',
  'kidneys': 'urological', 'bladder/urinary': 'urological', 'urinary': 'urological',
  'spine': 'musculoskeletal', 'shoulder/arm': 'musculoskeletal', 'elbow/forearm': 'musculoskeletal',
  'wrist/hand': 'musculoskeletal', 'hip/thigh': 'musculoskeletal', 'knee/leg': 'musculoskeletal',
  'ankle/foot': 'musculoskeletal', 'bones': 'musculoskeletal', 'joints': 'musculoskeletal',
  'muscles': 'musculoskeletal', 'tendons': 'musculoskeletal', 'musculoskeletal': 'musculoskeletal',
  'soft tissue': 'musculoskeletal',
  'skin': 'dermatological', 'skin/nerves': 'dermatological',
  'blood': 'hematological', 'blood/lymph': 'immunological',
  'whole body': 'immunological', 'unspecified': 'musculoskeletal',
};

// ICD chapter → bodyRegion fallback
const ICD_PREFIX_FALLBACK: Record<string, string> = {
  'A': 'whole body', 'B': 'whole body',
  'C': 'whole body', 'D': 'blood',
  'E': 'endocrine', 'F': 'brain', 'G': 'brain/nerves',
  'H': 'eye', 'I': 'heart', 'J': 'lungs', 'K': 'digestive',
  'L': 'skin', 'M': 'musculoskeletal', 'N': 'urinary',
  'O': 'reproductive', 'P': 'whole body', 'Q': 'whole body',
  'R': 'whole body', 'S': 'musculoskeletal', 'T': 'whole body',
};

function resolveBodyRegion(bodyRegion: string | undefined, icdCode: string): string {
  if (bodyRegion && (bodyRegion in BODY_REGION_POSITIONS || SYSTEMIC_REGIONS.has(bodyRegion))) return bodyRegion;
  const prefix = icdCode.split('.')[0];
  const chapter = prefix.charAt(0);
  if (chapter === 'H') {
    const num = parseInt(prefix.slice(1), 10);
    return num >= 60 ? 'ear' : 'eye';
  }
  return ICD_PREFIX_FALLBACK[chapter] || 'unspecified';
}

function lookupBodyRegion(bodyRegion: string | undefined, icdCode: string) {
  const region = resolveBodyRegion(bodyRegion, icdCode);
  const category = REGION_TO_CATEGORY[region] || 'musculoskeletal' as ConditionCategory;
  const regionEntry = BODY_REGION_POSITIONS[region];
  return {
    category,
    position: regionEntry ? regionEntry.positions[0] : null,
    isSystemic: !regionEntry,
  };
}

function mapDiagnosisToCondition(d: APIDiagnosis, index: number): Condition {
  const bodyRegion = d.disease?.bodyRegion;
  const mapping = lookupBodyRegion(bodyRegion, d.icdCode);
  const diagnosisDate = d.diagnosisDate || d.created_at || new Date().toISOString();

  const name = d.disease?.name || d.notes?.split('.')[0] || d.icdCode;
  const description = d.disease?.description || d.notes || d.icdCode;

  return {
    id: index + 1,
    sourceDiagnosisId: d._id,
    code: d.icdCode,
    name,
    category: mapping.category,
    position: mapping.position,
    severity: d.severity ?? 'mild',
    diagnosedDate: diagnosisDate.split('T')[0],
    description,
    notes: d.notes || '',
    bodyRegion: bodyRegion || undefined,
  };
}

function mapHospitalVisitToAppointment(v: APIHospitalVisit): Appointment {
  const admissionDate = v.admissionDate || v.visitDate || v.created_at || new Date().toISOString();
  const [date, timeWithZ] = admissionDate.split('T');
  const time = timeWithZ ? timeWithZ.substring(0, 5) : '00:00';

  // Build diagnosis string from populated diagnoses if available
  const diagnosisStr = v.populatedDiagnoses?.length
    ? v.populatedDiagnoses.map(d => d.diseaseName || d.icdCode).join(', ')
    : (v.diagnoses || []).join(', ');

  // Build treatment from populated prescriptions if available
  const treatmentStr = v.populatedPrescriptions?.length
    ? v.populatedPrescriptions.map(p => p.medicationName || 'Medication').join(', ')
    : 'Hospital Visit';

  return {
    id: v._id || Date.now().toString(),
    date,
    time,
    type: 'appointments',
    doctor: v.doctorName || 'Unknown Doctor',
    specialty: v.department || 'Hospital',
    reason: v.populatedDiagnoses?.[0]?.diseaseName || v.department || 'Medical Visit',
    diagnosis: diagnosisStr,
    treatment: treatmentStr,
    notes: v.dischargeSummary
      || v.populatedDiagnoses?.map(d => d.notes).filter(Boolean).join('; ')
      || 'No details provided.',
    severity: (v.populatedDiagnoses?.[0]?.severity as any) || undefined,
  };
}

function mapPrescriptionToMedication(p: APIPrescription): Medication {
  const prescribedDate = p.prescribedDate || p.created_at || new Date().toISOString();
  const date = prescribedDate.split('T')[0];

  return {
    id: p._id || Date.now().toString(),
    name: p.medication?.genericName || 'Unknown Medication',
    dosage: p.dosage,
    frequency: p.frequency,
    type: 'prescriptions',
    date,
    //prescribedDate: date,
    prescribedBy: p.doctorName || '',
    indication: p.instructions || '',
    sideEffects: '',
    instructions: p.instructions || '',
    refills: 0,
    nextRefill: '',
    startDate: p.startDate || date,
    endDate: p.endDate || new Date(new Date(date).getTime() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
  };
}

function mapAPITestToLabResult(test: APITest): LabResult {
  const dateStr = test.date || new Date().toISOString();
  const [date, timeWithZ] = dateStr.split('T');
  const time = timeWithZ ? timeWithZ.substring(0, 5) : '00:00';

  const getStatus = (value: number, refMin?: number | null, refMax?: number | null) => {
    if (refMin === undefined || refMin === null || refMax === undefined || refMax === null) {
      return 'warning' as const;
    }
    if (value < refMin) return 'warning' as const;
    if (value > refMax) return 'critical' as const;
    return 'normal' as const;
  };

  const results: import('../types/medical').LabTest[] = (test.subtests || []).map((sub, index) => ({
    id: `${test._id || 'test'}-subtest-${index}`,
    name: sub.name,
    value: sub.value ?? 0,
    unit: sub.unit ?? '',
    range: (sub.refMin !== undefined && sub.refMin !== null && sub.refMax !== undefined && sub.refMax !== null)
      ? `${sub.refMin}-${sub.refMax}`
      : 'Unknown',
    status: getStatus(sub.value ?? 0, sub.refMin, sub.refMax)
  }));

  return {
    id: test._id || Date.now().toString(),
    date,
    time,
    type: 'labs',
    orderingPhysician: 'Lab Coordinator',
    testName: test.name || test.testType || 'Medical Test',
    results
  };
}

function mapVaccinationToEvent(v: APIVaccination): import('../types/medical').VaccinationEvent {
  const dateStr = v.administeredDate || v.created_at || new Date().toISOString();
  const [date, timeWithZ] = dateStr.split('T');
  const time = timeWithZ ? timeWithZ.substring(0, 5) : '00:00';

  return {
    id: v._id || Date.now().toString(),
    date,
    time,
    type: 'vaccinations',
    name: v.name || 'Unknown Vaccine',
    administrationPlace: v.administrationPlace || 'Unknown Location',
  };
}

function mapRadiologyImageToEvent(image: APIRadiologyImage): ImagingEvent {
  const dateStr = image.date || image.created_at || new Date().toISOString();
  const [date, timeWithZ] = dateStr.split('T');
  const time = timeWithZ ? timeWithZ.substring(0, 5) : '00:00';

  return {
    id: image._id || Date.now().toString(),
    date,
    time,
    type: 'imaging',
    name: image.explanation || 'Imaging study',
    hospital: image.hospital || 'Unknown Hospital',
    explanation: image.explanation || '',
    imageUrl: image.imageUrl,
  };
}

// --- Test event generators (frontend-only, never persisted to DB) ---
// Used by the TimelinePage dev injector to stress-test layout/lane-packing.
// All generated events use IDs prefixed with "test-" so clearTestEvents can
// strip them precisely and the Timeline can render them with a dashed border.

const TEST_DOCTORS: Array<{ name: string; specialty: string }> = [
  { name: 'Dr. Sarah Mitchell',  specialty: 'Cardiology' },
  { name: 'Dr. James Chen',      specialty: 'Neurology' },
  { name: 'Dr. Elena Rodriguez', specialty: 'Endocrinology' },
  { name: 'Dr. Marcus Thompson', specialty: 'Pulmonology' },
  { name: 'Dr. Aisha Patel',     specialty: 'Dermatology' },
  { name: 'Dr. David Kim',       specialty: 'Orthopedics' },
  { name: 'Dr. Priya Sharma',    specialty: 'Gastroenterology' },
  { name: 'Dr. Robert Williams', specialty: 'Ophthalmology' },
  { name: 'Dr. Linda Garcia',    specialty: 'Rheumatology' },
  { name: 'Dr. Michael Brown',   specialty: 'Internal Medicine' },
];

const TEST_REASONS = [
  'Annual checkup', 'Follow-up consultation', 'Chest pain assessment',
  'Headache evaluation', 'Routine screening', 'Lab results review',
  'Symptom assessment', 'Pre-op consultation', 'Post-op follow-up',
  'Vaccination', 'Blood pressure check', 'Diabetes management',
  'Allergy testing', 'Skin examination', 'Joint pain consultation',
];

const TEST_MEDICATIONS: Array<{ name: string; dosage: string; frequency: string; indication: string }> = [
  { name: 'Metformin',     dosage: '500mg',   frequency: 'Twice daily',       indication: 'Type 2 Diabetes' },
  { name: 'Lisinopril',    dosage: '10mg',    frequency: 'Once daily',        indication: 'Hypertension' },
  { name: 'Atorvastatin',  dosage: '20mg',    frequency: 'Once daily',        indication: 'High cholesterol' },
  { name: 'Levothyroxine', dosage: '50mcg',   frequency: 'Once daily',        indication: 'Hypothyroidism' },
  { name: 'Amlodipine',    dosage: '5mg',     frequency: 'Once daily',        indication: 'Hypertension' },
  { name: 'Sertraline',    dosage: '50mg',    frequency: 'Once daily',        indication: 'Anxiety/Depression' },
  { name: 'Omeprazole',    dosage: '20mg',    frequency: 'Once daily',        indication: 'Acid reflux' },
  { name: 'Albuterol',     dosage: '2 puffs', frequency: 'As needed',         indication: 'Asthma' },
  { name: 'Ibuprofen',     dosage: '400mg',   frequency: 'Every 6 hours',     indication: 'Pain relief' },
  { name: 'Amoxicillin',   dosage: '500mg',   frequency: 'Three times daily', indication: 'Bacterial infection' },
  { name: 'Prednisone',    dosage: '10mg',    frequency: 'Once daily',        indication: 'Inflammation' },
  { name: 'Cetirizine',    dosage: '10mg',    frequency: 'Once daily',        indication: 'Allergies' },
];

const TEST_LAB_NAMES = [
  'Complete Blood Count (CBC)',
  'Comprehensive Metabolic Panel',
  'Lipid Panel',
  'Hemoglobin A1c',
  'Thyroid Function (TSH)',
  'Vitamin D, 25-Hydroxy',
  'Iron Studies',
  'Liver Function Panel',
  'Urinalysis',
  'C-Reactive Protein',
  'Vitamin B12',
  'Basic Metabolic Panel',
];

function pickRandom<T>(arr: T[]): T {
  return arr[Math.floor(Math.random() * arr.length)];
}

function randomInt(min: number, max: number): number {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

// Box-Muller normal sample, clamped to [min, max] in days from today.
// Centered on 0 with the given std so most generated events cluster around
// "now" (matches what real patient data looks like).
//
// IMPORTANT: only safe to clamp when [min, max] is roughly symmetric around 0.
// Asymmetric clamping (e.g. [-180, 0]) would pile ~50% of samples onto the
// boundary — use pastBiasedDays() for past-only distributions instead.
function normalOffsetDays(std: number, min: number, max: number): number {
  const u1 = Math.random() || 1e-9;
  const u2 = Math.random();
  const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
  return Math.max(min, Math.min(max, Math.round(z * std)));
}

// Past-only sample with a recency bias, drawn via rejection sampling from a
// normal distribution. Returns days strictly in [-maxAgo, -1] — never today.
// Avoids the boundary pile-up that simple clamping at 0 would cause: the
// resulting distribution peaks just before today and decays toward -maxAgo,
// matching real patient lab history (recent labs more common than old ones).
function pastBiasedDays(std: number, maxAgo: number): number {
  for (let i = 0; i < 30; i++) {
    const u1 = Math.random() || 1e-9;
    const u2 = Math.random();
    const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
    const days = Math.round(z * std);
    if (days >= -maxAgo && days < 0) return days;
  }
  // Safety fallback if rejection sampling somehow fails to converge.
  return -randomInt(1, maxAgo);
}

// Local-TZ YYYY-MM-DD — never round-trip through UTC for calendar-day values
// (see CLAUDE.md "Local dates, never round-trip through UTC").
function toLocalDateStr(d: Date): string {
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  return `${y}-${m}-${day}`;
}

function dateFromOffset(days: number): Date {
  const d = new Date();
  d.setHours(0, 0, 0, 0);
  d.setDate(d.getDate() + days);
  return d;
}

function randomTime(): string {
  const h = randomInt(8, 17);
  const m = randomInt(0, 59);
  return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
}

function generateTestAppointment(uid: string): Appointment {
  const doc = pickRandom(TEST_DOCTORS);
  const reason = pickRandom(TEST_REASONS);
  const date = toLocalDateStr(dateFromOffset(normalOffsetDays(60, -180, 180)));
  return {
    id: `test-appt-${uid}`,
    date,
    time: randomTime(),
    type: 'appointments',
    doctor: doc.name,
    specialty: doc.specialty,
    reason,
    diagnosis: '',
    treatment: '',
    notes: `Auto-generated test event — ${reason} with ${doc.name}.`,
  };
}

function generateTestMedication(uid: string): Medication {
  const med = pickRandom(TEST_MEDICATIONS);
  const startOffset = normalOffsetDays(60, -180, 180);
  const duration = randomInt(1, 14);
  const startDate = toLocalDateStr(dateFromOffset(startOffset));
  const endDate = toLocalDateStr(dateFromOffset(startOffset + duration));
  const doc = pickRandom(TEST_DOCTORS);
  return {
    id: `test-med-${uid}`,
    name: med.name,
    date: startDate,
    dosage: med.dosage,
    frequency: med.frequency,
    type: 'prescriptions',
    prescribedBy: doc.name,
    indication: med.indication,
    sideEffects: '',
    instructions: `Take as prescribed. ${med.frequency}.`,
    refills: randomInt(0, 5),
    nextRefill: '',
    startDate,
    endDate,
  };
}

function generateTestLabResult(uid: string): LabResult {
  const name = pickRandom(TEST_LAB_NAMES);
  // Labs in the past only — Timeline filters future labs out anyway.
  // Use pastBiasedDays (rejection sampling) so labs spread across the past
  // with a recency bias instead of piling up exactly on today.
  const date = toLocalDateStr(dateFromOffset(pastBiasedDays(60, 180)));
  const doc = pickRandom(TEST_DOCTORS);
  return {
    id: `test-lab-${uid}`,
    date,
    time: randomTime(),
    type: 'labs',
    orderingPhysician: doc.name,
    testName: name,
    results: [],
  };
}

const emptyPatient: Patient = {
  id: '',
  name: '',
  dateOfBirth: '',
  age: 0,
  gender: '',
  bloodType: '',
  height: '',
  weight: '',
};

interface HealthState {
  // Data
  patient: Patient;
  conditions: Condition[];
  appointments: Appointment[];
  medications: Medication[];
  labResults: LabResult[];
  vitals: VitalSign[];
  vaccinations: import('../types/medical').VaccinationEvent[];
  imaging: ImagingEvent[];
  healthTrends: HealthMetric[];

  // Raw API data (cached for pages that need the original shape)
  _loadedUserId: string | null;
  _rawDiagnoses: APIDiagnosis[];
  _rawVisits: APIHospitalVisit[];
  _rawPrescriptions: APIPrescription[];
  _rawTests: APITest[];
  _rawVaccinations: APIVaccination[];
  _rawImages: APIRadiologyImage[];

  // UI State
  currentConditionIndex: number;
  timelineFilter: EventType | 'all';
  detailPanelOpen: boolean;

  // Actions
  setCurrentCondition: (index: number) => void;
  nextCondition: () => void;
  prevCondition: () => void;
  setTimelineFilter: (filter: EventType | 'all') => void;
  openDetailPanel: () => void;
  closeDetailPanel: () => void;
  loadUserData: (userId: string, force?: boolean) => Promise<void>;

  // CRUD Actions
  addDiagnosis: (diagnosis: Partial<APIDiagnosis>) => Promise<void>;
  editDiagnosis: (id: string, updates: Partial<APIDiagnosis>) => Promise<void>;
  removeDiagnosis: (id: string) => Promise<void>;

  addHospitalVisit: (visit: Partial<APIHospitalVisit>) => Promise<void>;
  editHospitalVisit: (id: string, updates: Partial<APIHospitalVisit>) => Promise<void>;
  removeHospitalVisit: (id: string) => Promise<void>;

  addPrescription: (prescription: Partial<APIPrescription>) => Promise<void>;
  editPrescription: (id: string, updates: Partial<APIPrescription>) => Promise<void>;
  removePrescription: (id: string) => Promise<void>;

  // Frontend-only test data — never persisted to the database. IDs are
  // prefixed "test-" so clearTestEvents() can strip them precisely.
  injectTestEvents: (count: number) => void;
  clearTestEvents: () => void;

  // Computed
  getCurrentCondition: () => Condition | undefined;
  getFilteredTimelineEvents: () => TimelineEvent[];
  getRelatedAppointments: (conditionId: number) => Appointment[];
  getRelatedMedications: (conditionId: number) => Medication[];
}

export const useHealthStore = create<HealthState>((set, get) => ({
  // Start empty — no mock data
  patient: emptyPatient,
  conditions: [],
  appointments: [],
  medications: [],
  labResults: [],
  vitals: [],
  vaccinations: [],
  imaging: [],
  healthTrends: [],

  _loadedUserId: null,
  _rawDiagnoses: [],
  _rawVisits: [],
  _rawPrescriptions: [],
  _rawTests: [],
  _rawVaccinations: [],
  _rawImages: [],

  // Initial UI state
  currentConditionIndex: 0,
  timelineFilter: 'all',
  detailPanelOpen: false,

  // Actions
  setCurrentCondition: (index: number) =>
    set({ currentConditionIndex: index }),

  nextCondition: () =>
    set((state) => ({
      currentConditionIndex: state.conditions.length > 0
        ? (state.currentConditionIndex + 1) % state.conditions.length
        : 0,
    })),

  prevCondition: () =>
    set((state) => ({
      currentConditionIndex: state.conditions.length > 0
        ? (state.currentConditionIndex - 1 + state.conditions.length) % state.conditions.length
        : 0,
    })),

  setTimelineFilter: (filter: EventType | 'all') =>
    set({ timelineFilter: filter }),

  openDetailPanel: () =>
    set({ detailPanelOpen: true }),

  closeDetailPanel: () =>
    set({ detailPanelOpen: false }),

  injectTestEvents: (count: number) => {
    // Roughly 40% appointments / 30% medications / 30% labs — exercises
    // lane-packing across all three types.
    const apptCount = Math.floor(count * 0.4);
    const medCount  = Math.floor(count * 0.3);
    const labCount  = count - apptCount - medCount;
    const stamp = Date.now().toString(36);

    const newAppts = Array.from({ length: apptCount }, (_, i) =>
      generateTestAppointment(`${stamp}-${i}`));
    const newMeds = Array.from({ length: medCount }, (_, i) =>
      generateTestMedication(`${stamp}-${i}`));
    const newLabs = Array.from({ length: labCount }, (_, i) =>
      generateTestLabResult(`${stamp}-${i}`));

    set((state) => ({
      appointments: [...state.appointments, ...newAppts],
      medications:  [...state.medications,  ...newMeds],
      labResults:   [...state.labResults,   ...newLabs],
    }));
  },

  clearTestEvents: () => set((state) => ({
    appointments: state.appointments.filter(a => !a.id.startsWith('test-')),
    medications:  state.medications.filter(m => !m.id.startsWith('test-')),
    labResults:   state.labResults.filter(l => !l.id.startsWith('test-')),
  })),

  loadUserData: async (userId: string, force = false) => {
    // Skip if already loaded for this user
    if (!force && get()._loadedUserId === userId && get().conditions.length > 0) return;

    try {
      const [diagnoses, visits, prescriptions, tests, rawVaccinations, rawImages] = await Promise.all([
        fetchDiagnoses(userId),
        fetchHospitalVisits(userId),
        fetchPrescriptions(userId),
        fetchTests(userId),
        fetchVaccinations(userId),
        fetchRadiologyImages(userId)
      ]);

      tests.sort((a, b) => {
        const dateA = a.date ? new Date(a.date).getTime() : 0;
        const dateB = b.date ? new Date(b.date).getTime() : 0;
        return dateB - dateA;
      });

      const conditions = diagnoses.map(mapDiagnosisToCondition);
      const appointments = visits.map(mapHospitalVisitToAppointment);
      const medications = prescriptions.map(mapPrescriptionToMedication);
      const labResults = tests.map(mapAPITestToLabResult);
      const vaccinations = rawVaccinations.map(mapVaccinationToEvent);
      const imaging = rawImages.map(mapRadiologyImageToEvent);

      set({
        _loadedUserId: userId,
        _rawDiagnoses: diagnoses,
        _rawVisits: visits,
        _rawPrescriptions: prescriptions,
        _rawTests: tests,
        _rawVaccinations: rawVaccinations,
        _rawImages: rawImages,
        conditions,
        appointments,
        medications,
        labResults,
        vaccinations,
        imaging,
        currentConditionIndex: 0
      });
    } catch (err) {
      console.error("Failed to load user data:", err);
    }
  },

  // CRUD Implementations
  addDiagnosis: async (diagnosis) => {
    try {
      const created = await createDiagnosis(diagnosis);
      const { _rawDiagnoses } = get();
      const updatedRaw = [..._rawDiagnoses, created];
      set({
        _rawDiagnoses: updatedRaw,
        conditions: updatedRaw.map(mapDiagnosisToCondition)
      });
    } catch (err) {
      console.error("Failed to add diagnosis:", err);
      throw err;
    }
  },

  editDiagnosis: async (id, updates) => {
    try {
      const updated = await updateDiagnosis(id, updates);
      const { _rawDiagnoses } = get();
      const updatedRaw = _rawDiagnoses.map(d => d._id === id ? updated : d);
      set({
        _rawDiagnoses: updatedRaw,
        conditions: updatedRaw.map(mapDiagnosisToCondition)
      });
    } catch (err) {
      console.error("Failed to edit diagnosis:", err);
      throw err;
    }
  },

  removeDiagnosis: async (id) => {
    try {
      await deleteDiagnosis(id);
      const { _rawDiagnoses } = get();
      const updatedRaw = _rawDiagnoses.filter(d => d._id !== id);
      set({
        _rawDiagnoses: updatedRaw,
        conditions: updatedRaw.map(mapDiagnosisToCondition)
      });
    } catch (err) {
      console.error("Failed to remove diagnosis:", err);
      throw err;
    }
  },

  addHospitalVisit: async (visit) => {
    try {
      const created = await createHospitalVisit(visit);
      const { _rawVisits } = get();
      const updatedRaw = [..._rawVisits, created];
      set({
        _rawVisits: updatedRaw,
        appointments: updatedRaw.map(mapHospitalVisitToAppointment)
      });
    } catch (err) {
      console.error("Failed to add hospital visit:", err);
      throw err;
    }
  },

  editHospitalVisit: async (id, updates) => {
    try {
      const updated = await updateHospitalVisit(id, updates);
      const { _rawVisits } = get();
      const updatedRaw = _rawVisits.map(v => v._id === id ? updated : v);
      set({
        _rawVisits: updatedRaw,
        appointments: updatedRaw.map(mapHospitalVisitToAppointment)
      });
    } catch (err) {
      console.error("Failed to edit hospital visit:", err);
      throw err;
    }
  },

  removeHospitalVisit: async (id) => {
    try {
      await deleteHospitalVisit(id);
      const { _rawVisits } = get();
      const updatedRaw = _rawVisits.filter(v => v._id !== id);
      set({
        _rawVisits: updatedRaw,
        appointments: updatedRaw.map(mapHospitalVisitToAppointment)
      });
    } catch (err) {
      console.error("Failed to remove hospital visit:", err);
      throw err;
    }
  },

  addPrescription: async (prescription) => {
    try {
      const created = await createPrescription(prescription);
      const { _rawPrescriptions } = get();
      const updatedRaw = [..._rawPrescriptions, created];
      set({
        _rawPrescriptions: updatedRaw,
        medications: updatedRaw.map(mapPrescriptionToMedication)
      });
    } catch (err) {
      console.error("Failed to add prescription:", err);
      throw err;
    }
  },

  editPrescription: async (id, updates) => {
    try {
      const updated = await updatePrescription(id, updates);
      const { _rawPrescriptions } = get();
      const updatedRaw = _rawPrescriptions.map(p => p._id === id ? updated : p);
      set({
        _rawPrescriptions: updatedRaw,
        medications: updatedRaw.map(mapPrescriptionToMedication)
      });
    } catch (err) {
      console.error("Failed to edit prescription:", err);
      throw err;
    }
  },

  removePrescription: async (id) => {
    try {
      await deletePrescription(id);
      const { _rawPrescriptions } = get();
      const updatedRaw = _rawPrescriptions.filter(p => p._id !== id);
      set({
        _rawPrescriptions: updatedRaw,
        medications: updatedRaw.map(mapPrescriptionToMedication)
      });
    } catch (err) {
      console.error("Failed to remove prescription:", err);
      throw err;
    }
  },

  // Computed getters
  getCurrentCondition: () => {
    const state = get();
    if (state.conditions.length === 0) return undefined;
    return state.conditions[state.currentConditionIndex];
  },

  getFilteredTimelineEvents: () => {
    const state = get();
    const allEvents: import('../types/medical').TimelineEvent[] = [
      ...state.appointments,
      ...state.labResults,
      ...state.vitals,
      ...state.vaccinations,
      ...state.imaging,
    ];

    if (state.timelineFilter === 'all') {
      return allEvents.sort((a, b) =>
        new Date(b.date).getTime() - new Date(a.date).getTime()
      );
    }

    return allEvents
      .filter(event => event.type === state.timelineFilter)
      .sort((a, b) =>
        new Date(b.date).getTime() - new Date(a.date).getTime()
      );
  },

  getRelatedAppointments: (conditionId: number) => {
    const state = get();
    const condition = state.conditions.find(c => c.id === conditionId);
    if (!condition) return [];

    return state.appointments.filter(apt =>
      apt.diagnosis.includes(condition.code) ||
      apt.diagnosis.includes(condition.name)
    );
  },

  getRelatedMedications: (conditionId: number) => {
    const state = get();
    const condition = state.conditions.find(c => c.id === conditionId);
    if (!condition?.sourceDiagnosisId) return [];

    const diagnosis = state._rawDiagnoses.find(d => d._id === condition.sourceDiagnosisId);
    const relatedPrescriptionIds = new Set<string>();

    state._rawPrescriptions.forEach((prescription) => {
      if (prescription.diagnosisId === condition.sourceDiagnosisId) {
        relatedPrescriptionIds.add(prescription._id);
      }
    });

    (diagnosis?.relatedPrescriptions || []).forEach((relatedPrescription) => {
      relatedPrescriptionIds.add(relatedPrescription._id);

      const fullPrescription = state._rawPrescriptions.find(
        (prescription) => prescription.prescriptionId === relatedPrescription.prescriptionId
      );
      if (fullPrescription) {
        relatedPrescriptionIds.add(fullPrescription._id);
      }
    });

    if (relatedPrescriptionIds.size === 0) return [];

    return state.medications.filter(med => relatedPrescriptionIds.has(med.id));
  },
}));
