import { access, mkdir, readFile, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";

import type {
  CitationImportFormat,
  CitationLibrarySource,
  ImportCitationResponse,
  ImportEnwResponse,
  ReferenceLibraryResponse
} from "@skola/shared";

import { parseCitationFile } from "./citation-parsers.js";

interface CitationLibraryFile {
  version: number;
  nextDecimalId: number;
  sources: CitationLibrarySource[];
}

const LIBRARY_DIRECTORY = path.join(os.homedir(), "Documents", "Skola", "citations");
const LIBRARY_FILE_PATH = path.join(LIBRARY_DIRECTORY, "sources.skl");

function createEmptyLibrary(): CitationLibraryFile {
  return {
    version: 1,
    nextDecimalId: 1,
    sources: []
  };
}

function sortSourcesByTitle(sources: CitationLibrarySource[]): CitationLibrarySource[] {
  return [...sources].sort((left, right) =>
    left.title.localeCompare(right.title, undefined, { sensitivity: "base" })
  );
}

function normalizeSource(source: Partial<CitationLibrarySource>): CitationLibrarySource {
  return {
    decimalId: Number(source.decimalId) || 0,
    type: source.type || "Reference",
    title: source.title || "Untitled source",
    authors: Array.isArray(source.authors) ? source.authors.filter(Boolean) : [],
    journal: source.journal || "",
    volume: source.volume || "",
    issue: source.issue || "",
    pages: source.pages || "",
    date: source.date || "",
    year: Number(source.year) || 0,
    doi: source.doi || "",
    abstract: source.abstract || "",
    isbn: source.isbn || "",
    publisher: source.publisher || "",
    origin: source.origin || "ENW",
    fileName: source.fileName || "",
    importedAt: source.importedAt || new Date(0).toISOString(),
    note: source.note || ""
  };
}

async function ensureLibraryFile(): Promise<void> {
  await mkdir(LIBRARY_DIRECTORY, { recursive: true });

  try {
    await access(LIBRARY_FILE_PATH);
  } catch {
    await writeFile(LIBRARY_FILE_PATH, JSON.stringify(createEmptyLibrary(), null, 2), "utf8");
  }
}

async function readLibraryFile(): Promise<CitationLibraryFile> {
  await ensureLibraryFile();
  const raw = await readFile(LIBRARY_FILE_PATH, "utf8");

  if (!raw.trim()) {
    const emptyLibrary = createEmptyLibrary();
    await writeLibraryFile(emptyLibrary);
    return emptyLibrary;
  }

  const parsed = JSON.parse(raw) as Partial<CitationLibraryFile>;
  const sources = Array.isArray(parsed.sources)
    ? (parsed.sources as Partial<CitationLibrarySource>[]).map(normalizeSource)
    : [];
  const maxDecimalId = sources.reduce((maxValue, source) => Math.max(maxValue, source.decimalId), 0);
  const nextDecimalId =
    typeof parsed.nextDecimalId === "number" && parsed.nextDecimalId > maxDecimalId
      ? parsed.nextDecimalId
      : maxDecimalId + 1;

  return {
    version: 1,
    nextDecimalId,
    sources
  };
}

async function writeLibraryFile(library: CitationLibraryFile): Promise<void> {
  await writeFile(LIBRARY_FILE_PATH, JSON.stringify(library, null, 2), "utf8");
}

export async function getReferenceLibrary(): Promise<ReferenceLibraryResponse> {
  const library = await readLibraryFile();

  return {
    libraryPath: LIBRARY_FILE_PATH,
    sources: sortSourcesByTitle(library.sources)
  };
}

export async function importCitation(
  fileName: string,
  content: string,
  format: CitationImportFormat
): Promise<ImportCitationResponse> {
  const library = await readLibraryFile();
  const parsed = parseCitationFile(fileName, content, format);
  const added: CitationLibrarySource = {
    decimalId: library.nextDecimalId,
    origin: format.toUpperCase(),
    fileName,
    importedAt: new Date().toISOString(),
    ...parsed
  };

  const nextLibrary: CitationLibraryFile = {
    version: 1,
    nextDecimalId: library.nextDecimalId + 1,
    sources: [...library.sources, added]
  };

  await writeLibraryFile(nextLibrary);

  return {
    libraryPath: LIBRARY_FILE_PATH,
    added,
    sources: sortSourcesByTitle(nextLibrary.sources)
  };
}

export async function importEnwCitation(fileName: string, content: string): Promise<ImportEnwResponse> {
  return importCitation(fileName, content, "enw");
}

export async function deleteCitationFromLibrary(decimalId: number): Promise<ReferenceLibraryResponse> {
  const library = await readLibraryFile();
  const sources = library.sources.filter((source) => source.decimalId !== decimalId);

  const nextLibrary: CitationLibraryFile = {
    version: 1,
    nextDecimalId: Math.max(
      library.nextDecimalId,
      sources.reduce((maxValue, source) => Math.max(maxValue, source.decimalId), 0) + 1
    ),
    sources
  };

  await writeLibraryFile(nextLibrary);

  return {
    libraryPath: LIBRARY_FILE_PATH,
    sources: sortSourcesByTitle(nextLibrary.sources)
  };
}
