# -*- coding: utf-8 -*-
# Copyright: (c) 2018, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import annotations

DOCUMENTATION = """
    name: su
    short_description: Substitute User
    description:
        - This become plugin allows your remote/login user to execute commands as another user via the su utility.
    author: ansible (@core)
    version_added: "2.8"
    options:
        become_user:
            description: User you 'become' to execute the task
            default: root
            ini:
              - section: privilege_escalation
                key: become_user
              - section: su_become_plugin
                key: user
            vars:
              - name: ansible_become_user
              - name: ansible_su_user
            env:
              - name: ANSIBLE_BECOME_USER
              - name: ANSIBLE_SU_USER
            keyword:
              - name: become_user
        become_exe:
            description: Su executable
            default: su
            ini:
              - section: privilege_escalation
                key: become_exe
              - section: su_become_plugin
                key: executable
            vars:
              - name: ansible_become_exe
              - name: ansible_su_exe
            env:
              - name: ANSIBLE_BECOME_EXE
              - name: ANSIBLE_SU_EXE
            keyword:
              - name: become_exe
        become_flags:
            description: Options to pass to su
            default: ''
            ini:
              - section: privilege_escalation
                key: become_flags
              - section: su_become_plugin
                key: flags
            vars:
              - name: ansible_become_flags
              - name: ansible_su_flags
            env:
              - name: ANSIBLE_BECOME_FLAGS
              - name: ANSIBLE_SU_FLAGS
            keyword:
              - name: become_flags
        become_pass:
            description: Password to pass to su
            required: False
            vars:
              - name: ansible_become_password
              - name: ansible_become_pass
              - name: ansible_su_pass
            env:
              - name: ANSIBLE_BECOME_PASS
              - name: ANSIBLE_SU_PASS
            ini:
              - section: su_become_plugin
                key: password
        prompt_l10n:
            description:
                - List of localized strings to match for prompt detection
                - If empty we'll use the built in one
                - Do NOT add a colon (:) to your custom entries. Ansible adds a colon at the end of each prompt;
                  if you add another one in your string, your prompt will fail with a "Timeout" error.
            default: []
            type: list
            elements: string
            ini:
              - section: su_become_plugin
                key: localized_prompts
            vars:
              - name: ansible_su_prompt_l10n
            env:
              - name: ANSIBLE_SU_PROMPT_L10N
"""

import re
import shlex

from ansible.module_utils.common.text.converters import to_text
from ansible.plugins.become import BecomeBase


class BecomeModule(BecomeBase):

    name = 'su'

    pipelining = False

    # messages for detecting prompted password issues
    fail = ('Authentication failure',)

    SU_PROMPT_LOCALIZATIONS = [
        'Password',
        '암호',
        'パスワード',
        'Adgangskode',
        'Contraseña',
        'Contrasenya',
        'Hasło',
        'Heslo',
        'Jelszó',
        'Lösenord',
        'Mật khẩu',
        'Mot de passe',
        'Parola',
        'Parool',
        'Pasahitza',
        'Passord',
        'Passwort',
        'Salasana',
        'Sandi',
        'Senha',
        'Wachtwoord',
        'ססמה',
        'Лозинка',
        'Парола',
        'Пароль',
        'गुप्तशब्द',
        'शब्दकूट',
        'సంకేతపదము',
        'හස්පදය',
        '密码',
        '密碼',
        '口令',
    ]

    def check_password_prompt(self, b_output: bytes) -> bool:
        """ checks if the expected password prompt exists in b_output """
        prompts = self.get_option('prompt_l10n') or self.SU_PROMPT_LOCALIZATIONS
        password_prompt_strings = "|".join(re.escape(p) for p in prompts)
        # Colon or unicode fullwidth colon
        prompt_pattern = rf"(?:{password_prompt_strings})\s*[:：]"
        match = re.search(prompt_pattern, to_text(b_output), flags=re.IGNORECASE)

        if match:
            self.prompt = match.group(0)  # preserve the actual matched string so we can scrub the output

        return bool(match)

    def build_become_command(self, cmd, shell):
        super(BecomeModule, self).build_become_command(cmd, shell)

        # Prompt handling for ``su`` is more complicated, this
        # is used to satisfy the connection plugin
        self.prompt = True

        if not cmd:
            return cmd

        exe = self.get_option('become_exe') or self.name
        flags = self.get_option('become_flags') or ''
        user = self.get_option('become_user') or ''
        success_cmd = self._build_success_command(cmd, shell)

        return "%s %s %s -c %s" % (exe, flags, user, shlex.quote(success_cmd))
