"""IgnoreErrorsRule used with ansible-lint."""
from __future__ import annotations

import sys
from typing import TYPE_CHECKING

from ansiblelint.rules import AnsibleLintRule

if TYPE_CHECKING:
    from ansiblelint.file_utils import Lintable
    from ansiblelint.utils import Task


class IgnoreErrorsRule(AnsibleLintRule):
    """Use failed_when and specify error conditions instead of using ignore_errors."""

    id = "ignore-errors"
    description = (
        "Instead of ignoring all errors, ignore the errors only when using ``{{ ansible_check_mode }}``, "
        "register the errors using ``register``, "
        "or use ``failed_when:`` and specify acceptable error conditions "
        "to reduce the risk of ignoring important failures."
    )
    severity = "LOW"
    tags = ["unpredictability"]
    version_added = "v5.0.7"

    def matchtask(
        self,
        task: Task,
        file: Lintable | None = None,
    ) -> bool | str:
        if (
            task.get("ignore_errors")
            and task.get("ignore_errors") != "{{ ansible_check_mode }}"
            and not task.get("register")
        ):
            return True

        return False


if "pytest" in sys.modules:
    import pytest

    if TYPE_CHECKING:
        from ansiblelint.testing import RunFromText  # pylint: disable=ungrouped-imports

    IGNORE_ERRORS_TRUE = """
- hosts: all
  tasks:
    - name: Run apt-get update
      command: apt-get update
      ignore_errors: true
"""

    IGNORE_ERRORS_FALSE = """
- hosts: all
  tasks:
    - name: Run apt-get update
      command: apt-get update
      ignore_errors: false
"""

    IGNORE_ERRORS_CHECK_MODE = """
- hosts: all
  tasks:
    - name: Run apt-get update
      command: apt-get update
      ignore_errors: "{{ ansible_check_mode }}"
"""

    IGNORE_ERRORS_REGISTER = """
- hosts: all
  tasks:
    - name: Run apt-get update
      command: apt-get update
      ignore_errors: true
      register: ignore_errors_register
"""

    FAILED_WHEN = """
- hosts: all
  tasks:
    - name: Disable apport
      become: 'yes'
      lineinfile:
        line: "enabled=0"
        dest: /etc/default/apport
        mode: 0644
        state: present
      register: default_apport
      failed_when: default_apport.rc !=0 and not default_apport.rc == 257
"""

    @pytest.mark.parametrize(
        "rule_runner",
        (IgnoreErrorsRule,),
        indirect=["rule_runner"],
    )
    def test_ignore_errors_true(rule_runner: RunFromText) -> None:
        """The task uses ignore_errors."""
        results = rule_runner.run_playbook(IGNORE_ERRORS_TRUE)
        assert len(results) == 1

    @pytest.mark.parametrize(
        "rule_runner",
        (IgnoreErrorsRule,),
        indirect=["rule_runner"],
    )
    def test_ignore_errors_false(rule_runner: RunFromText) -> None:
        """The task uses ignore_errors: false, oddly enough."""
        results = rule_runner.run_playbook(IGNORE_ERRORS_FALSE)
        assert len(results) == 0

    @pytest.mark.parametrize(
        "rule_runner",
        (IgnoreErrorsRule,),
        indirect=["rule_runner"],
    )
    def test_ignore_errors_check_mode(rule_runner: RunFromText) -> None:
        """The task uses ignore_errors: "{{ ansible_check_mode }}"."""
        results = rule_runner.run_playbook(IGNORE_ERRORS_CHECK_MODE)
        assert len(results) == 0

    @pytest.mark.parametrize(
        "rule_runner",
        (IgnoreErrorsRule,),
        indirect=["rule_runner"],
    )
    def test_ignore_errors_register(rule_runner: RunFromText) -> None:
        """The task uses ignore_errors: but output is registered and managed."""
        results = rule_runner.run_playbook(IGNORE_ERRORS_REGISTER)
        assert len(results) == 0

    @pytest.mark.parametrize(
        "rule_runner",
        (IgnoreErrorsRule,),
        indirect=["rule_runner"],
    )
    def test_failed_when(rule_runner: RunFromText) -> None:
        """Instead of ignore_errors, this task uses failed_when."""
        results = rule_runner.run_playbook(FAILED_WHEN)
        assert len(results) == 0
