"""
Salesforce metadata component inventory helpers.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from enum import Enum
from pathlib import Path
from typing import Any, Dict, Iterable, List, Optional


class MetadataType(str, Enum):
    CUSTOM_FIELD = "CustomField"
    PERMISSION_SET = "PermissionSet"
    VALIDATION_RULE = "ValidationRule"
    QUEUE = "Queue"


@dataclass(frozen=True)
class MetadataComponent:
    type: MetadataType
    api_name: str
    label: str | None = None
    source_path: Path | None = None
    properties: Dict[str, Any] = field(default_factory=dict)

    @property
    def parent_object(self) -> str | None:
        if self.type in {MetadataType.CUSTOM_FIELD, MetadataType.VALIDATION_RULE}:
            if "." in self.api_name:
                return self.api_name.split(".", 1)[0]
        return None

    @property
    def member_name(self) -> str:
        return self.api_name


@dataclass
class ComponentInventory:
    components: List[MetadataComponent]

    def by_type(self) -> Dict[str, List[MetadataComponent]]:
        grouped: Dict[str, List[MetadataComponent]] = {}
        for comp in self.components:
            grouped.setdefault(comp.type.value, []).append(comp)
        return grouped

    def package_xml_types(self) -> List[Dict[str, Any]]:
        grouped = self.by_type()
        payload: List[Dict[str, Any]] = []
        for type_name in sorted(grouped.keys()):
            members = sorted({comp.member_name for comp in grouped[type_name]})
            payload.append({"type": type_name, "members": members})
        return payload

    def as_map(self) -> Dict[tuple[str, str], MetadataComponent]:
        return {(comp.type.value, comp.api_name): comp for comp in self.components}


def scan_source_dir(force_app_path: Path, *, include: Optional[Iterable[MetadataType]] = None) -> ComponentInventory:
    root = _resolve_source_root(force_app_path)
    if not root.exists():
        raise ValueError(f"source directory not found: {root}")

    include_set = set(include) if include is not None else set(MetadataType)
    components: List[MetadataComponent] = []

    for path in sorted(root.rglob("*.xml")):
        if not path.is_file():
            continue
        rel = path.relative_to(root).as_posix()
        parts = rel.split("/")

        if _is_custom_field_path(parts, path.name):
            if MetadataType.CUSTOM_FIELD in include_set:
                obj = parts[1]
                field_name = _strip_suffix(path.name, ".field-meta.xml")
                api_name = f"{obj}.{field_name}"
                components.append(
                    MetadataComponent(
                        type=MetadataType.CUSTOM_FIELD,
                        api_name=api_name,
                        source_path=path,
                    )
                )
            continue

        if _is_validation_rule_path(parts, path.name):
            if MetadataType.VALIDATION_RULE in include_set:
                obj = parts[1]
                rule_name = _strip_suffix(path.name, ".validationRule-meta.xml")
                api_name = f"{obj}.{rule_name}"
                components.append(
                    MetadataComponent(
                        type=MetadataType.VALIDATION_RULE,
                        api_name=api_name,
                        source_path=path,
                    )
                )
            continue

        if _is_permission_set_path(parts, path.name):
            if MetadataType.PERMISSION_SET in include_set:
                perm_name = _strip_suffix(path.name, ".permissionset-meta.xml")
                components.append(
                    MetadataComponent(
                        type=MetadataType.PERMISSION_SET,
                        api_name=perm_name,
                        source_path=path,
                    )
                )
            continue

        if _is_queue_path(parts, path.name):
            if MetadataType.QUEUE in include_set:
                queue_name = _strip_suffix(path.name, ".queue-meta.xml")
                components.append(
                    MetadataComponent(
                        type=MetadataType.QUEUE,
                        api_name=queue_name,
                        source_path=path,
                    )
                )
            continue

    return ComponentInventory(components=components)


def _resolve_source_root(force_app_path: Path) -> Path:
    root = force_app_path
    candidate = root / "main" / "default"
    if candidate.exists():
        return candidate
    return root


def _strip_suffix(value: str, suffix: str) -> str:
    if value.endswith(suffix):
        return value[: -len(suffix)]
    return value


def _is_custom_field_path(parts: List[str], name: str) -> bool:
    if len(parts) < 4:
        return False
    return parts[0] == "objects" and parts[2] == "fields" and name.endswith(".field-meta.xml")


def _is_validation_rule_path(parts: List[str], name: str) -> bool:
    if len(parts) < 4:
        return False
    return parts[0] == "objects" and parts[2] == "validationRules" and name.endswith(
        ".validationRule-meta.xml"
    )


def _is_permission_set_path(parts: List[str], name: str) -> bool:
    if len(parts) < 2:
        return False
    return parts[0] == "permissionsets" and name.endswith(".permissionset-meta.xml")


def _is_queue_path(parts: List[str], name: str) -> bool:
    if len(parts) < 2:
        return False
    return parts[0] == "queues" and name.endswith(".queue-meta.xml")
