from lxml import etree
from pathlib import Path
import shutil

# ==========================
# CONFIGURAÇÕES
# ==========================
REMOVE_COMMENTS = True  # True para remover comentários, False para deixar

# Lista de caminhos base
BASE_DIRS = [
    Path("/home/teknisa/workfolder/framework"),
]

# Pastas a ignorar
IGNORE_DIRS = {"vendor", "node_modules", "bower_components", ".idea"}


# ==========================
# FUNÇÕES
# ==========================
def should_ignore(path: Path):
    return any(part in IGNORE_DIRS for part in path.parts)


def iter_children_safe(elem):
    """Itera apenas elementos válidos, ignorando comentários ou objetos inválidos."""
    for child in elem:
        if isinstance(child, etree._Element):
            yield child
        else:
            continue


def converter_xml(file_path: Path):
    parser = etree.XMLParser(remove_blank_text=False)
    try:
        tree = etree.parse(str(file_path), parser)
        root = tree.getroot()
    except Exception as e:
        print(f"⚠️  Erro ao ler/parsar XML: {file_path} -> {e}")
        return "erro"

    # Remove comentários se configurado
    if REMOVE_COMMENTS:
        for elem in root.xpath('//comment()'):
            parent = elem.getparent()
            if parent is not None:
                parent.remove(elem)

    alterado = False

    # Namespace
    nsmap = {'ns': root.nsmap.get(None)} if None in root.nsmap else {}

    # --- 1️⃣ Adiciona <defaults public="true"/> ---
    try:
        for services in root.xpath(".//ns:services", namespaces=nsmap):
            for child in iter_children_safe(services):
                try:
                    has_defaults = any(
                        isinstance(c, etree._Element) and (c.tag.split("}")[-1] if "}" in c.tag else c.tag) == "defaults"
                        for c in iter_children_safe(services)
                    )
                    if not has_defaults:
                        defaults = etree.Element("defaults")
                        defaults.set("public", "true")
                        services.insert(0, defaults)
                        alterado = True
                        print(f"➕ Adicionado <defaults public=\"true\"/> em {file_path}")
                except Exception as e:
                    print(f"⚠️ Erro ao processar <defaults> em {file_path} -> {e}")
    except Exception as e:
        print(f"⚠️ Erro ao localizar <services> em {file_path} -> {e}")

    # --- 2️⃣ Converte factory-class/factory-method ---
    try:
        for service in root.xpath(".//ns:service", namespaces=nsmap):
            for s in iter_children_safe(service):
                try:
                    factory_class = service.attrib.pop("factory-class", None)
                    factory_method = service.attrib.pop("factory-method", None)
                    if factory_class and factory_method:
                        factory = etree.Element("factory")
                        factory.set("class", factory_class)
                        factory.set("method", factory_method)
                        service.insert(0, factory)
                        alterado = True
                        print(f"🛠️ Convertido service '{service.attrib.get('id', '')}' para usar <factory>")
                except Exception as e:
                    print(f"⚠️ Erro ao processar <service> em {file_path} -> {e}")
    except Exception as e:
        print(f"⚠️ Erro ao localizar <service> em {file_path} -> {e}")

    # --- 3️⃣ Remove type="service" de parâmetros ---
    try:
        for param in root.xpath(".//ns:parameter", namespaces=nsmap):
            if isinstance(param, etree._Element) and param.attrib.get("type") == "service":
                key = param.attrib.get("key", "")
                val = param.attrib.get("id", "") or param.text

                print(f"📝 Convertendo parâmetro service: key='{key}' -> valor='{val}'")

                # Remove os atributos
                param.attrib.pop("type", None)
                param.attrib.pop("id", None)

                # Define o valor como texto
                param.text = val

                alterado = True
    except Exception as e:
        print(f"⚠️ Erro ao processar <parameter> em {file_path} -> {e}")

    # --- 4️⃣ Remove key inválida em <argument type="collection"> ---
    try:
        for arg in root.xpath(".//ns:argument", namespaces=nsmap):
            if isinstance(arg, etree._Element) and arg.attrib.get("type") == "collection" and "key" in arg.attrib:
                key_val = arg.attrib["key"]
                if not key_val.startswith("$"):
                    parent_service = arg.getparent()
                    while parent_service is not None and not (isinstance(parent_service, etree._Element) and (parent_service.tag.split("}")[-1] if "}" in parent_service.tag else parent_service.tag) == "service"):
                        parent_service = parent_service.getparent()
                    service_id = parent_service.attrib.get("id", "") if parent_service is not None else "??"
                    print(f"❌ Removendo key inválida em service '{service_id}': '{key_val}'")
                    arg.attrib.pop("key")
                    alterado = True
    except Exception as e:
        print(f"⚠️ Erro ao processar <argument> em {file_path} -> {e}")

    # --- Salva se alterado ---
    if alterado:
        shutil.copy(file_path, file_path.with_suffix(file_path.suffix + ".bak"))
        tree.write(
            str(file_path),
            pretty_print=True,
            encoding="utf-8",
            xml_declaration=True
        )
        print(f"✅ Modificado: {file_path}")
        return "modificado"
    else:
        print(f"ℹ️ Sem mudanças: {file_path}")
        return "sem_mudanca"


# ==========================
# EXECUÇÃO
# ==========================
if __name__ == "__main__":
    print("🚀 Iniciando conversão dos arquivos XML...\n")

    modificados, sem_mudanca, erros = [], [], []

    for base in BASE_DIRS:
        for xml in base.rglob("*.xml"):
            if should_ignore(xml):
                continue
            try:
                status = converter_xml(xml)
            except Exception as e:
                print(f"⚠️ Erro crítico em {xml} -> {e}")
                erros.append(xml)
                continue

            if status == "modificado":
                modificados.append(xml)
            elif status == "sem_mudanca":
                sem_mudanca.append(xml)
            else:
                erros.append(xml)

    print("\n📊 Resumo:")
    if modificados:
        print(f"✅ Modificados ({len(modificados)}):")
        for f in modificados:
            print("   -", f)
    if sem_mudanca:
        print(f"ℹ️ Sem mudanças ({len(sem_mudanca)}):")
        for f in sem_mudanca:
            print("   -", f)
    if erros:
        print(f"❌ Erros ({len(erros)}):")
        for f in erros:
            print("   -", f)

    print("\n🏁 Conversão finalizada!")
