from typing import Optional, Literal, cast
from TexSoup import TexNode
from TexSoup.data import TexNamedEnv, BracketGroup
from collections.abc import Sequence

from texmemo.parser import Parser, get_environments
from texmemo.config import Config
from texmemo.types import Card, SectionConfig, ParsedEnv
from texmemo.writer import CSVWriter
from texmemo.filter import Filter

from .format import CSVFormatter


class Exporter():
    def __init__(self) -> None:
        self._parser = Parser()
        self._config = Config()
        self._writer = CSVWriter()
        self._filter = Filter()
        self._formatter = CSVFormatter()

    def export(
        self,
        input: str | TexNode,
        output_path: Optional[str],
        tag: bool,
        header: bool,
        delimiter: str,
        strict_mode: bool,
        write_file: bool,
        force: bool
    ):  # -> CSV-like object
        if isinstance(input, str):
            input = self._parser.parse(input, strict_mode)
        # TODO fix should take from config currently hard-coded
        parsed_env = self._filter.filter(
            input, None, None, [SectionConfig(name="theorem", child="proof"), SectionConfig(name="definition", child=None)])
        cards = self._create_cards(parsed_env, tag)
        csv = self._formatter.format_csv(cards, header)

        if write_file and output_path:
            self._writer.write(output_path, csv, delimiter, force)

    def _get_subtitle(self, env: ParsedEnv) -> str | None:
        args = env.env.args
        if args is None:
            return None
        arg = args[0]
        # Need to do this because of TexSoup limited type system
        if arg and isinstance(arg, BracketGroup):
            subtitle = cast(Sequence, arg.contents)
            subtitle = [str(x) for x in subtitle]
            return "".join(subtitle)

    def _get_front(self, env: ParsedEnv, method: Literal['subtitle', 'comment']) -> str | None:
        if method == 'subtitle':
            return self._get_subtitle(env)
        else:
            caption = env.env.find("ankisync")
            if caption is None:
                return None
            return caption.contents[0]

    def _get_back(self, env: ParsedEnv) -> str:
        back = [str(env.env)]
        if env.child is not None:
            back.append(str(env.child))

        return "\n".join(back)

    def _create_card(self, env: ParsedEnv, tag: bool, method: Literal['subtitle', 'comment']) -> Card | None:
        tag_name = str(env.env.name) if tag else None
        front = self._get_front(env, method)
        back = self._get_back(env)

        if front is None or back is None:
            return None

        return Card(front=front, back=back, tag=tag_name)

    def _create_cards(self, envs: list[ParsedEnv], tag: bool) -> list[Card]:
        # TODO take from config currently hard-coded
        methods: dict[str, Literal['subtitle', 'comment']] = {"definition": "subtitle",
                                                              "theorem": "comment", "lemma": "comment"}

        cards: list[Card] = []
        for env in envs:
            method = methods.get(env.env.name)
            if method:
                card = self._create_card(env, tag, method)
                if card:
                    cards.append(card)

        return cards
