"""Classes for representing match statement patterns."""

from __future__ import annotations

from typing import TypeVar

from mypy_extensions import trait

from mypy.nodes import Expression, NameExpr, Node, RefExpr
from mypy.visitor import PatternVisitor

T = TypeVar("T")


@trait
class Pattern(Node):
    """A pattern node."""

    __slots__ = ()

    def accept(self, visitor: PatternVisitor[T]) -> T:
        raise RuntimeError("Not implemented", type(self))


class AsPattern(Pattern):
    """The pattern <pattern> as <name>"""

    # The python ast, and therefore also our ast merges capture, wildcard and as patterns into one
    # for easier handling.
    # If pattern is None this is a capture pattern. If name and pattern are both none this is a
    # wildcard pattern.
    # Only name being None should not happen but also won't break anything.
    pattern: Pattern | None
    name: NameExpr | None

    def __init__(self, pattern: Pattern | None, name: NameExpr | None) -> None:
        super().__init__()
        self.pattern = pattern
        self.name = name

    def accept(self, visitor: PatternVisitor[T]) -> T:
        return visitor.visit_as_pattern(self)


class OrPattern(Pattern):
    """The pattern <pattern> | <pattern> | ..."""

    patterns: list[Pattern]

    def __init__(self, patterns: list[Pattern]) -> None:
        super().__init__()
        self.patterns = patterns

    def accept(self, visitor: PatternVisitor[T]) -> T:
        return visitor.visit_or_pattern(self)


class ValuePattern(Pattern):
    """The pattern x.y (or x.y.z, ...)"""

    expr: Expression

    def __init__(self, expr: Expression):
        super().__init__()
        self.expr = expr

    def accept(self, visitor: PatternVisitor[T]) -> T:
        return visitor.visit_value_pattern(self)


class SingletonPattern(Pattern):
    # This can be exactly True, False or None
    value: bool | None

    def __init__(self, value: bool | None):
        super().__init__()
        self.value = value

    def accept(self, visitor: PatternVisitor[T]) -> T:
        return visitor.visit_singleton_pattern(self)


class SequencePattern(Pattern):
    """The pattern [<pattern>, ...]"""

    patterns: list[Pattern]

    def __init__(self, patterns: list[Pattern]):
        super().__init__()
        self.patterns = patterns

    def accept(self, visitor: PatternVisitor[T]) -> T:
        return visitor.visit_sequence_pattern(self)


class StarredPattern(Pattern):
    # None corresponds to *_ in a list pattern. It will match multiple items but won't bind them to
    # a name.
    capture: NameExpr | None

    def __init__(self, capture: NameExpr | None):
        super().__init__()
        self.capture = capture

    def accept(self, visitor: PatternVisitor[T]) -> T:
        return visitor.visit_starred_pattern(self)


class MappingPattern(Pattern):
    keys: list[Expression]
    values: list[Pattern]
    rest: NameExpr | None

    def __init__(self, keys: list[Expression], values: list[Pattern], rest: NameExpr | None):
        super().__init__()
        assert len(keys) == len(values)
        self.keys = keys
        self.values = values
        self.rest = rest

    def accept(self, visitor: PatternVisitor[T]) -> T:
        return visitor.visit_mapping_pattern(self)


class ClassPattern(Pattern):
    """The pattern Cls(...)"""

    class_ref: RefExpr
    positionals: list[Pattern]
    keyword_keys: list[str]
    keyword_values: list[Pattern]

    def __init__(
        self,
        class_ref: RefExpr,
        positionals: list[Pattern],
        keyword_keys: list[str],
        keyword_values: list[Pattern],
    ):
        super().__init__()
        assert len(keyword_keys) == len(keyword_values)
        self.class_ref = class_ref
        self.positionals = positionals
        self.keyword_keys = keyword_keys
        self.keyword_values = keyword_values

    def accept(self, visitor: PatternVisitor[T]) -> T:
        return visitor.visit_class_pattern(self)
