"""
Helpers for Salesforce package.xml and destructiveChanges.xml manifests.
"""

from __future__ import annotations

import xml.etree.ElementTree as ET
from pathlib import Path
from typing import Any, Dict, Iterable, List

from g_gremlin.sfdc_metadata_components import MetadataComponent


XMLNS = "http://soap.sforce.com/2006/04/metadata"


def generate_package_xml(components: Iterable[MetadataComponent], api_version: str) -> str:
    types_map = _group_components(components)
    return _build_package_xml(types_map, api_version)


def write_package_xml(path: Path, components: Iterable[MetadataComponent], api_version: str) -> Path:
    xml_text = generate_package_xml(components, api_version)
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(xml_text, encoding="utf-8")
    return path


def parse_package_xml(path: Path) -> List[Dict[str, Any]]:
    if not path.exists():
        raise ValueError(f"package.xml not found: {path}")
    tree = ET.parse(path)
    root = tree.getroot()
    ns = _detect_namespace(root)
    results: List[Dict[str, Any]] = []
    for type_node in root.findall(f".//{ns}types"):
        name_node = type_node.find(f"{ns}name")
        type_name = name_node.text.strip() if name_node is not None and name_node.text else ""
        members = []
        for member_node in type_node.findall(f"{ns}members"):
            if member_node.text:
                members.append(member_node.text.strip())
        if type_name:
            results.append({"type": type_name, "members": members})
    return results


def generate_destructive_changes_xml(
    components: Iterable[MetadataComponent],
    api_version: str,
) -> str:
    types_map = _group_components(components)
    return _build_package_xml(types_map, api_version)


# ---- internal helpers ----


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


def _detect_namespace(root: ET.Element) -> str:
    if root.tag.startswith("{") and "}" in root.tag:
        return root.tag.split("}", 1)[0] + "}"
    return ""


def _build_package_xml(types_map: Dict[str, List[str]], api_version: str) -> str:
    ET.register_namespace("", XMLNS)
    root = ET.Element(f"{{{XMLNS}}}Package")

    for type_name in sorted(types_map.keys()):
        type_elem = ET.SubElement(root, f"{{{XMLNS}}}types")
        for member in sorted(set(types_map[type_name])):
            member_elem = ET.SubElement(type_elem, f"{{{XMLNS}}}members")
            member_elem.text = member
        name_elem = ET.SubElement(type_elem, f"{{{XMLNS}}}name")
        name_elem.text = type_name

    version_elem = ET.SubElement(root, f"{{{XMLNS}}}version")
    version_elem.text = str(api_version)

    try:
        ET.indent(root)  # type: ignore[attr-defined]
    except Exception:
        pass
    xml_text = ET.tostring(root, encoding="unicode")
    if not xml_text.endswith("\n"):
        xml_text += "\n"
    return xml_text
