from enum import Enum, StrEnum
from typing import Any, List, Optional, Union, Dict, Literal

from pydantic import BaseModel, field_validator, ValidationInfo


class AdditionalParam(Enum):
    NO_RESOLVE = 'no-resolve'
    SRC = 'src'


class RoutingRuleType(Enum):
    """Enumeration of all supported Clash rule types"""
    DOMAIN = "DOMAIN"
    DOMAIN_SUFFIX = "DOMAIN-SUFFIX"
    DOMAIN_KEYWORD = "DOMAIN-KEYWORD"
    DOMAIN_REGEX = "DOMAIN-REGEX"
    DOMAIN_WILDCARD = "DOMAIN-WILDCARD"

    GEOSITE = "GEOSITE"
    GEOIP = "GEOIP"

    IP_CIDR = "IP-CIDR"
    IP_CIDR6 = "IP-CIDR6"
    IP_SUFFIX = "IP-SUFFIX"
    IP_ASN = "IP-ASN"


    SRC_GEOIP = "SRC-GEOIP"
    SRC_IP_ASN = "SRC-IP-ASN"
    SRC_IP_CIDR = "SRC-IP-CIDR"
    SRC_IP_SUFFIX = "SRC-IP-SUFFIX"

    DST_PORT = "DST-PORT"
    SRC_PORT = "SRC-PORT"

    IN_PORT = "IN-PORT"
    IN_TYPE = "IN-TYPE"
    IN_USER = "IN-USER"
    IN_NAME = "IN-NAME"

    PROCESS_PATH = "PROCESS-PATH"
    PROCESS_PATH_REGEX = "PROCESS-PATH-REGEX"
    PROCESS_NAME = "PROCESS-NAME"
    PROCESS_NAME_REGEX = "PROCESS-NAME-REGEX"

    UID = "UID"
    NETWORK = "NETWORK"
    DSCP = "DSCP"

    RULE_SET = "RULE-SET"
    AND = "AND"
    OR = "OR"
    NOT = "NOT"
    SUB_RULE = "SUB-RULE"

    MATCH = "MATCH"


class Action(StrEnum):
    """Enumeration of rule actions"""
    DIRECT = "DIRECT"
    REJECT = "REJECT"
    REJECT_DROP = "REJECT-DROP"
    PASS = "PASS"
    COMPATIBLE = "COMPATIBLE"


class RuleBase(BaseModel):
    rule_type: RoutingRuleType
    action: Union[Action, str]  # Can be Action enum or custom proxy group name
    raw_rule: str

    def to_dict(self) -> Dict[str, Any]:
        pass

    def __str__(self) -> str:
        pass

    def __eq__(self, other: 'RuleBase') -> bool:
        if not isinstance(other, RuleBase):
            return NotImplemented
        return self.__str__() == other.__str__()


class ClashRule(RuleBase):
    """Represents a parsed Clash routing rule"""
    rule_type: RoutingRuleType
    payload: str
    additional_params: Optional[AdditionalParam] = None

    def condition_string(self) -> str:
        return f"{self.rule_type.value},{self.payload}"

    def to_dict(self) -> Dict[str, Any]:
        return {
            'type': self.rule_type.value,
            'payload': self.payload,
            'action': self.action.value if isinstance(self.action, Action) else self.action,
            'additional_params': self.additional_params.value if self.additional_params else None,
            'rule_string': str(self)
        }

    def __str__(self) -> str:
        rule_str = f"{self.condition_string()},{self.action}"
        if self.additional_params:
            rule_str += f",{self.additional_params.value}"
        return rule_str

    @field_validator('payload', mode='after')
    @classmethod
    def validate_payload(cls, v: Optional[str], info: ValidationInfo) -> Optional[str]:
        # 获取其他字段的值
        rule_type = info.data['rule_type']

        if rule_type == RoutingRuleType.NETWORK and v is not None and v.upper() not in ('TCP', 'UDP'):
            raise ValueError('Payload must be TCP or UDP')
        return v


class LogicRule(RuleBase):
    """Represents a logic rule (AND, OR, NOT)"""
    rule_type: Literal[RoutingRuleType.AND, RoutingRuleType.OR, RoutingRuleType.NOT]
    conditions: List[Union[ClashRule, 'LogicRule']]

    def condition_string(self) -> str:
        conditions_str = ','.join([f"({c.condition_string()})" for c in self.conditions])
        return f"{self.rule_type.value},({conditions_str})"

    def to_dict(self) -> Dict[str, Any]:
        conditions: list[str] = []
        for condition in self.conditions:
            conditions.append(condition.condition_string())

        return {
            'type': self.rule_type.value,
            'conditions': conditions,
            'action': self.action.value if isinstance(self.action, Action) else self.action,
            'rule_string': str(self)
        }

    @field_validator('conditions', mode='after')
    @classmethod
    def validate_conditions(cls, v: List[Union[ClashRule, 'LogicRule']]) -> List[Union[ClashRule, 'LogicRule']]:
        if not v:
            raise ValueError('A condition list must be provided')
        return v

    def __str__(self) -> str:
        return f"{self.condition_string()},{self.action}"


class SubRule(RuleBase):
    rule_type: Literal[RoutingRuleType.SUB_RULE] = RoutingRuleType.SUB_RULE
    condition: Union[ClashRule, LogicRule]
    action: str

    def condition_string(self) -> str:
        return f"{self.rule_type.value},({self.condition.condition_string()})"

    def to_dict(self) -> Dict[str, Any]:
        return {
            'type': self.rule_type.value,
            'condition': f"({self.condition.condition_string()})",
            'action': self.action,
            'rule_string': str(self)
        }

    def __str__(self) -> str:
        return f"{self.condition_string()},{self.action}"


class MatchRule(RuleBase):
    """Represents a match rule"""
    rule_type: Literal[RoutingRuleType.MATCH] = RoutingRuleType.MATCH

    @staticmethod
    def condition_string() -> str:
        return "MATCH"

    def to_dict(self) -> Dict[str, str]:
        return {
            'type': 'MATCH',
            'action': self.action.value if isinstance(self.action, Action) else self.action,
            'rule_string': str(self)
        }

    def __str__(self) -> str:
        return f"{self.condition_string()},{self.action}"


RuleType = Union[ClashRule, LogicRule, SubRule, MatchRule]
