# taxes_az/taxes_az/doctype/xml_mapping_tool/xml_mapping_tool.py

import frappe
import json
from frappe.model.document import Document
from frappe import _
from xml.etree import ElementTree as ET

class XMLMappingTool(Document):
    pass

@frappe.whitelist()
def generate_xml(mapping_tool, doctype, docname):
    try:
        # Получаем документ с правилами маппинга
        mapping = frappe.get_doc("XML Mapping Tool", mapping_tool)
        
        # Проверяем наличие необходимых данных
        if not mapping.xml_sample_file or not mapping.mapping_data:
            frappe.throw(_("Mapping tool doesn't have sample XML or mapping data"))
        
        # Получаем данные документа
        doc_data = frappe.get_doc(doctype, docname)
        
        # Применяем маппинг для создания XML
        try:
            root = ET.fromstring(mapping.xml_sample_file)
        except Exception as e:
            frappe.throw(_("Error parsing XML template: {0}").format(str(e)))
        
        # Загружаем правила маппинга
        mapping_rules = json.loads(mapping.mapping_data)
        
        # Применяем правила маппинга
        for xml_path, field_name in mapping_rules.items():
            if not field_name:
                continue
                
            try:
                # Получаем значение поля из документа
                field_value = doc_data.get(field_name)
                
                # Обрабатываем путь XML
                if '@' in xml_path:  # Это атрибут
                    element_path, attr_name = xml_path.rsplit('@', 1)
                    element_path = element_path.rstrip('/')
                    
                    # Находим элемент по пути
                    element = _find_element_by_path(root, element_path)
                    if element is not None:
                        element.set(attr_name, str(field_value) if field_value is not None else '')
                else:  # Это обычный элемент
                    # Находим элемент по пути
                    element = _find_element_by_path(root, xml_path)
                    if element is not None:
                        element.text = str(field_value) if field_value is not None else ''
            except Exception as e:
                frappe.log_error(_("Error setting value for {0}: {1}").format(xml_path, str(e)))
        
        # Конвертируем обратно в строку
        xml_string = ET.tostring(root, encoding='utf-8', method='xml').decode('utf-8')
        
        # Добавляем XML декларацию
        xml_declaration = '<?xml version="1.0" encoding="UTF-8"?>\n'
        xml_string = xml_declaration + xml_string
        
        return xml_string
    except Exception as e:
        frappe.log_error(_("Error generating XML: {0}").format(str(e)))
        frappe.throw(_("Error generating XML: {0}").format(str(e)))

def _find_element_by_path(root, path):
    """
    Находит элемент по пути XPath в XML дереве.
    Эта функция нужна, поскольку простой root.find() не всегда правильно работает
    с вложенными путями.
    """
    if not path:
        return None
        
    # Удаляем имя корневого элемента из пути, если оно там есть
    if path.startswith(root.tag + '/'):
        path = path[len(root.tag) + 1:]
    
    # Разбиваем путь на компоненты
    components = path.split('/')
    
    # Начинаем с корневого элемента
    current_element = root
    
    # Проходим по каждому компоненту пути
    for i, component in enumerate(components):
        if not component:  # Пропускаем пустые компоненты
            continue
            
        if component == current_element.tag:
            # Если текущий компонент соответствует тегу элемента, просто продолжаем
            continue
            
        # Для путей с множественными элементами (например, row в HesaplamaUzreEnt)
        # мы должны найти все подходящие элементы и выбрать нужный
        # Для простоты, возьмем первый найденный
        found = False
        for child in current_element.findall('./'):
            if child.tag == component:
                current_element = child
                found = True
                break
                
        if not found:
            # Пробуем найти через XPath напрямую
            try:
                result = current_element.find('.//' + component)
                if result is not None:
                    current_element = result
                    found = True
            except:
                pass
                
        if not found:
            # Если элемент не найден, возвращаем None
            return None
    
    return current_element