import { randomUUID } from 'crypto';
import type { ClinicSettings } from './settings.store';
import { readDataFile, writeDataFile } from './local-store';
import {
  buildSequenceKey,
  formatSequenceNumber,
  getSequenceDefinition,
  type DocumentSequenceType,
} from './document-sequences';

export type StoredDocumentIssue = {
  id: string;
  moduleScope: string;
  documentType: string;
  outputFormat: string;
  documentNumber: string;
  title: string;
  statusLabel: string;
  targetId: string | null;
  patientName: string | null;
  issuerUserId: string | null;
  issuerName: string;
  issuerRole: string;
  logoLabel: string;
  signatureLabel: string;
  clinicSnapshot: ClinicSettings;
  payloadSnapshot: unknown;
  metadata: Record<string, unknown> | null;
  issuedAt: string;
  createdAt: string;
};

type StoredDocumentSequence = {
  key: string;
  series: string;
  year: number;
  currentNumber: number;
  createdAt: string;
  updatedAt: string;
};

type CreateFallbackDocumentIssueInput = Omit<StoredDocumentIssue, 'id' | 'documentNumber' | 'issuedAt' | 'createdAt'> & {
  sequenceType: DocumentSequenceType;
  issuedAt?: string;
};

type DocumentHistoryFilters = {
  moduleScope?: string;
  documentType?: string;
  targetId?: string;
  limit?: number;
};

const documentIssuesFileName = 'document-issues.json';
const documentSequencesFileName = 'document-sequences.json';

const buildEmptyDocumentHistory = async () => [] as StoredDocumentIssue[];

const buildEmptySequences = async () => [] as StoredDocumentSequence[];

const listFallbackDocumentSequences = async () =>
  readDataFile<StoredDocumentSequence[]>(documentSequencesFileName, buildEmptySequences);

export const nextFallbackDocumentSequence = async (type: DocumentSequenceType, issuedAt = new Date()) => {
  const sequences = await listFallbackDocumentSequences();
  const definition = getSequenceDefinition(type);
  const key = buildSequenceKey(type, issuedAt);
  const now = issuedAt.toISOString();
  const existingSequence = sequences.find((sequence) => sequence.key === key);

  if (!existingSequence) {
    const createdSequence: StoredDocumentSequence = {
      key,
      series: definition.series,
      year: issuedAt.getFullYear(),
      currentNumber: 1,
      createdAt: now,
      updatedAt: now,
    };

    sequences.push(createdSequence);
    await writeDataFile(documentSequencesFileName, sequences);
    return createdSequence;
  }

  existingSequence.series = definition.series;
  existingSequence.currentNumber += 1;
  existingSequence.updatedAt = now;

  await writeDataFile(documentSequencesFileName, sequences);
  return existingSequence;
};

export const syncFallbackDocumentSequence = async (
  type: DocumentSequenceType,
  minimumCurrentNumber: number,
  issuedAt = new Date(),
) => {
  const normalizedMinimum = Math.max(0, Math.floor(minimumCurrentNumber));
  const sequences = await listFallbackDocumentSequences();
  const definition = getSequenceDefinition(type);
  const key = buildSequenceKey(type, issuedAt);
  const now = issuedAt.toISOString();
  const existingSequence = sequences.find((sequence) => sequence.key === key);

  if (!existingSequence) {
    if (normalizedMinimum === 0) {
      return null;
    }

    const createdSequence: StoredDocumentSequence = {
      key,
      series: definition.series,
      year: issuedAt.getFullYear(),
      currentNumber: normalizedMinimum,
      createdAt: now,
      updatedAt: now,
    };

    sequences.push(createdSequence);
    await writeDataFile(documentSequencesFileName, sequences);
    return createdSequence;
  }

  if (existingSequence.currentNumber >= normalizedMinimum) {
    return existingSequence;
  }

  existingSequence.series = definition.series;
  existingSequence.currentNumber = normalizedMinimum;
  existingSequence.updatedAt = now;
  await writeDataFile(documentSequencesFileName, sequences);
  return existingSequence;
};

export const listFallbackDocumentIssues = async (filters: DocumentHistoryFilters = {}) => {
  const items = await readDataFile<StoredDocumentIssue[]>(documentIssuesFileName, buildEmptyDocumentHistory);

  const filteredItems = items
    .filter((item) => (filters.moduleScope ? item.moduleScope === filters.moduleScope : true))
    .filter((item) => (filters.documentType ? item.documentType === filters.documentType : true))
    .filter((item) => (filters.targetId ? item.targetId === filters.targetId : true))
    .sort((left, right) => new Date(right.issuedAt).getTime() - new Date(left.issuedAt).getTime());

  return typeof filters.limit === 'number' && Number.isFinite(filters.limit)
    ? filteredItems.slice(0, filters.limit)
    : filteredItems;
};

export const createFallbackDocumentIssue = async (input: CreateFallbackDocumentIssueInput) => {
  const issuedAt = input.issuedAt ? new Date(input.issuedAt) : new Date();
  const sequence = await nextFallbackDocumentSequence(input.sequenceType, issuedAt);
  const items = await readDataFile<StoredDocumentIssue[]>(documentIssuesFileName, buildEmptyDocumentHistory);
  const issue: StoredDocumentIssue = {
    id: randomUUID(),
    moduleScope: input.moduleScope,
    documentType: input.documentType,
    outputFormat: input.outputFormat,
    documentNumber: formatSequenceNumber(sequence.series, sequence.currentNumber),
    title: input.title,
    statusLabel: input.statusLabel,
    targetId: input.targetId,
    patientName: input.patientName,
    issuerUserId: input.issuerUserId,
    issuerName: input.issuerName,
    issuerRole: input.issuerRole,
    logoLabel: input.logoLabel,
    signatureLabel: input.signatureLabel,
    clinicSnapshot: input.clinicSnapshot,
    payloadSnapshot: input.payloadSnapshot,
    metadata: input.metadata,
    issuedAt: issuedAt.toISOString(),
    createdAt: new Date().toISOString(),
  };

  items.unshift(issue);
  await writeDataFile(documentIssuesFileName, items);
  return issue;
};
