from enum import Enum
from typing import Optional, Union

from pydantic import BaseModel, Field, field_validator


class PrivacyRequestTopLevelFields(Enum):
    """Top level fields for privacy request."""

    privacy_request = "privacy_request"
    policy = "privacy_request.policy"
    identity = "privacy_request.identity"


class PrivacyRequestPolicyConvenienceFields(Enum):
    """Convenience fields for privacy request policy."""

    rule_action_types = "rule_action_types"
    has_access_rule = "has_access_rule"
    has_erasure_rule = "has_erasure_rule"
    has_consent_rule = "has_consent_rule"
    has_update_rule = "has_update_rule"
    rule_count = "rule_count"
    rule_names = "rule_names"
    has_storage_destination = "has_storage_destination"


class PrivacyRequestLocationConvenienceFields(Enum):
    """Convenience fields for privacy request location."""

    location_country = "location_country"
    location_groups = "location_groups"
    location_regulations = "location_regulations"


class PrivacyRequestConvenienceFields(Enum):
    """Convenience fields for privacy request."""

    # Policy convenience fields
    rule_action_types = f"{PrivacyRequestTopLevelFields.policy.value}.{PrivacyRequestPolicyConvenienceFields.rule_action_types.value}"
    has_access_rule = f"{PrivacyRequestTopLevelFields.policy.value}.{PrivacyRequestPolicyConvenienceFields.has_access_rule.value}"
    has_erasure_rule = f"{PrivacyRequestTopLevelFields.policy.value}.{PrivacyRequestPolicyConvenienceFields.has_erasure_rule.value}"
    has_consent_rule = f"{PrivacyRequestTopLevelFields.policy.value}.{PrivacyRequestPolicyConvenienceFields.has_consent_rule.value}"
    has_update_rule = f"{PrivacyRequestTopLevelFields.policy.value}.{PrivacyRequestPolicyConvenienceFields.has_update_rule.value}"
    rule_count = f"{PrivacyRequestTopLevelFields.policy.value}.{PrivacyRequestPolicyConvenienceFields.rule_count.value}"
    rule_names = f"{PrivacyRequestTopLevelFields.policy.value}.{PrivacyRequestPolicyConvenienceFields.rule_names.value}"
    has_storage_destination = f"{PrivacyRequestTopLevelFields.policy.value}.{PrivacyRequestPolicyConvenienceFields.has_storage_destination.value}"
    # Location hierarchy convenience fields
    location_country = f"{PrivacyRequestTopLevelFields.privacy_request.value}.{PrivacyRequestLocationConvenienceFields.location_country.value}"
    location_groups = f"{PrivacyRequestTopLevelFields.privacy_request.value}.{PrivacyRequestLocationConvenienceFields.location_groups.value}"
    location_regulations = f"{PrivacyRequestTopLevelFields.privacy_request.value}.{PrivacyRequestLocationConvenienceFields.location_regulations.value}"


class PrivacyRequestFields(Enum):
    """Fields for privacy request."""

    created_at = f"{PrivacyRequestTopLevelFields.privacy_request.value}.created_at"
    due_date = f"{PrivacyRequestTopLevelFields.privacy_request.value}.due_date"
    identity_verified_at = (
        f"{PrivacyRequestTopLevelFields.privacy_request.value}.identity_verified_at"
    )
    location = f"{PrivacyRequestTopLevelFields.privacy_request.value}.location"
    origin = f"{PrivacyRequestTopLevelFields.privacy_request.value}.origin"
    requested_at = f"{PrivacyRequestTopLevelFields.privacy_request.value}.requested_at"
    source = f"{PrivacyRequestTopLevelFields.privacy_request.value}.source"
    submitted_by = f"{PrivacyRequestTopLevelFields.privacy_request.value}.submitted_by"


class PolicyFields(Enum):
    """Fields for policy."""

    id = "privacy_request.policy.id"
    name = f"{PrivacyRequestTopLevelFields.policy.value}.name"
    key = f"{PrivacyRequestTopLevelFields.policy.value}.key"
    description = f"{PrivacyRequestTopLevelFields.policy.value}.description"
    execution_timeframe = (
        f"{PrivacyRequestTopLevelFields.policy.value}.execution_timeframe"
    )
    rules = f"{PrivacyRequestTopLevelFields.policy.value}.rules"


class IdentityFields(Enum):
    """Fields for identity."""

    email = f"{PrivacyRequestTopLevelFields.identity.value}.email"
    phone_number = f"{PrivacyRequestTopLevelFields.identity.value}.phone_number"
    external_id = f"{PrivacyRequestTopLevelFields.identity.value}.external_id"
    fides_user_device_id = (
        f"{PrivacyRequestTopLevelFields.identity.value}.fides_user_device_id"
    )
    ljt_readerID = f"{PrivacyRequestTopLevelFields.identity.value}.ljt_readerID"
    ga_client_id = f"{PrivacyRequestTopLevelFields.identity.value}.ga_client_id"


class ConditionalDependencyFieldType(Enum):
    """Type of the field for conditional dependencies."""

    string = "string"
    boolean = "boolean"
    array = "array"
    integer = "integer"
    float = "float"
    date = "date"


class CustomFieldPathPrefix(Enum):
    """Valid path prefixes for custom fields in privacy request conditional dependencies.

    Custom privacy request fields are dynamically created based on privacy center
    configuration and are stored on the privacy request itself (not the policy).
    """

    custom_privacy_request_fields = "privacy_request.custom_privacy_request_fields"

    @classmethod
    def is_valid_custom_field_path(cls, field_path: str) -> bool:
        """Check if a field path is a valid custom field path.

        Args:
            field_path: The field path to validate

        Returns:
            True if the path starts with a valid custom field prefix, False otherwise
        """
        return any(field_path.startswith(prefix.value + ".") for prefix in cls)

    @classmethod
    def get_custom_field_name(cls, field_path: str) -> Optional[str]:
        """Extract the custom field name from a valid custom field path.

        Args:
            field_path: The field path to extract the field name from

        Returns:
            The custom field name if the path is valid, None otherwise
        """
        for prefix in cls:
            if field_path.startswith(prefix.value + "."):
                return field_path[len(prefix.value) + 1 :]
        return None


# Union type for all valid field paths
# Pydantic will validate against enum values for standard fields
# Custom field paths (dynamically created, validated at runtime) follow the pattern:
# privacy_request.custom_privacy_request_fields.{field_name}
ConditionalDependencyFieldPath = Union[
    PrivacyRequestFields,
    PolicyFields,
    IdentityFields,
    PrivacyRequestConvenienceFields,
    str,  # Custom field paths (must match prefix pattern, validated below)
]


class ConditionalDependencyFieldInfo(BaseModel):
    """Information about a field available for conditional dependencies."""

    field_path: ConditionalDependencyFieldPath = Field(
        ...,
        description="Path to the field in the PrivacyRequest. Must be one of the standard field paths or a valid custom field path.",
    )
    field_type: ConditionalDependencyFieldType = Field(
        ..., description="Type of the field from ConditionalDependencyFieldType"
    )
    description: Optional[str] = Field(None, description="Description of the field")
    is_convenience_field: bool = Field(
        False,
        description="Whether this is a convenience field (derived from other fields)",
    )

    @field_validator("field_path")
    @classmethod
    def validate_field_path(cls, value: Union[Enum, str]) -> Union[Enum, str]:
        """Validate that field_path is either an enum value or a valid custom field path.

        Args:
            value: The field path to validate (can be an enum member or string)

        Returns:
            The validated field path

        Raises:
            ValueError: If the field path is not valid
        """
        # If it's already an enum member, it's valid
        if isinstance(value, Enum):
            return value

        # If it's a string, check if it matches an enum value or is a valid custom field path
        if isinstance(value, str):
            # Check if it matches any enum value
            all_enum_values = (
                {field.value for field in PrivacyRequestFields}
                | {field.value for field in PolicyFields}
                | {field.value for field in IdentityFields}
                | {field.value for field in PrivacyRequestConvenienceFields}
            )

            if value in all_enum_values:
                return value

            # If it doesn't match an enum value, it must be a valid custom field path
            if not CustomFieldPathPrefix.is_valid_custom_field_path(value):
                raise ValueError(
                    f"Invalid field_path '{value}'. Must be one of the standard field paths "
                    f"or a valid custom field path starting with one of: "
                    f"{', '.join(prefix.value for prefix in CustomFieldPathPrefix)}"
                )
            return value

        return value
