# Copyright (C) 2018 Linaro Limited
#
# Author: Remi Duraffort <remi.duraffort@linaro.org>
#
# SPDX-License-Identifier: GPL-2.0-or-later
from __future__ import annotations

from typing import TYPE_CHECKING

from lava_common.exceptions import InfrastructureError
from lava_common.yaml import yaml_safe_dump
from lava_dispatcher.action import Action, Pipeline
from lava_dispatcher.actions.deploy.download import DownloaderAction
from lava_dispatcher.actions.deploy.environment import DeployDeviceEnvironment
from lava_dispatcher.actions.deploy.overlay import OverlayAction
from lava_dispatcher.logical import RetryAction
from lava_dispatcher.utils.strings import substitute

if TYPE_CHECKING:
    from lava_dispatcher.job import Job


class FlasherRetryAction(RetryAction):
    name = "deploy-flasher-retry"
    description = "deploy flasher with retry"
    summary = "deploy custom flasher"

    def populate(self, parameters):
        self.pipeline = Pipeline(parent=self, job=self.job, parameters=parameters)
        self.pipeline.add_action(FlasherAction(self.job))


class FlasherAction(Action):
    name = "deploy-flasher"
    description = "deploy flasher"
    summary = "deploy custom flasher"

    command_exception = InfrastructureError

    def __init__(self, job: Job):
        super().__init__(job)
        self.commands = []
        self.path = None

    def validate(self):
        super().validate()
        method = self.job.device["actions"]["deploy"]["methods"]["flasher"]
        self.commands = method.get("commands")
        if not isinstance(self.commands, list):
            self.errors = "'commands' should be a list"

    def populate(self, parameters):
        self.pipeline = Pipeline(parent=self, job=self.job, parameters=parameters)

        if self.test_needs_overlay(parameters):
            self.pipeline.add_action(OverlayAction(self.job))

        # Download the images
        self.path = self.mkdtemp()
        uniquify = parameters.get("uniquify", True)
        for image in parameters["images"].keys():
            # Skip download if 'download: false`
            if not parameters["images"][image].get("download", True):
                continue
            self.pipeline.add_action(
                DownloaderAction(
                    self.job,
                    image,
                    self.path,
                    params=parameters["images"][image],
                    uniquify=uniquify,
                )
            )

        if self.test_needs_deployment(parameters):
            self.pipeline.add_action(DeployDeviceEnvironment(self.job))

    def run(self, connection, max_end_time):
        connection = super().run(connection, max_end_time)
        # Substitute in the device commands
        substitutions = {}
        for key in self.parameters["images"].keys():
            # Use the URL directly as the substitution value if 'download:false'
            if not self.parameters["images"][key].get("download", True):
                substitutions["{%s}" % key.upper()] = self.parameters["images"][key][
                    "url"
                ]
            else:
                filename = self.get_namespace_data(
                    action="download-action", label=key, key="file"
                )
                filename = filename[len(self.path) + 1 :]
                substitutions["{%s}" % key.upper()] = filename

        # Add power commands
        substitutions["{HARD_RESET_COMMAND}"] = str(self.job.device.hard_reset_command)
        substitutions["{SOFT_REBOOT_COMMAND}"] = str(
            self.job.device.soft_reboot_command
        )
        substitutions["{PRE_OS_COMMAND}"] = str(self.job.device.pre_os_command)
        if self.job.device.pre_os_command is None:
            substitutions["{PRE_OS_COMMAND}"] = ""
        substitutions["{PRE_POWER_COMMAND}"] = str(self.job.device.pre_power_command)
        if self.job.device.pre_power_command is None:
            substitutions["{PRE_POWER_COMMAND}"] = ""
        substitutions["{POWER_ON_COMMAND}"] = str(self.job.device.power_command)
        substitutions["{POWER_OFF_COMMAND}"] = str(
            self.job.device.get("commands", {}).get("power_off", "")
        )

        # Add some device configuration
        substitutions["{DEVICE_INFO}"] = yaml_safe_dump(
            self.job.device.get("device_info", [])
        )
        substitutions["{STATIC_INFO}"] = yaml_safe_dump(
            self.job.device.get("static_info", [])
        )

        # Run the commands
        for cmd in self.commands:
            cmds = substitute([cmd], substitutions)
            self.run_cmd(cmds[0], error_msg="Unable to flash the device", cwd=self.path)

        return connection
