"""
Copyright (C) 2024-2026

This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

from typing import (
    Any,
    Dict,
    List,
    Union,
)
import logging
from re import compile, findall

from .object_model import Family, Variable
from ..error import DictConsistencyError
from ..i18n import _
from ..tiramisu import normalize_family


class Paths:
    regexp_relative = compile(r"^_*\.(.*)$")

    def __init__(
        self,
        default_namespace: str,
        isolated_namespace: bool,
    ) -> None:
        self._data: Dict[str, Union[Variable, Family]] = {}
        self._dynamics: Dict[str:str] = {}
        if default_namespace is not None:
            default_namespace = normalize_family(default_namespace)
        self.default_namespace = default_namespace
        self.isolated_namespace = isolated_namespace

    def has_value(self) -> bool:
        return self._data != {}

    def add(
        self,
        option: "Collect",
        data: Any,
        *,
        force: bool = False,
    ) -> None:
        path = option.path
        self._data[path] = data
        if not force and option.family_is_dynamic:
            self._dynamics[path] = option.parent_dynamic

    def get_full_path(
        self,
        path: str,
        current_path: str,
    ):
        if "{{ suffix }}" in path:
            # version 1.0 and 1.1
            path = path.replace("{{ suffix }}", "{{ identifier }}")
        relative, subpath = path.split(".", 1)
        relative_len = len(relative)
        path_len = current_path.count(".")
        if path_len + 1 == relative_len:
            return subpath
        parent_path = current_path.rsplit(".", relative_len)[0]
        return parent_path + "." + subpath

    def get_with_dynamic(
        self,
        path: str,
        current_path: str,
        version: str,
        namespace: str,
        xmlfiles: List[str],
    ) -> Any:
        identifier = None
        if version != "1.0" and self.regexp_relative.search(path):
            path = self.get_full_path(
                path,
                current_path,
            )
        #        elif identifier_path:
        #            path = f"{identifier_path}.{path}"
        dynamic = None
        # version 1.0
        if version == "1.0":
            if not path in self._data and "{{ suffix }}" not in path:
                new_path = None
                current_path = None
                identifiers = []
                for name in path.split("."):
                    parent_path = current_path
                    if current_path:
                        current_path += "." + name
                    else:
                        current_path = name
                    if current_path in self._data:
                        if new_path:
                            new_path += "." + name
                        else:
                            new_path = name
                        continue
                    for dynamic_path in self._dynamics:
                        if "." in dynamic_path:
                            parent_dynamic, name_dynamic = dynamic_path.rsplit(".", 1)
                        else:
                            parent_dynamic = None
                            name_dynamic = dynamic_path
                        if (
                            parent_dynamic == parent_path
                            and name_dynamic.endswith("{{ identifier }}")
                            and name == name_dynamic.replace("{{ identifier }}", "")
                        ):
                            new_path += "." + name_dynamic
                            break
                        regexp = "^" + name_dynamic.replace("{{ identifier }}", "(.*)")
                        finded = findall(regexp, name)
                        if len(finded) != 1 or not finded[0]:
                            continue
                        if finded[0] == "{{ identifier }}":
                            identifiers.append(None)
                        else:
                            identifiers.append(finded[0])
                        if new_path is None:
                            new_path = name_dynamic
                        else:
                            new_path += "." + name_dynamic
                        parent_path = dynamic_path
                        break
                    else:
                        if new_path:
                            new_path += "." + name
                        else:
                            new_path = name
                path = new_path
            else:
                identifiers = None
        elif not path in self._data:
            new_path = parent_path = current_path = None
            identifiers = []
            for name in path.split("."):
                if current_path:
                    current_path += "." + name
                else:
                    current_path = name
                # parent_path, name_path = path.rsplit('.', 1)
                if current_path in self._data:
                    if new_path:
                        new_path += "." + name
                    else:
                        new_path = name
                    parent_path = current_path
                    if "{{ identifier }}" in name:
                        identifiers.append(None)
                    continue
                for dynamic_path in self._dynamics:
                    if "." in dynamic_path:
                        parent_dynamic, name_dynamic = dynamic_path.rsplit(".", 1)
                    else:
                        parent_dynamic = None
                        name_dynamic = dynamic_path
                    if (
                        "{{ identifier }}" not in name_dynamic
                        or parent_path != parent_dynamic
                    ):
                        continue
                    regexp = "^" + name_dynamic.replace("{{ identifier }}", "(.*)")
                    finded = findall(regexp, name)
                    if len(finded) != 1 or not finded[0]:
                        continue
                    if finded[0] == "{{ identifier }}":
                        identifiers.append(None)
                    else:
                        identifiers.append(finded[0])
                    if new_path is None:
                        new_path = name_dynamic
                    else:
                        new_path += "." + name_dynamic
                    parent_path = dynamic_path
                    break
                else:
                    if new_path:
                        new_path += "." + name
                    else:
                        new_path = name
                    if "{{ identifier }}" in name:
                        identifiers.append(None)
                    parent_path = current_path
            path = new_path
        else:
            identifiers = None
        if path not in self._data:
            return None, None
        option = self._data[path]
        option_namespace = option.namespace
        if (
            self.isolated_namespace
            and self.default_namespace not in [namespace, option_namespace]
            and namespace != option_namespace
        ):
            msg = _(
                'The variable or the family "{0}" located in the "{1}" namespace shall not be used in the "{2}" namespace'
            ).format(path, option_namespace, namespace)
            raise DictConsistencyError(msg, 38, xmlfiles)
        return option, identifiers

    def __getitem__(
        self,
        path: str,
    ) -> Union[Family, Variable]:
        if not path in self._data:
            raise AttributeError(f"cannot find variable or family {path}")
        return self._data[path]

    def __contains__(
        self,
        path: str,
    ) -> bool:
        return path in self._data

    def __delitem__(
        self,
        path: str,
    ) -> None:
        logging.info("remove empty family %s", path)
        del self._data[path]

    def is_dynamic(self, path: str) -> bool:
        return path in self._dynamics

    def get(self):
        return self._data.values()

    def replace(self, path, variable):
        self._data[path] = variable
