# Copyright 2008-2023 The Wazo Authors  (see the AUTHORS file)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations

import logging
import re
import sys
from collections.abc import Sequence
from typing import NoReturn

log = logging.getLogger("xivo.xivo_helpers")


find_asterisk_pattern_char = re.compile(r'[\[NXZ!.]').search


def position_of_asterisk_pattern_char(ast_pattern: str) -> int | None:
    if not (mo := find_asterisk_pattern_char(ast_pattern)):
        return None
    return mo.start()


def clean_extension(exten: str | None) -> str:
    """
    Return an extension from an Asterisk extension pattern.
    """
    if exten is None:
        return ""

    exten = str(exten)

    if exten.startswith('_'):
        exten = exten[1:]
        e = position_of_asterisk_pattern_char(exten)
        if e is not None:
            exten = exten[:e]

    return exten


def split_extension(exten: str) -> tuple[str, ...]:
    """
    Return a list of strings that compose the multi parts of an
    extension as to be generated by unsplit_extension().

    >>> split_extension('**142***2666**2***3#**3')
    ('*42', '*666*', '*#*')
    """
    flag = 0
    ret: list[str] = []
    cur = ""
    i = 1

    if not isinstance(exten, str):
        raise ValueError("exten argument must be a string")

    for x in exten:
        if flag == 2:
            if x.isdigit():
                x_int = int(x)
                if x_int == i:
                    flag = 0
                    cur += '*'
                else:
                    raise ValueError(f"Wrong digit: {x_int:d}, excepted: {i:d}")
            elif x == '*':
                ret.append(cur)
                cur = ""
                i += 1
            else:
                raise ValueError(f"Wrong value: {x!r}, excepted digit or asterisk!")
        elif x == '*':
            flag += 1
        elif flag == 1:
            flag = 0
            ret.append(cur)
            cur = x
            i += 1
        else:
            cur += x
    else:
        ret.append(cur)

    return tuple(ret)


def unsplit_extension(xlist: list[str] | tuple[str, ...]) -> str:
    """
    Compute and return an extension from multi extensions.

    >>> unsplit_extension(('*98','666'))
    '**198*666'
    """
    ret = []
    cur = ""

    if not isinstance(xlist, (tuple, list)):
        raise ValueError("Argument must be a tuple or list")

    for i, x in enumerate(xlist):
        i += 1
        for c in x:
            if c == '*':
                cur += f"**{i}"
            else:
                cur += c
        else:
            ret.append(cur)
            cur = ""

    return '*'.join(ret)


def fkey_extension(funckey_prefix: str, funckey_args: Sequence[str]) -> str:
    components = [cleaned for x in funckey_args if (cleaned := clean_extension(x))]
    return clean_extension(funckey_prefix) + unsplit_extension(components)


def abort(message: str, show_tb: bool = False) -> NoReturn:
    """
    Log @message at critical level (including a backtrace
    if @show_tb is true) then exit.
    """
    log.critical(message, exc_info=show_tb)
    sys.exit(1)
