# Copyright (C) 2020 - Today: Iván Todorovich
# Copyright (C) 2020 - Today: Simone Rubino
# @author: Iván Todorovich (https://twitter.com/ivantodorovich)
# @author: Simone Rubino (https://github.com/Daemo00)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import re
from pathlib import Path
import lxml.etree as et
from odoo_module_migrate.base_migration_script import BaseMigrationScript


def src_model_new_value(field_elem, model_dot_name):
    """
    Particular behavior for new binding_model_id.

    Apply some heuristic to change
    old attribute `src_model=account.move`
    to `<field name="binding_model_id" ref="account.model_account_move"/>`.
    """
    module_name = model_dot_name.split(".")[0]
    model_und_name = model_dot_name.replace(".", "_")
    ref_value = f"{module_name}.model_{model_und_name}"
    field_elem.set("ref", ref_value)


def value_to_text(field_elem, attr_value):
    """Standard behavior: an attribute value is the new fields's text."""
    field_elem.text = attr_value


TAG_ATTR_RENAMING = {
    "report": {
        "name": ("report_name", value_to_text),
        "string": ("name", value_to_text),
    },
    "act_window": {
        "src_model": ("binding_model_id", src_model_new_value),
    },
}
"""
Configuration for renaming particular attributes.

The dictionary maps old tag names to their attributes' names.
Each old attribute name is then mapped a tuple containing:

- name of the corresponding new field
- a function that applies the old attribute's value to the new field
"""


def _reformat_file(file_path: Path):
    """Reformat `file_path`.

    Substitute `act_window` and `report` tag with `record` tag.
    Note that:
    - `id` attribute is kept in the `record` tag;
    - in `report` tag:
      - `string` has been renamed to `name`;
      - `name` has been renamed to `report_name`;
    - other attributes are assigned to respective fields
    """
    parser = et.XMLParser(remove_blank_text=True)
    tree = et.parse(str(file_path.resolve()), parser)
    root = tree.getroot()
    reformat_tags = (*root.findall("act_window"), *root.findall("report"))
    if not reformat_tags:
        return None

    regexp = r"(?P<indent>[ \t]*)" r"(?P<tag><{tag_type}[^/]*id=\"{tag_id}\"[^/]*/>)"

    new_tags_dict = dict()
    for tag in reformat_tags:
        tag_regex = regexp.format(
            tag_type=tag.tag,
            tag_id=tag.attrib["id"],
        )
        for attrib, value in tag.attrib.items():
            if attrib == "id":
                continue
            tag.attrib.pop(attrib)

            attr_renames_dict = TAG_ATTR_RENAMING.get(tag.tag, dict())
            new_value_func = value_to_text
            if attrib in attr_renames_dict:
                new_attr_name, new_value_func = attr_renames_dict.get(attrib)
                attrib = new_attr_name

            field_elem = et.SubElement(tag, "field", {"name": attrib})
            new_value_func(field_elem, value)

        tag.attrib["model"] = "ir.actions." + tag.tag
        tag.tag = "record"

        new_tags_dict[tag_regex] = tag

    # Read in the file
    xml_file = file_path.read_text()

    # Replace the target string
    for tag_regex, tag in new_tags_dict.items():
        match = re.search(tag_regex, xml_file)
        if match:
            indent = match.group("indent")
            tag_match = match.group("tag")
            et.indent(tag, space=indent, level=1)
            # Remove trailing newline
            tag_string = et.tostring(tag, pretty_print=True)[:-1]
            xml_file = re.sub(tag_match, tag_string.decode(), xml_file)

    # Write the file out again
    file_path.write_text(xml_file)
    return file_path


def _get_files(module_path, reformat_file_ext):
    """Get files to be reformatted."""
    file_paths = list()
    if not module_path.is_dir():
        raise Exception(f"'{module_path}' is not a directory")
    file_paths.extend(module_path.rglob("*" + reformat_file_ext))
    return file_paths


def reformat_deprecated_tags(
    logger, module_path, module_name, manifest_path, migration_steps, tools
):
    """Reformat deprecated tags in XML files.

    Deprecated tags are `act_window` and `report`:
    they have to be substituted by the `record` tag.
    """

    reformat_file_ext = ".xml"
    file_paths = _get_files(module_path, reformat_file_ext)
    logger.debug(f"{reformat_file_ext} files found:\n" f"{list(map(str, file_paths))}")

    reformatted_files = list()
    for file_path in file_paths:
        reformatted_file = _reformat_file(file_path)
        if reformatted_file:
            reformatted_files.append(reformatted_file)
    logger.debug("Reformatted files:\n" f"{list(reformatted_files)}")


_TEXT_REPLACES = {
    ".js": {
        r"tour\.STEPS\.SHOW_APPS_MENU_ITEM": "tour.stepUtils.showAppsMenuItem()",
        r"tour\.STEPS\.TOGGLE_HOME_MENU": "tour.stepUtils.toggleHomeMenu()",
    },
    ".py": {
        r"\.phantom_js\(": ".browser_js(",
    },
}


class MigrationScript(BaseMigrationScript):

    _GLOBAL_FUNCTIONS = [reformat_deprecated_tags]
    _TEXT_REPLACES = _TEXT_REPLACES
