"""PDF storage backends — FileSystemStorage with extensible interface.

Architecture:
    StorageBackend (ABC) → FileSystemStorage (default, ~/axm/papers/)
"""

from __future__ import annotations

from abc import ABC, abstractmethod
from pathlib import Path

__all__ = ["FileSystemStorage", "StorageBackend"]

_DEFAULT_OUTPUT_DIR = Path.home() / "axm" / "papers"


class StorageBackend(ABC):
    """Abstract interface for PDF storage.

    Extensible for future backends (e.g. ZoteroStorage).
    """

    @abstractmethod
    def save(self, ref: str, pdf_bytes: bytes, bib_key: str) -> str:
        """Save a PDF and return the absolute path.

        Args:
            ref: Identifier of the paper (for logging only).
            pdf_bytes: Raw PDF content.
            bib_key: BibTeX citation key (used for filename).

        Returns:
            Absolute path to the saved file.
        """

    @abstractmethod
    def exists(self, bib_key: str) -> bool:
        """Check if a PDF for this key already exists.

        Args:
            bib_key: BibTeX citation key.

        Returns:
            True if the PDF file exists.
        """

    @abstractmethod
    def paper_dir(self, bib_key: str) -> Path:
        """Return the directory path for a paper.

        Args:
            bib_key: BibTeX citation key.

        Returns:
            Path to the paper directory.
        """

    @abstractmethod
    def save_bibtex(self, bib_key: str, raw_bibtex: str) -> str:
        """Save BibTeX entry to paper.bib.

        Args:
            bib_key: BibTeX citation key.
            raw_bibtex: Raw BibTeX string.

        Returns:
            Absolute path to the saved file.
        """


class FileSystemStorage(StorageBackend):
    """Default storage: saves PDFs as {output_dir}/{key}/{key}.pdf.

    Layout per paper::

        {output_dir}/{key}/
        ├── {key}.pdf
        ├── paper.bib
        ├── content.md
        └── figures/

    Default output_dir: ~/axm/papers/
    """

    def __init__(self, output_dir: Path | None = None) -> None:
        self.output_dir = output_dir or _DEFAULT_OUTPUT_DIR

    def paper_dir(self, bib_key: str) -> Path:
        """Return the folder path for a given paper key."""
        return self.output_dir / bib_key

    def save(self, ref: str, pdf_bytes: bytes, bib_key: str) -> str:
        """Save PDF to disk as {key}/{key}.pdf.

        Creates parent directories if needed. Overwrites existing files.

        Args:
            ref: Identifier of the paper (for logging only).
            pdf_bytes: Raw PDF content.
            bib_key: BibTeX citation key (used for filename).

        Returns:
            Absolute path to the saved file.
        """
        folder = self.paper_dir(bib_key)
        folder.mkdir(parents=True, exist_ok=True)
        filepath = folder / f"{bib_key}.pdf"
        filepath.write_bytes(pdf_bytes)
        return str(filepath.resolve())

    def save_bibtex(self, bib_key: str, raw_bibtex: str) -> str:
        """Write paper.bib inside the paper folder.

        Args:
            bib_key: BibTeX citation key.
            raw_bibtex: Raw BibTeX string.

        Returns:
            Absolute path to paper.bib.
        """
        folder = self.paper_dir(bib_key)
        folder.mkdir(parents=True, exist_ok=True)
        bib_path = folder / "paper.bib"
        bib_path.write_text(raw_bibtex, encoding="utf-8")
        return str(bib_path.resolve())

    def exists(self, bib_key: str) -> bool:
        """Check if paper folder exists."""
        return self.paper_dir(bib_key).is_dir()
