from abc import ABC
import re
from pathlib import Path
from typing import Optional

from bs4 import BeautifulSoup
from bs4.element import Tag, NavigableString, PageElement
from markdown import markdown
from pydantic import BaseModel, computed_field

from trame.config import MD_EXTENSION, ENCODING, BS4_HTML_PARSER

from trame.piece import Piece

# NOTE: Depending on which options and/or extensions are being used, the parser may need its state reset between each call to convert.
# See: https://python-markdown.github.io/reference/#the-details
# html1 = md.convert(text1)
# md.reset()
# html2 = md.convert(text2)


class Trame(BaseModel):
    path: Optional[Path]
    markdown_content: str
    html_content: str
    pieces: list[Piece]
    origin: str
    # base_url: str

    @classmethod
    def from_file(self, path: Optional[Path]):
        return TrameBuilder.from_file(path)


class TrameBuilder:
    @staticmethod
    def from_string(origin: str, markdown_content: str) -> Trame:
        html_content = markdown(markdown_content, extensions=MD_EXTENSION)
        soup = BeautifulSoup(html_content, features=BS4_HTML_PARSER)
        pieces = []
        for element in soup.children:
            piece = Piece.build_from_bs4_element(element)
            # Only for EmptyNavigableString and NoTagName
            if piece is not None:
                pieces.append(piece)
        return Trame(
            path=None,
            markdown_content=markdown_content,
            html_content=html_content,
            pieces=pieces,
            origin=origin,
        )

    @classmethod
    def from_file(cls, path: Path, encoding=ENCODING) -> "Trame":
        with open(path, encoding=encoding) as f:
            md_content = f.read()

        trame = cls.from_string(origin=str(path), markdown_content=md_content)
        # Override path field to store the actual Path object
        trame.path = path
        return trame
