# Copyright (C) 2024 Collabora Limited
#
# Author: Igor Ponomarev <igor.ponomarev@collabora.com>
#
# SPDX-License-Identifier: GPL-2.0-or-later
from __future__ import annotations

from pathlib import Path
from shutil import which
from tempfile import TemporaryDirectory
from time import monotonic as time_monotonic
from unittest.mock import patch

from lava_common.exceptions import JobError
from lava_dispatcher.action import Action

from .test_basic import LavaDispatcherTestCase


class TestActionRunCmd(LavaDispatcherTestCase):
    def setUp(self) -> None:
        self.action = Action(self.create_job_mock())

    def test_simple_command_with_args(self) -> None:
        with self.collect_lava_logs(self.action) as logs:
            ret = self.action.run_cmd(["printf", "Hello, world!"])

        self.assertEqual(ret, 0)
        self.assertEqual(logs[1], ("DEBUG", ">> %s", ["Hello, world!"]))

    def test_no_args_command_with_spaces(self) -> None:
        true_path = which("true")
        with TemporaryDirectory() as tmpdir:
            tmpdir_path = Path(tmpdir)
            symlink_with_spaces_to_true = tmpdir_path / "with spaces true"
            symlink_with_spaces_to_true.symlink_to(true_path)

            ret = self.action.run_cmd([str(symlink_with_spaces_to_true)])

        self.assertEqual(ret, 0)

    def test_command_timeout(self) -> None:
        self.action.timeout.duration = 0.01

        start_time = time_monotonic()

        with self.assertRaises(self.action.command_exception), self.collect_lava_logs(
            self.action
        ) as error_logs:
            self.action.run_cmd(["sleep", "10"])

        end_time = time_monotonic()

        self.assertEqual(
            error_logs[-1],
            ("ERROR", "Unable to run: %r", [["sleep", "10"]]),
        )

        self.assertLess(
            end_time - start_time,
            1.0,
        )

    def test_command_timeout_ignores_sigterm(self) -> None:
        self.action.timeout.duration = 0.01

        start_time = time_monotonic()

        with self.assertRaises(self.action.command_exception), self.collect_lava_logs(
            self.action
        ) as debug_logs, patch.object(Action, "_SUBPROCESS_SIGTERM_TIMEOUT", 0.01):
            self.action.run_cmd(
                [
                    "sh",
                    "-c",
                    "trap 'echo IGNORING_SIGTERM' TERM KILL;sleep 10",
                ]
            )

        end_time = time_monotonic()

        self.assertEqual(
            debug_logs[-1],
            (
                "ERROR",
                "Unable to run: %r",
                [["sh", "-c", "trap 'echo IGNORING_SIGTERM' TERM KILL;sleep 10"]],
            ),
        )

        self.assertLess(end_time - start_time, 1.0)

    def test_command_does_not_exist(self) -> None:
        non_existant_command = "THIS_COMMAND_does_NOT_exist"

        with self.assertRaises(self.action.command_exception), self.collect_lava_logs(
            self.action
        ) as error_logs:
            self.action.run_cmd([non_existant_command])

        self.assertEqual(
            error_logs[-1],
            ("ERROR", "Unable to run: %r", [["THIS_COMMAND_does_NOT_exist"]]),
        )


class TestParsedCommand(LavaDispatcherTestCase):
    def setUp(self) -> None:
        self.action = Action(self.create_job_mock())

    def test_parsed_command_fail(self) -> None:
        with self.assertRaises(JobError):
            self.action.parsed_command(["bash", "-c", "echo test && false"])

        self.assertEqual("test", self.action.results["output"])
        self.assertEqual("1", self.action.results["returncode"])

    def test_parsed_command_input(self) -> None:
        self.assertEqual(
            "Hello, world!", self.action.parsed_command(["cat"], input="Hello, world!")
        )

    def test_unicode_escape(self) -> None:
        self.action.parsed_command(["printf", r"\xc3\x28"])

    def test_env(self) -> None:
        output = self.action.parsed_command(
            ["sh", "-c", "echo $FOOBAR"], env={"FOOBAR": "test"}
        )
        self.assertEqual(output, "test\n")
