import logging
import re
from PySubtitle.Helpers.unicode_utils import is_latin
from enum import Enum

from PySubtitle.Helpers import GetValueFromName

class Substitutions:
    """
    Helper class to perform textual substitutions based on a dictionary of (before,after) pairs.
    """
    class Mode(Enum):
        Auto = 0
        WholeWords = 1
        PartialWords = 2

        def serialize(self):
            return self.name

    template_wholewords = r"\b{}\b"
    template_partialwords = r"{}"
    # template_automatic is not used directly; custom logic is used for Auto mode

    def __init__(self, substitutions : dict | list | str, mode : Mode = Mode.Auto):
        self._patterns = None
        self._mode = self._parse_mode(mode)
        self.substitutions = substitutions

    @property
    def mode(self) -> Mode:
        return self._mode

    @mode.setter
    def mode(self, mode : Mode | int | str):
        self._mode = self._parse_mode(mode)
        self._patterns = None

    @property
    def substitutions(self) -> dict:
        return self._substitutions

    @substitutions.setter
    def substitutions(self, substitutions : dict | list | str):
        self._substitutions = Substitutions.Parse(substitutions) if substitutions else {}
        self._patterns = None


    @property
    def patterns(self):
        if self._patterns is None:
            self._patterns = self._compile_patterns()
        return self._patterns

    def PerformSubstitutions(self, input : list | str):
        """
        Try to substitute all (before,after) pairs in an input string

        :param input: string to perform substitutions on.
        :return: a string with the substitutions performed.
        """
        result = str(input)
        if self.mode == self.Mode.Auto:
            for before, after in self.substitutions.items():
                result = self._auto_substitute(result, before, after)
        else:
            for pattern, substitution in self.patterns:
                result = pattern.sub(substitution, result)
        return result

    def _auto_substitute(self, text, before, after):
        # Substitute 'before' with 'after' only when not surrounded by Latin chars
        result = []
        i = 0
        n = len(text)
        blen = len(before)
        while i <= n - blen:
            if text[i:i+blen] == before:
                prev_char = text[i-1] if i > 0 else ''
                next_char = text[i+blen] if i+blen < n else ''
                if not (is_latin(prev_char) or is_latin(next_char)):
                    result.append(after)
                    i += blen
                    continue
            result.append(text[i])
            i += 1
        result.extend(text[i:])
        return ''.join(result)

    def PerformSubstitutionsOnAll(self, input : list):
        """
        Try to substitute all (before,after) pairs in a list of strings.

        :param input: list of strings to perform substitutions on.
        :return: a list of strings with the substitutions performed, along with a dictionary of (before,after) pairs for each element that had substitutions.
        """
        result = [ self.PerformSubstitutions(line) for line in input ]
        replacements = { line: new_line for line, new_line in zip(input, result) if new_line != str(line) }
        return result, replacements

    def _compile_patterns(self):
        patterns = []
        template = self._get_template()
        for before, after in self.substitutions.items():
            substitution = template.format(re.escape(before))
            pattern = re.compile(substitution, flags=re.UNICODE)
            patterns.append((pattern, after))
        return patterns

    def _get_template(self):
        if self.mode == Substitutions.Mode.WholeWords:
            return self.template_wholewords
        elif self.mode == Substitutions.Mode.PartialWords:
            return self.template_partialwords
        else:
            return self.template_automatic

    def _parse_mode(self, mode):
        if isinstance(mode, self.Mode):
            return mode

        if isinstance(mode, int):
            try:
                return self.Mode(mode)
            except ValueError:
                raise ValueError(f"No enum member for value: {mode}")

        return GetValueFromName(mode, self.Mode)

    @classmethod
    def Parse(cls, sub_list : str | list[str] | dict, separator="::") -> dict:
        """
        Parse a list of (before,after) pairs from a string, dictionary or list of strings, or a file containing such pairs.

        :param sub_list: is assumed to be a list of (before,after) pairs separated by the separator ("::" by default).

        :return: a dictionary of (before,after) pairs
        """
        if not sub_list:
            return {}

        if isinstance(sub_list, dict):
            return sub_list

        if isinstance(sub_list, str):
            import re
            sub_list = re.split("[\n,]", sub_list)

        if isinstance(sub_list, list):
            substitutions = {}
            for sub in sub_list:
                if "::" in sub:
                    before, after = sub.split(separator)
                    substitutions[before] = after
                elif sub.strip():
                    try:
                        with open(sub, "r", encoding="utf-8", newline='') as f:
                            for line in [line.strip() for line in f if line.strip()]:
                                if "::" in line:
                                    before, after = line.split("::")
                                    substitutions[before] = after
                                else:
                                    raise ValueError(f"Invalid substitution format in {sub}: {line}")

                    except FileNotFoundError:
                        logging.warning(f"Substitution file not found: {sub}")
                    except ValueError:
                        raise

            return substitutions

        return {}