import type { BibliographyResponse, CitationLibrarySource, CitationStyle } from "@skola/shared";

// ---------------------------------------------------------------------------
// Author formatting helpers
// ---------------------------------------------------------------------------

interface NameParts {
  last: string;
  initials: string;
  first: string;
}

function parseAuthor(author: string): NameParts {
  // Handles "Last, First Middle" or "First Last" formats
  const commaIdx = author.indexOf(",");
  if (commaIdx > -1) {
    const last = author.slice(0, commaIdx).trim();
    const rest = author.slice(commaIdx + 1).trim();
    const firstNames = rest.split(/\s+/);
    const initials = firstNames.map((n) => (n[0] ?? "").toUpperCase() + ".").join(" ");
    const first = rest;
    return { last, initials, first };
  }
  // "First Last" format
  const parts = author.trim().split(/\s+/);
  const last = parts[parts.length - 1] ?? author;
  const firstParts = parts.slice(0, -1);
  const initials = firstParts.map((n) => (n[0] ?? "").toUpperCase() + ".").join(" ");
  const first = firstParts.join(" ");
  return { last, initials, first };
}

/** APA7 / Harvard: Last, F. M. */
function authorLastInitials(author: string): string {
  const { last, initials } = parseAuthor(author);
  return initials ? `${last}, ${initials}` : last;
}

/** MLA9 / Chicago17 first author: Last, First */
function authorLastFirst(author: string): string {
  const { last, first } = parseAuthor(author);
  return first ? `${last}, ${first}` : last;
}

/** MLA9 / Chicago17 subsequent authors: First Last */
function authorFirstLast(author: string): string {
  const { last, first } = parseAuthor(author);
  return first ? `${first} ${last}` : last;
}

/** IEEE: F. Last */
function authorInitialsLast(author: string): string {
  const { last, initials } = parseAuthor(author);
  return initials ? `${initials} ${last}` : last;
}

/** Vancouver: LastFM (no periods) */
function authorVancouver(author: string): string {
  const { last, first } = parseAuthor(author);
  const initials = first
    .split(/\s+/)
    .map((n) => (n[0] ?? "").toUpperCase())
    .join("");
  return `${last} ${initials}`;
}

function doiUrl(doi: string): string {
  if (!doi) return "";
  return doi.startsWith("http") ? doi : `https://doi.org/${doi}`;
}

// ---------------------------------------------------------------------------
// Style formatters (plain text)
// ---------------------------------------------------------------------------

function formatAPA7Plain(sources: CitationLibrarySource[]): string[] {
  return sources.map((s) => {
    const authors = s.authors.slice(0, 20);
    let authorStr: string;
    if (authors.length === 0) {
      authorStr = "Anonymous";
    } else if (authors.length <= 20 && s.authors.length <= 20) {
      const formatted = authors.map(authorLastInitials);
      if (formatted.length === 1) {
        authorStr = formatted[0] ?? "";
      } else {
        const last = formatted[formatted.length - 1] ?? "";
        authorStr = formatted.slice(0, -1).join(", ") + ", & " + last;
      }
    } else {
      const first19 = s.authors.slice(0, 19).map(authorLastInitials).join(", ");
      const lastAuthor = authorLastInitials(s.authors[s.authors.length - 1] ?? "");
      authorStr = `${first19}, ... ${lastAuthor}`;
    }

    const parts: string[] = [];
    parts.push(`${authorStr} (${s.year}).`);
    parts.push(`${s.title}.`);
    if (s.journal) {
      const vol = s.volume ? `, ${s.volume}` : "";
      const issue = s.issue ? `(${s.issue})` : "";
      const pg = s.pages ? `, ${s.pages}` : "";
      parts.push(`*${s.journal}*${vol}${issue}${pg}.`);
    }
    if (s.doi) parts.push(doiUrl(s.doi));
    return parts.join(" ");
  });
}

function formatHarvardPlain(sources: CitationLibrarySource[]): string[] {
  return sources.map((s) => {
    const formattedAuthors = s.authors.map(authorLastInitials);
    let authorStr: string;
    if (formattedAuthors.length === 0) {
      authorStr = "Anonymous";
    } else if (formattedAuthors.length === 1) {
      authorStr = formattedAuthors[0] ?? "";
    } else {
      const last = formattedAuthors[formattedAuthors.length - 1] ?? "";
      authorStr = formattedAuthors.slice(0, -1).join(", ") + " and " + last;
    }

    const parts: string[] = [];
    parts.push(`${authorStr} (${s.year})`);
    parts.push(`'${s.title}',`);
    if (s.journal) {
      const vol = s.volume ? `, ${s.volume}` : "";
      const issue = s.issue ? `(${s.issue})` : "";
      const pg = s.pages ? `, pp. ${s.pages}` : "";
      parts.push(`*${s.journal}*${vol}${issue}${pg}.`);
    }
    return parts.join(" ");
  });
}

function formatMLA9Plain(sources: CitationLibrarySource[]): string[] {
  return sources.map((s) => {
    let authorStr: string;
    if (s.authors.length === 0) {
      authorStr = "";
    } else if (s.authors.length === 1) {
      authorStr = authorLastFirst(s.authors[0] ?? "") + ".";
    } else {
      const first = authorLastFirst(s.authors[0] ?? "");
      const rest = s.authors.slice(1).map(authorFirstLast).join(", and ");
      authorStr = `${first}, and ${rest}.`;
    }

    const parts: string[] = [];
    if (authorStr) parts.push(authorStr);
    parts.push(`"${s.title}."`);
    if (s.journal) {
      const vol = s.volume ? ` ${s.volume}` : "";
      const issue = s.issue ? `.${s.issue}` : "";
      const pg = s.pages ? `: ${s.pages}` : "";
      parts.push(`*${s.journal}*${vol}${issue} (${s.year})${pg}.`);
    }
    return parts.join(" ");
  });
}

function formatChicago17Plain(sources: CitationLibrarySource[]): string[] {
  return sources.map((s) => {
    let authorStr: string;
    if (s.authors.length === 0) {
      authorStr = "";
    } else if (s.authors.length === 1) {
      authorStr = authorLastFirst(s.authors[0] ?? "") + ".";
    } else {
      const first = authorLastFirst(s.authors[0] ?? "");
      const rest = s.authors.slice(1).map(authorFirstLast).join(", and ");
      authorStr = `${first}, and ${rest}.`;
    }

    const parts: string[] = [];
    if (authorStr) parts.push(authorStr);
    parts.push(`"${s.title}."`);
    if (s.journal) {
      const vol = s.volume ? ` ${s.volume}` : "";
      const issue = s.issue ? `, no. ${s.issue}` : "";
      const pg = s.pages ? `: ${s.pages}` : "";
      parts.push(`*${s.journal}*${vol}${issue} (${s.year})${pg}.`);
    }
    return parts.join(" ");
  });
}

function formatIEEEPlain(sources: CitationLibrarySource[]): string[] {
  return sources.map((s, idx) => {
    const n = idx + 1;
    let authorStr: string;
    if (s.authors.length === 0) {
      authorStr = "Anonymous";
    } else if (s.authors.length === 1) {
      authorStr = authorInitialsLast(s.authors[0] ?? "");
    } else {
      const all = s.authors.map(authorInitialsLast);
      const last = all[all.length - 1] ?? "";
      authorStr = all.slice(0, -1).join(", ") + " and " + last;
    }

    const parts: string[] = [];
    parts.push(`[${n}] ${authorStr},`);
    parts.push(`"${s.title},"`);
    if (s.journal) {
      const vol = s.volume ? `, vol. ${s.volume}` : "";
      const issue = s.issue ? `, no. ${s.issue}` : "";
      const pg = s.pages ? `, pp. ${s.pages}` : "";
      parts.push(`*${s.journal}*${vol}${issue}${pg}, ${s.year}.`);
    } else {
      parts.push(`${s.year}.`);
    }
    return parts.join(" ");
  });
}

function formatVancouverPlain(sources: CitationLibrarySource[]): string[] {
  return sources.map((s, idx) => {
    const n = idx + 1;
    let authorStr: string;
    if (s.authors.length === 0) {
      authorStr = "Anonymous";
    } else {
      const all = s.authors.slice(0, 6).map(authorVancouver);
      if (s.authors.length > 6) {
        authorStr = all.join(", ") + " et al";
      } else {
        authorStr = all.join(", ");
      }
    }

    const parts: string[] = [];
    parts.push(`${n}. ${authorStr}.`);
    parts.push(`${s.title}.`);
    if (s.journal) {
      const vol = s.volume ? `;${s.volume}` : "";
      const issue = s.issue ? `(${s.issue})` : "";
      const pg = s.pages ? `:${s.pages}` : "";
      parts.push(`${s.journal}. ${s.year}${vol}${issue}${pg}.`);
    }
    return parts.join(" ");
  });
}

// ---------------------------------------------------------------------------
// HTML wrapper
// ---------------------------------------------------------------------------

function toHtml(entries: string[], style: CitationStyle): string {
  const htmlEntries = entries.map((entry) => {
    // Wrap *journal* in <em> tags
    const withEm = entry.replace(/\*([^*]+)\*/g, "<em>$1</em>");
    return `<p class="bibliography-entry">${withEm}</p>`;
  });
  return `<div class="bibliography" data-style="${style}">\n${htmlEntries.join("\n")}\n</div>`;
}

// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------

export function formatBibliography(
  sources: CitationLibrarySource[],
  style: CitationStyle
): BibliographyResponse {
  // Numbered styles keep original order; alpha styles sort by title
  const isNumbered = style === "IEEE" || style === "VANCOUVER";
  const sorted = isNumbered
    ? [...sources]
    : [...sources].sort((a, b) =>
        a.title.localeCompare(b.title, undefined, { sensitivity: "base" })
      );

  let plainEntries: string[];

  switch (style) {
    case "APA7":
      plainEntries = formatAPA7Plain(sorted);
      break;
    case "HARVARD":
      plainEntries = formatHarvardPlain(sorted);
      break;
    case "MLA9":
      plainEntries = formatMLA9Plain(sorted);
      break;
    case "CHICAGO17":
      plainEntries = formatChicago17Plain(sorted);
      break;
    case "IEEE":
      plainEntries = formatIEEEPlain(sorted);
      break;
    case "VANCOUVER":
      plainEntries = formatVancouverPlain(sorted);
      break;
    default: {
      const _exhaustive: never = style;
      throw new Error(`Unsupported style: ${String(_exhaustive)}`);
    }
  }

  const plain = plainEntries.join("\n\n");
  const html = toHtml(plainEntries, style);

  return { style, html, plain };
}
