// FB2 Parser - Converts FictionBook 2 to Common Intermediate Format
// FB2 is XML-based, similar to EPUB structure
// Procedural style: Functions, not classes

// ============================================================
// Main Parse Function
// ============================================================

export async function parseFB2(fb2Blob: Blob): Promise<EbookCIF> {
  // FB2 can be plain XML or zipped (.fb2.zip)
  let xmlContent: string;

  if (
    fb2Blob.type === "application/zip" ||
    fb2Blob.type === "application/x-zip-compressed"
  ) {
    const JSZip = (await import("jszip")).default;
    const zip = await JSZip.loadAsync(fb2Blob);
    const files = Object.keys(zip.files);

    // Find the first .fb2 file in the zip
    const fb2File = files.find((f) => f.endsWith(".fb2"));
    if (!fb2File) {
      throw new Error("No .fb2 file found in archive");
    }

    xmlContent = await zip.file(fb2File)!.async("text");
  } else {
    xmlContent = await fb2Blob.text();
  }

  const xmlDoc = parseXML(xmlContent);

  const metadata = extractFB2Metadata(xmlDoc);
  const toc = parseFB2TOC(xmlDoc);
  const spine = createFB2Spine(xmlDoc);
  const resources = await extractFB2Resources(xmlDoc, fb2Blob);

  // Calculate locations (minimal - backend handles detailed tracking)
  const totalCharacters = calculateFB2Characters(xmlDoc);

  return {
    metadata,
    toc,
    spine,
    resources,
    locations: {
      totalCharacters,
      estimatedPages: Math.ceil(totalCharacters / 1500),
    },
  };
}

// ============================================================
// Helper Functions
// ============================================================

function parseXML(xmlString: string): XMLDocument {
  const parser = new DOMParser();
  return parser.parseFromString(xmlString, "text/xml");
}

function extractFB2Metadata(xmlDoc: XMLDocument): EbookCIF["metadata"] {
  const titleInfo = xmlDoc.querySelector("title-info");
  const documentInfo = xmlDoc.querySelector("document-info");

  if (!titleInfo) {
    throw new Error("Invalid FB2: no title-info found");
  }

  return {
    title: titleInfo.querySelector("book-title")?.textContent || "",
    author: extractFB2Author(titleInfo),
    language: titleInfo.querySelector("lang")?.textContent || "en",
    publisher:
      documentInfo?.querySelector("publisher")?.textContent || undefined,
    isbn: undefined, // FB2 doesn't typically have ISBN
  };
}

function extractFB2Author(titleInfo: Element): string {
  const author = titleInfo.querySelector("author");
  if (!author) return "";

  const firstName = author.querySelector("first-name")?.textContent || "";
  const lastName = author.querySelector("last-name")?.textContent || "";
  const middleName = author.querySelector("middle-name")?.textContent || "";

  const parts = [firstName, middleName, lastName].filter(Boolean);
  return parts.join(" ") || "Unknown";
}

function parseFB2TOC(xmlDoc: XMLDocument): EbookCIF["toc"] {
  const toc: EbookCIF["toc"] = [];
  const body = xmlDoc.querySelector("body");

  if (!body) return toc;

  const sections = body.querySelectorAll(":scope > section");
  let sectionIndex = 0;

  for (const section of sections) {
    const title = section.querySelector("title");
    const titleText =
      title?.textContent.trim() || `Section ${sectionIndex + 1}`;

    toc.push({
      id: `section-${sectionIndex}`,
      title: titleText,
      href: `#section-${sectionIndex}`,
      children: [],
    });

    sectionIndex++;
  }

  return toc;
}

function createFB2Spine(xmlDoc: XMLDocument): EbookCIF["spine"] {
  const spine: EbookCIF["spine"] = [];
  const body = xmlDoc.querySelector("body");

  if (!body) return spine;

  // Convert each section to HTML
  const sections = body.querySelectorAll(":scope > section");

  sections.forEach((section, index) => {
    const htmlContent = convertFB2SectionToHTML(section, index);

    spine.push({
      id: `section-${index}`,
      type: "html",
      content: htmlContent,
      index,
    });
  });

  return spine;
}

function convertFB2SectionToHTML(section: Element, index: number): string {
  const title = section.querySelector("title");
  let html = `<div id="section-${index}" class="fb2-section">`;

  if (title) {
    html += `<h1>${title.textContent}</h1>`;
  }

  // Convert paragraphs
  const paragraphs = section.querySelectorAll("p");
  paragraphs.forEach((p) => {
    html += `<p>${p.innerHTML}</p>`;
  });

  // Convert images
  const images = section.querySelectorAll("image");
  images.forEach((img) => {
    const href = img.getAttribute("l:href");
    const alt = img.getAttribute("alt") || "";
    if (href) {
      html += `<img src="${href}" alt="${alt}" />`;
    }
  });

  html += "</div>";

  return html;
}

async function extractFB2Resources(
  xmlDoc: XMLDocument,
  fb2Blob: Blob,
): Promise<Map<string, Blob>> {
  const resources = new Map<string, Blob>();

  // FB2 can have embedded images (base64) or external references
  const binary = xmlDoc.querySelector("binary");
  if (binary) {
    const contentType = binary.getAttribute("content-type");
    const id = binary.getAttribute("id");

    if (contentType && id && binary.textContent) {
      // Decode base64
      const base64Data = binary.textContent.trim();
      const byteString = atob(base64Data);
      const byteArray = new Uint8Array(byteString.length);

      for (let i = 0; i < byteString.length; i++) {
        byteArray[i] = byteString.charCodeAt(i);
      }

      const blob = new Blob([byteArray], { type: contentType });
      resources.set(`#${id}`, blob);
    }
  }

  return resources;
}

function calculateFB2Characters(xmlDoc: XMLDocument): number {
  const body = xmlDoc.querySelector("body");
  if (!body) return 0;

  return body.textContent?.length || 0;
}

function generatePageBreaks(totalCharacters: number): number[] {
  const breaks: number[] = [];
  const charsPerPage = 1000;

  for (let i = charsPerPage; i < totalCharacters; i += charsPerPage) {
    breaks.push(i);
  }

  return breaks;
}

// ============================================================
// Metadata Quick Extract
// ============================================================

export async function getFB2Metadata(
  fb2Blob: Blob,
): Promise<Partial<EbookCIF["metadata"]>> {
  let xmlContent: string;

  if (
    fb2Blob.type === "application/zip" ||
    fb2Blob.type === "application/x-zip-compressed"
  ) {
    const JSZip = (await import("jszip")).default;
    const zip = await JSZip.loadAsync(fb2Blob);
    const files = Object.keys(zip.files);

    // Find the first .fb2 file in the zip
    const fb2File = files.find((f) => f.endsWith(".fb2"));

    if (!fb2File) return {};

    xmlContent = await zip.file(fb2File)!.async("text");
  } else {
    xmlContent = await fb2Blob.text();
  }

  const xmlDoc = parseXML(xmlContent);
  return extractFB2Metadata(xmlDoc);
}
