import dataclasses
from typing import (
    List,
    NotRequired,
    Union,
    Type,
    Any,
    Mapping,
    Self,
    Tuple,
    Iterable,
    TypeVar,
)

from debputy.manifest_parser.declarative_parser import ParserGenerator
from debputy.manifest_parser.tagging_types import DebputyParsedContent
from debputy.plugin.api.spec import ParserDocumentation

_DEBPUTY_DOC_PARSER_GENERATOR = ParserGenerator()


class AttributeRefDoc(DebputyParsedContent):
    attr: NotRequired[Union[str, List[str]]]
    description: str


class ParserRefDocumentation(DebputyParsedContent):
    title: str
    description: NotRequired[str]
    attributes: NotRequired[List[AttributeRefDoc]]
    undocumented_attributes: NotRequired[List[str]]
    non_mapping_description: NotRequired[str]
    ref_doc_url: NotRequired[str]
    synopsis: NotRequired[str]


class PMRData(ParserRefDocumentation):
    rule_reference: str
    documentation: NotRequired[ParserRefDocumentation]


class DebputyDocReferenceData(DebputyParsedContent):
    pluggable_manifest_rules: NotRequired[List[PMRData]]


DEBPUTY_DOC_REFERENCE_DATA_PARSER = _DEBPUTY_DOC_PARSER_GENERATOR.generate_parser(
    DebputyDocReferenceData
)


D = TypeVar("D")


def _unique(
    topic: str, key_attr_name: str, kd: Iterable[Tuple[str, D]]
) -> Mapping[str, D]:
    seen = set()
    r = {}
    for k, d in kd:
        if k in seen:
            raise ValueError(
                f"The multiple instances of the {key_attr_name} attribute in {topic}"
                f' had the value: "{k}". The value must be unique'
            )
        seen.add(k)
        r[k] = d

    return r


@dataclasses.dataclass(slots=True, frozen=True)
class DebputyParsedDoc:
    pluggable_manifest_rules: Mapping[str, ParserDocumentation]

    @classmethod
    def from_ref_data(cls, docs: DebputyDocReferenceData) -> Self:
        return cls(
            pluggable_manifest_rules=_unique(
                "pluggable-manifest-rules",
                "rule-reference",
                (
                    (d["rule_reference"], ParserDocumentation.from_ref_doc(d))
                    for d in docs.get("pluggable_manifest_rules", [])
                ),
            )
        )


def parser_type_name(v: Union[str, Type[Any]]) -> str:
    if isinstance(v, str):
        return v if v != "<ROOT>" else ""
    return v.__name__
