# Copyright (c) Ansible project
# Simplified BSD License (see LICENSES/BSD-2-Clause.txt or https://opensource.org/licenses/BSD-2-Clause)
# SPDX-License-Identifier: BSD-2-Clause

# Note that this module util is **PRIVATE** to the collection. It can have breaking changes at any time.
# Do not use this from other collections or standalone plugins/modules!

from __future__ import annotations

import os
import re
import typing as t

if t.TYPE_CHECKING:
    from ansible.module_utils.basic import AnsibleModule


def _create_regex_group_complement(s: str) -> re.Pattern:
    lines = (line.strip() for line in s.split("\n") if line.strip())
    chars = [_f for _f in (line.split("#")[0].strip() for line in lines) if _f]
    group = rf"[^{''.join(chars)}]"
    return re.compile(group)


class HomebrewValidate:
    # class regexes ------------------------------------------------ {{{
    VALID_PATH_CHARS = rf"""
        \w                  # alphanumeric characters (i.e., [a-zA-Z0-9_])
        \s                  # spaces
        :                   # colons
        {os.path.sep}       # the OS-specific path separator
        .                   # dots
        \-                  # dashes
    """

    VALID_BREW_PATH_CHARS = rf"""
        \w                  # alphanumeric characters (i.e., [a-zA-Z0-9_])
        \s                  # spaces
        {os.path.sep}       # the OS-specific path separator
        .                   # dots
        \-                  # dashes
    """

    VALID_PACKAGE_CHARS = r"""
        \w                  # alphanumeric characters (i.e., [a-zA-Z0-9_])
        .                   # dots
        /                   # slash (for taps)
        \+                  # plusses
        \-                  # dashes
        :                   # colons (for URLs)
        @                   # at-sign
    """

    INVALID_PATH_REGEX = _create_regex_group_complement(VALID_PATH_CHARS)
    INVALID_BREW_PATH_REGEX = _create_regex_group_complement(VALID_BREW_PATH_CHARS)
    INVALID_PACKAGE_REGEX = _create_regex_group_complement(VALID_PACKAGE_CHARS)
    # /class regexes ----------------------------------------------- }}}

    # class validations -------------------------------------------- {{{
    @classmethod
    def valid_path(cls, path: list[str] | str) -> bool:
        """
        `path` must be one of:
         - list of paths
         - a string containing only:
             - alphanumeric characters
             - dashes
             - dots
             - spaces
             - colons
             - os.path.sep
        """

        if isinstance(path, str):
            return not cls.INVALID_PATH_REGEX.search(path)

        try:
            iter(path)
        except TypeError:
            return False
        else:
            paths = path
            return all(cls.valid_brew_path(path_) for path_ in paths)

    @classmethod
    def valid_brew_path(cls, brew_path: str | None) -> bool:
        """
        `brew_path` must be one of:
         - None
         - a string containing only:
             - alphanumeric characters
             - dashes
             - dots
             - spaces
             - os.path.sep
        """

        if brew_path is None:
            return True

        return isinstance(brew_path, str) and not cls.INVALID_BREW_PATH_REGEX.search(brew_path)

    @classmethod
    def valid_package(cls, package: str | None) -> bool:
        """A valid package is either None or alphanumeric."""

        if package is None:
            return True

        return isinstance(package, str) and not cls.INVALID_PACKAGE_REGEX.search(package)


def parse_brew_path(module: AnsibleModule) -> str:
    """Attempt to find the Homebrew executable path.

    Requires:
        - module has a `path` parameter
        - path is a valid path string for the target OS. Otherwise, module.fail_json()
          is called with msg="Invalid_path: <path>".
    """
    path = module.params["path"]
    if not HomebrewValidate.valid_path(path):
        module.fail_json(msg=f"Invalid path: {path}")

    if isinstance(path, str):
        paths = path.split(":")
    elif isinstance(path, list):
        paths = path
    else:
        module.fail_json(msg=f"Invalid path: {path}")

    brew_path = module.get_bin_path("brew", required=True, opt_dirs=paths)
    if not HomebrewValidate.valid_brew_path(brew_path):
        module.fail_json(msg=f"Invalid brew path: {brew_path}")

    return brew_path
