import type {
  AllergyItem,
  ConditionItem,
  DemographicsPayload,
  EmrLookups,
  EmrProgress,
  FamilyHistoryItem,
  HospitalisationItem,
  ImmunizationItem,
  LabResultItem,
  LabTrends,
  LifestylePayload,
  MedicalReportPayload,
  MedicationItem,
  ProcedureItem,
  ReportFile,
  ReportType,
  ReportsTimeline,
} from './emr-types';

export class EmrClientError extends Error {
  constructor(
    message: string,
    readonly statusCode: number,
  ) {
    super(message);
    this.name = 'EmrClientError';
  }
}

async function jsonOrThrow<T>(res: Response): Promise<T> {
  if (res.status === 204) return undefined as T;
  const data = (await res.json().catch(() => null)) as
    | T
    | { message: string | string[] }
    | null;
  if (!res.ok) {
    const msg =
      (data as { message?: string | string[] } | null)?.message ?? `Request failed (${res.status})`;
    throw new EmrClientError(Array.isArray(msg) ? msg.join('\n') : msg, res.status);
  }
  return data as T;
}

async function putJson<T>(path: string, body: unknown): Promise<T> {
  const res = await fetch(path, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  });
  return jsonOrThrow<T>(res);
}

async function postJson<T>(path: string, body: unknown): Promise<T> {
  const res = await fetch(path, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  });
  return jsonOrThrow<T>(res);
}

async function getJson<T>(path: string): Promise<T> {
  const res = await fetch(path, { cache: 'no-store' });
  return jsonOrThrow<T>(res);
}

export const emrClient = {
  getProgress: (): Promise<EmrProgress> => getJson('/api/emr/progress'),
  getLookups: (): Promise<EmrLookups> => getJson('/api/emr/lookups'),
  getRecord: <T = unknown>(): Promise<T> => getJson('/api/emr'),

  saveDemographics: (body: DemographicsPayload): Promise<void> =>
    putJson('/api/emr/demographics', body),

  saveLifestyle: (body: LifestylePayload): Promise<void> =>
    putJson('/api/emr/lifestyle', body),

  saveAllergies: (items: AllergyItem[]): Promise<void> =>
    putJson('/api/emr/allergies', { items }),

  saveImmunizations: (items: ImmunizationItem[]): Promise<void> =>
    putJson('/api/emr/immunizations', { items }),

  saveConditions: (items: ConditionItem[]): Promise<void> =>
    putJson('/api/emr/conditions', { items }),

  saveMedications: (items: MedicationItem[]): Promise<void> =>
    putJson('/api/emr/medications', { items }),

  saveFamilyHistory: (items: FamilyHistoryItem[]): Promise<void> =>
    putJson('/api/emr/family-history', { items }),

  addReport: (body: MedicalReportPayload): Promise<{ id: string }> =>
    postJson('/api/emr/reports', body),

  deleteReport: async (id: string): Promise<void> => {
    const res = await fetch(`/api/emr/reports/${id}`, { method: 'DELETE' });
    await jsonOrThrow<unknown>(res);
  },

  // ----- Multi-file timeline (v1-parity) -----

  getReportsTimeline: (): Promise<ReportsTimeline> => getJson('/api/emr/reports/timeline'),

  uploadReport: async (
    meta: {
      title: string;
      type: ReportType;
      subType?: string;
      reportDate?: string;
      provider?: string;
      notes?: string;
    },
    files: File[],
  ): Promise<{
    id: string;
    title: string;
    type: ReportType;
    subType: string | null;
    reportDate: string | null;
    provider: string | null;
    notes: string | null;
    files: ReportFile[];
  }> => {
    const fd = new FormData();
    fd.append('title', meta.title);
    fd.append('type', meta.type);
    if (meta.subType) fd.append('subType', meta.subType);
    if (meta.reportDate) fd.append('reportDate', meta.reportDate);
    if (meta.provider) fd.append('provider', meta.provider);
    if (meta.notes) fd.append('notes', meta.notes);
    for (const f of files) fd.append('files', f, f.name);
    const res = await fetch('/api/emr/reports/upload', { method: 'POST', body: fd });
    return jsonOrThrow(res);
  },

  appendReportFiles: async (reportId: string, files: File[]): Promise<ReportFile[]> => {
    const fd = new FormData();
    for (const f of files) fd.append('files', f, f.name);
    const res = await fetch(`/api/emr/reports/${reportId}/files`, { method: 'POST', body: fd });
    return jsonOrThrow(res);
  },

  deleteReportFile: async (reportId: string, fileId: string): Promise<void> => {
    const res = await fetch(`/api/emr/reports/${reportId}/files/${fileId}`, {
      method: 'DELETE',
    });
    await jsonOrThrow<unknown>(res);
  },

  // ----- Procedures / surgeries -----
  listProcedures: <T = unknown>(): Promise<T[]> => getJson('/api/emr/procedures'),
  saveProcedures: (items: ProcedureItem[]): Promise<void> =>
    putJson('/api/emr/procedures', { items }),

  // ----- Hospitalisations -----
  listHospitalisations: <T = unknown>(): Promise<T[]> => getJson('/api/emr/hospitalisations'),
  saveHospitalisations: (items: HospitalisationItem[]): Promise<void> =>
    putJson('/api/emr/hospitalisations', { items }),

  // ----- Lab results (structured + trends) -----
  listLabResults: <T = unknown>(): Promise<T[]> => getJson('/api/emr/lab-results'),
  getLabTrends: (): Promise<LabTrends> => getJson('/api/emr/lab-results/trends'),
  addLabResult: (body: LabResultItem): Promise<{ id: string }> =>
    postJson('/api/emr/lab-results', body),
  addLabResultsBulk: (items: LabResultItem[]): Promise<unknown> =>
    postJson('/api/emr/lab-results/bulk', { items }),
  deleteLabResult: async (id: string): Promise<void> => {
    const res = await fetch(`/api/emr/lab-results/${id}`, { method: 'DELETE' });
    await jsonOrThrow<unknown>(res);
  },
};
