import io
import os
import re
from collections import Counter
from dataclasses import dataclass
from typing import TextIO

from . import model


@dataclass
class SnippetContext:
    root: model.SourceLine
    input_fp: TextIO
    file_name: str
    snippet_by_name: model.SnippetByName


SnippetContexts = list[SnippetContext]


def extract_snippets(text: str) -> model.Snippets:
    rx = re.compile(
        r"^```(?P<head>\s*)"
        r"data-flow-diagram\s+"
        r"(?P<output>.*?)\s*"
        r"^(?P<src>.*?)^\s*```",
        re.DOTALL | re.M,
    )

    return [
        model.Snippet(
            text=match["head"] + match["src"],
            name=os.path.splitext(match["output"])[0],
            output=match["output"],
            line_nr=len(text[: match.start()].splitlines()),
        )
        for match in rx.finditer(text)
    ]
    # FIXME: have text a TextIO and find in streaming


def make_snippets_params(
    provenance: str,
    snippets: model.Snippets,
) -> SnippetContexts:
    res: SnippetContexts = []
    snippet_by_name = {s.name: s for s in snippets}

    for snippet in snippets:
        # snippet w/o output, maybe just as includee
        if snippet.output.startswith("#"):
            continue

        # snippet with output
        input_fp = io.StringIO(snippet.text)
        snippet_provenance = f"{provenance}<snippet:{snippet.output}>"
        root = model.SourceLine(
            "", snippet_provenance, None, snippet.line_nr, is_container=True
        )
        file_name = input_fp.name = snippet.output
        res.append(SnippetContext(root, input_fp, file_name, snippet_by_name))

    return res


def check_snippets_unicity(provenance: str, snippets: model.Snippets) -> None:
    snippet_names = [s.name for s in snippets]  # capture duplicates
    counts = Counter(snippet_names)
    multiples = {k: n for k, n in counts.items() if n > 1}
    if multiples:
        root = model.SourceLine("", provenance, None, None)
        error_prefix = model.mk_err_prefix_from(root)
        raise model.DfdException(
            f"{error_prefix}Snippets defined multiple " f"times: {multiples}"
        )
