import json
import yaml
import tomli_w
import configparser
import xml.etree.ElementTree as ET
from types import SimpleNamespace
from typing import Any, Union, List
from pathlib import Path
from enum import Enum


class ConfigFormat(Enum):
    JSON = "json"
    YAML = "yaml"
    TOML = "toml"
    INI = "ini"
    XML = "xml"


def _namespace_to_dict(obj: Any) -> Any:
    if isinstance(obj, SimpleNamespace):
        return {k: _namespace_to_dict(v) for k, v in vars(obj).items()}
    elif isinstance(obj, dict):
        return {k: _namespace_to_dict(v) for k, v in obj.items()}
    elif isinstance(obj, list):
        return [_namespace_to_dict(item) for item in obj]
    else:
        return obj


def _filter_dict(data: Any, exclude: List[str]) -> Any:
    if isinstance(data, dict):
        return {k: _filter_dict(v, exclude) for k, v in data.items() if k not in exclude}
    elif isinstance(data, list):
        return [_filter_dict(item, exclude) for item in data]
    else:
        return data


def _dict_to_xml_element(name: str, data: Any) -> ET.Element:
    """
    Recursively converts a dictionary or list into an XML Element.
    """
    element = ET.Element(name)
    if isinstance(data, dict):
        for key, val in data.items():
            child = _dict_to_xml_element(key, val)
            element.append(child)
    elif isinstance(data, list):
        for item in data:
            child = _dict_to_xml_element("item", item)
            element.append(child)
    else:
        element.text = str(data)
    return element


def write_config(
    config: Union[dict, SimpleNamespace],
    filename: str,
    format: ConfigFormat,
    exclude_keys: List[str] = None
) -> None:
    """
    Writes configuration data to a file in the given format.

    :param config: Dictionary or SimpleNamespace to write.
    :param filename: Target file path.
    :param format: One of 'json', 'yaml', 'toml', 'ini', or 'xml'.
    :param exclude_keys: List of top-level keys to exclude from output.
    :raises ValueError: If the format is unsupported.
    """
    exclude_keys = exclude_keys or []
    data = _namespace_to_dict(config)
    filtered = _filter_dict(data, exclude_keys)

    path = Path(filename)
    if format == ConfigFormat.JSON:
        with open(path, "w", encoding="utf-8") as f:
            json.dump(filtered, f, indent=2)

    elif format == ConfigFormat.YAML:
        with open(path, "w", encoding="utf-8") as f:
            yaml.safe_dump(filtered, f, sort_keys=False)

    elif format == ConfigFormat.TOML:
        with open(path, "wb") as f:
            f.write(tomli_w.dumps(filtered).encode("utf-8"))

    elif format == ConfigFormat.INI:
        config_parser = configparser.ConfigParser()

        for section, values in filtered.items():
            if isinstance(values, dict):
                config_parser[section] = {str(k): str(v) for k, v in values.items()}
            else:
                config_parser["DEFAULT"][section] = str(values)

        with open(path, "w", encoding="utf-8") as f:
            config_parser.write(f)
    elif format == ConfigFormat.XML:
        root = _dict_to_xml_element("config", filtered)
        tree = ET.ElementTree(root)
        tree.write(path, encoding="utf-8", xml_declaration=True)
    else:
        raise ValueError(f"Unsupported format: {format}")
