import { randomUUID } from 'crypto';
import { readDataFile, writeDataFile } from './local-store';
import { getFallbackPatientById, listFallbackPatients } from './patient.store';

export const treatmentStatuses = [
  'PLANIFICADO',
  'EN_PROCESO',
  'COMPLETADO',
  'PAUSADO',
  'CANCELADO',
] as const;

export type TreatmentStatus = (typeof treatmentStatuses)[number];

export type StoredTreatment = {
  id: string;
  pacienteId: string;
  nombre: string;
  descripcion: string | null;
  costoBase: number;
  estado: TreatmentStatus;
  piezaDental: string | null;
  fechaInicio: string | null;
  fechaFin: string | null;
  notasClinicas: string | null;
  activo: boolean;
  createdAt: string;
  updatedAt: string;
};

type NullableText = string | null | undefined;

export type CreateFallbackTreatmentInput = {
  nombre: string;
  costoBase: number;
  descripcion?: NullableText;
  estado?: TreatmentStatus;
  piezaDental?: NullableText;
  fechaInicio?: NullableText;
  fechaFin?: NullableText;
  notasClinicas?: NullableText;
  activo?: boolean;
};

export type UpdateFallbackTreatmentInput = {
  nombre?: string;
  costoBase?: number;
  descripcion?: NullableText;
  estado?: TreatmentStatus;
  piezaDental?: NullableText;
  fechaInicio?: NullableText;
  fechaFin?: NullableText;
  notasClinicas?: NullableText;
  activo?: boolean;
};

export class FallbackTreatmentNotFoundError extends Error {
  constructor() {
    super('Tratamiento no encontrado.');
  }
}

const fileName = 'treatments.json';

const normalizeNullableText = (value: NullableText) => {
  if (typeof value !== 'string') {
    return value ?? null;
  }

  const trimmedValue = value.trim();
  return trimmedValue ? trimmedValue : null;
};

const normalizeDateValue = (value: NullableText) => {
  if (typeof value !== 'string' || !value.trim()) {
    return value ?? null;
  }

  const directDate = new Date(value);

  if (!Number.isNaN(directDate.getTime())) {
    return directDate.toISOString();
  }

  const assumedDate = new Date(`${value}T00:00:00`);

  if (!Number.isNaN(assumedDate.getTime())) {
    return assumedDate.toISOString();
  }

  return null;
};

const buildSeedTreatments = async (): Promise<StoredTreatment[]> => {
  const patients = await listFallbackPatients();

  if (patients.length === 0) {
    return [] as StoredTreatment[];
  }

  const firstPatient = patients[0];
  const secondPatient = patients[1] ?? firstPatient;

  if (!firstPatient || !secondPatient) {
    return [] as StoredTreatment[];
  }

  const now = new Date();

  const seedTreatments: StoredTreatment[] = [
    {
      id: randomUUID(),
      pacienteId: firstPatient.id,
      nombre: 'Control de caries molar 46',
      descripcion: 'Evaluacion y restauracion de molar posterior con sensibilidad.',
      costoBase: 180,
      estado: 'EN_PROCESO',
      piezaDental: '46',
      fechaInicio: new Date(now.getFullYear(), now.getMonth(), now.getDate() - 10).toISOString(),
      fechaFin: null,
      notasClinicas: 'Paciente refiere dolor al masticar y sensibilidad al frio.',
      activo: true,
      createdAt: new Date(now.getFullYear(), now.getMonth(), now.getDate() - 10).toISOString(),
      updatedAt: new Date(now.getFullYear(), now.getMonth(), now.getDate() - 2).toISOString(),
    },
    {
      id: randomUUID(),
      pacienteId: firstPatient.id,
      nombre: 'Exodoncia pieza 26',
      descripcion: 'Seguimiento post extraccion y control de cicatrizacion.',
      costoBase: 250,
      estado: 'COMPLETADO',
      piezaDental: '26',
      fechaInicio: new Date(now.getFullYear(), now.getMonth(), now.getDate() - 30).toISOString(),
      fechaFin: new Date(now.getFullYear(), now.getMonth(), now.getDate() - 18).toISOString(),
      notasClinicas: 'Procedimiento sin complicaciones. Se indico control a 7 dias.',
      activo: true,
      createdAt: new Date(now.getFullYear(), now.getMonth(), now.getDate() - 30).toISOString(),
      updatedAt: new Date(now.getFullYear(), now.getMonth(), now.getDate() - 18).toISOString(),
    },
    {
      id: randomUUID(),
      pacienteId: secondPatient.id,
      nombre: 'Corona en pieza 35',
      descripcion: 'Preparacion y colocacion de corona ceramica.',
      costoBase: 520,
      estado: 'PLANIFICADO',
      piezaDental: '35',
      fechaInicio: new Date(now.getFullYear(), now.getMonth(), now.getDate() + 4).toISOString(),
      fechaFin: null,
      notasClinicas: 'Paciente solicita plan estetico funcional.',
      activo: true,
      createdAt: new Date(now.getFullYear(), now.getMonth(), now.getDate() - 3).toISOString(),
      updatedAt: new Date(now.getFullYear(), now.getMonth(), now.getDate() - 3).toISOString(),
    },
  ];

  return seedTreatments;
};

const readTreatments = async () => readDataFile<StoredTreatment[]>(fileName, buildSeedTreatments);

const writeTreatments = async (treatments: StoredTreatment[]) => {
  await writeDataFile(fileName, treatments);
};

export const listFallbackTreatments = async (patientId?: string) => {
  const treatments = await readTreatments();
  const filteredTreatments = patientId
    ? treatments.filter((treatment) => treatment.pacienteId === patientId)
    : treatments;

  return filteredTreatments.sort(
    (left, right) => new Date(right.updatedAt).getTime() - new Date(left.updatedAt).getTime(),
  );
};

export const createFallbackTreatment = async (patientId: string, input: CreateFallbackTreatmentInput) => {
  const patient = await getFallbackPatientById(patientId);

  if (!patient) {
    throw new Error('Paciente no encontrado.');
  }

  const treatments = await readTreatments();
  const now = new Date().toISOString();
  const treatment: StoredTreatment = {
    id: randomUUID(),
    pacienteId: patient.id,
    nombre: input.nombre.trim(),
    descripcion: normalizeNullableText(input.descripcion),
    costoBase: Number(input.costoBase.toFixed(2)),
    estado: input.estado ?? 'PLANIFICADO',
    piezaDental: normalizeNullableText(input.piezaDental),
    fechaInicio: normalizeDateValue(input.fechaInicio),
    fechaFin: normalizeDateValue(input.fechaFin),
    notasClinicas: normalizeNullableText(input.notasClinicas),
    activo: input.activo ?? true,
    createdAt: now,
    updatedAt: now,
  };

  treatments.unshift(treatment);
  await writeTreatments(treatments);

  return treatment;
};

export const updateFallbackTreatment = async (
  patientId: string,
  treatmentId: string,
  input: UpdateFallbackTreatmentInput,
) => {
  const treatments = await readTreatments();
  const treatmentIndex = treatments.findIndex(
    (treatment) => treatment.id === treatmentId && treatment.pacienteId === patientId,
  );

  if (treatmentIndex === -1) {
    throw new FallbackTreatmentNotFoundError();
  }

  const currentTreatment = treatments[treatmentIndex];

  if (!currentTreatment) {
    throw new FallbackTreatmentNotFoundError();
  }

  const updatedTreatment: StoredTreatment = {
    ...currentTreatment,
    nombre:
      typeof input.nombre === 'string' && input.nombre.trim() ? input.nombre.trim() : currentTreatment.nombre,
    descripcion:
      input.descripcion !== undefined ? normalizeNullableText(input.descripcion) : currentTreatment.descripcion,
    costoBase:
      typeof input.costoBase === 'number' && Number.isFinite(input.costoBase) && input.costoBase > 0
        ? Number(input.costoBase.toFixed(2))
        : currentTreatment.costoBase,
    estado: input.estado ?? currentTreatment.estado,
    piezaDental:
      input.piezaDental !== undefined ? normalizeNullableText(input.piezaDental) : currentTreatment.piezaDental,
    fechaInicio: input.fechaInicio !== undefined ? normalizeDateValue(input.fechaInicio) : currentTreatment.fechaInicio,
    fechaFin: input.fechaFin !== undefined ? normalizeDateValue(input.fechaFin) : currentTreatment.fechaFin,
    notasClinicas:
      input.notasClinicas !== undefined ? normalizeNullableText(input.notasClinicas) : currentTreatment.notasClinicas,
    activo: typeof input.activo === 'boolean' ? input.activo : currentTreatment.activo,
    updatedAt: new Date().toISOString(),
  };

  treatments[treatmentIndex] = updatedTreatment;
  await writeTreatments(treatments);

  return updatedTreatment;
};
