/**
 * Patient-portal client for the admin-defined custom-fields engine.
 * Mirrors the admin-portal API. All requests go via the Next.js proxy
 * under `/api/form-fields/*` so the HttpOnly JWT cookie does its job.
 */

export type FormFieldContext =
  | 'patient_signup'
  | 'doctor_signup'
  | 'patient_profile'
  | 'doctor_profile'
  | 'emr_demographics'
  | 'emr_lifestyle'
  | 'emr_conditions'
  | 'emr_medications'
  | 'emr_family_history';

export type FormFieldType =
  | 'short_text'
  | 'long_text'
  | 'number'
  | 'date'
  | 'boolean'
  | 'select_single'
  | 'select_multi'
  | 'file'
  | 'image'
  | 'url'
  | 'phone';

export type FormFieldVisibility = 'admin_only' | 'self' | 'all';

export interface FormFieldDef {
  id: string;
  context: FormFieldContext;
  key: string;
  label: string;
  fieldType: FormFieldType;
  optionsJson: Array<{ value: string; label: string }> | null;
  required: boolean;
  validationJson: Record<string, unknown> | null;
  position: number;
  visibleTo: FormFieldVisibility;
  helpText: string | null;
  createdAt: string;
  updatedAt: string;
}

export interface FormFieldValue {
  id: string;
  defId: string;
  recordId: string;
  valueText: string | null;
  valueNumber: string | null;
  valueDate: string | null;
  valueBool: boolean | null;
  valueJson: unknown;
  fileUrl: string | null;
}

export interface FormFieldValuePair {
  def: FormFieldDef;
  value: FormFieldValue | null;
}

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

async function handle<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 FormFieldsClientError(
      Array.isArray(msg) ? msg.join('\n') : msg,
      res.status,
    );
  }
  return data as T;
}

export const formFieldsClient = {
  /**
   * Defs the caller is allowed to see for a context. Used during signup
   * (before a recordId exists) to render fields up front.
   */
  async renderDefs(
    context: FormFieldContext,
    recordId?: string,
  ): Promise<FormFieldDef[]> {
    const params = new URLSearchParams({ context });
    if (recordId) params.set('recordId', recordId);
    return handle<FormFieldDef[]>(
      await fetch(`/api/form-fields/render?${params.toString()}`, {
        cache: 'no-store',
      }),
    );
  },

  async getValues(
    context: FormFieldContext,
    recordId: string,
  ): Promise<FormFieldValuePair[]> {
    const params = new URLSearchParams({ context, recordId });
    return handle<FormFieldValuePair[]>(
      await fetch(`/api/form-fields/values?${params.toString()}`, {
        cache: 'no-store',
      }),
    );
  },

  async writeValue(
    defId: string,
    recordId: string,
    value: unknown,
  ): Promise<FormFieldValue> {
    return handle<FormFieldValue>(
      await fetch(`/api/form-fields/values`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ defId, recordId, value }),
      }),
    );
  },

  async uploadFile(
    defId: string,
    recordId: string,
    file: File,
  ): Promise<FormFieldValue> {
    const form = new FormData();
    form.append('file', file);
    const params = new URLSearchParams({ defId, recordId });
    return handle<FormFieldValue>(
      await fetch(`/api/form-fields/upload?${params.toString()}`, {
        method: 'POST',
        body: form,
      }),
    );
  },
};
