"""This module provides functions to process snippets of DFD source in whole MD files."""

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:
    """Find all ```data-flow-diagram ...``` code blocks in a markdown text."""
    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, 0)
        error_prefix = model.mk_err_prefix_from(root)
        raise model.DfdException(
            f"{error_prefix}Snippets defined multiple " f"times: {multiples}"
        )
