# Copyright © The Debusine Developers
# See the AUTHORS file at the top-level directory of this distribution
#
# This file is part of Debusine. It is subject to the license terms
# in the LICENSE file found in the top-level directory of this
# distribution. No part of Debusine, including this file, may be copied,
# modified, propagated, or distributed except according to the terms
# contained in the LICENSE file.

"""Debusine command line interface, workflow template management commands."""

import abc
import argparse
from collections.abc import Iterable
from typing import Any
from urllib.parse import quote

import rich
import rich.json
from rich.table import Table

from debusine.client.commands.base import (
    ModelCommand,
    OptionalInputDataCommand,
    RequiredInputDataCommand,
    WorkspaceCommand,
)
from debusine.client.models import WorkflowTemplateData, WorkflowTemplateDataNew


class WorkflowTemplateCommand(
    ModelCommand[WorkflowTemplateData], WorkspaceCommand, abc.ABC
):
    """Common infrastructure for workflow template commands."""

    def _url(self, instance: WorkflowTemplateData) -> str:
        return self.debusine.webui_url(
            f"/{quote(self.workspace)}/workflow-template/"
            f"{quote(instance.name)}/"
        )

    def _list_rich(self, instances: Iterable[WorkflowTemplateData]) -> None:
        """Print the list as a table."""
        table = Table(box=rich.box.MINIMAL_DOUBLE_HEAD)
        table.add_column("ID")
        table.add_column("Name")
        table.add_column("Task name")
        table.add_column("Priority")
        table.add_column("Task data")
        for instance in instances:
            url = self._url(instance)
            table.add_row(
                f"{instance.id}",
                f"[link={url}]{instance.name}[/]",
                instance.task_name,
                str(instance.priority),
                (
                    rich.json.JSON.from_data(instance.task_data)
                    if instance.task_data
                    else None
                ),
            )
        rich.print(table)

    def _show_rich(self, instance: WorkflowTemplateData) -> None:
        """Show a collection for humans."""
        url = self._url(instance)
        table = Table(box=rich.box.SIMPLE, show_header=False)
        table.add_column("Name", justify="right")
        table.add_row("ID:", f"#{instance.id}")
        table.add_row("Name:", f"[link={url}]{instance.name}[/]")
        table.add_row("Task name:", instance.task_name)
        table.add_row("Priority:", str(instance.priority))
        table.add_row(
            "Task data:", rich.json.JSON.from_data(instance.task_data)
        )
        rich.print(table)


class InputBase(WorkflowTemplateCommand, abc.ABC):
    """Add arguments to input parts of a WorkflowTemplate."""

    @classmethod
    def configure(cls, parser: argparse.ArgumentParser) -> None:
        """Configure the ArgumentParser for this subcommand."""
        super().configure(parser)
        parser.add_argument(
            "--priority",
            type=int,
            default=None,
            help="Base priority for work requests created from this template",
        )

    #: Priority set by the user
    new_priority: int | None

    def __init__(self, args: argparse.Namespace) -> None:
        """Populate new_priority."""
        super().__init__(args)
        self.new_priority = self.args.priority


class CreateBase(RequiredInputDataCommand, InputBase, abc.ABC):
    """Base for both workflow template creation commands."""

    def create_new(self) -> WorkflowTemplateData:
        """Create the new workflow template and return the server response."""
        workflow_template = WorkflowTemplateDataNew(
            name=self.args.template_name,
            task_name=self.args.task_name,
            task_data=self.input_data,
            priority=self.new_priority or 0,
        )
        with self._api_call_or_fail():
            return self.debusine.workflow_template_create(
                self.workspace, workflow_template
            )


class Create(CreateBase, WorkflowTemplateCommand, group="workflow-template"):
    """Create a workflow template."""

    @classmethod
    def configure(cls, parser: argparse.ArgumentParser) -> None:
        """Configure the ArgumentParser for this subcommand."""
        super().configure(parser)
        parser.add_argument(
            "task_name",
            type=str,
            help="Name of the workflow task that the template will use",
        )
        parser.add_argument(
            "template_name", type=str, help="Name of the new workflow template"
        )

    def run(self) -> None:
        """Run the command."""
        created = self.create_new()
        self.show(created)


class LegacyCreate(
    CreateBase,
    name="create-workflow-template",
    deprecated="see `workflow-template create`",
):
    """Create a workflow template."""

    @classmethod
    def configure(cls, parser: argparse.ArgumentParser) -> None:
        """Configure the ArgumentParser for this subcommand."""
        super().configure(parser)
        parser.add_argument(
            "template_name", type=str, help="Name of the new workflow template"
        )
        parser.add_argument(
            "task_name",
            type=str,
            help="Name of the workflow task that the template will use",
        )

    def run(self) -> None:
        """Run the command."""
        created = self.create_new()

        output: dict[str, Any]
        url = self.debusine.webui_url(
            f"/{self.workspace}/workflow-template/{created.name}/"
        )
        output = {
            "result": "success",
            "message": f"Workflow template registered: {url}",
            "workflow_template_id": created.id,
        }
        self.print_yaml_output(output)


class List(WorkflowTemplateCommand, group="workflow-template"):
    """List workflow templates."""

    def run(self) -> None:
        """Run the command."""
        with self._api_call_or_fail():
            instances = self.debusine.workflow_template_iter(self.workspace)
            self.list(instances)


class Show(WorkflowTemplateCommand, group="workflow-template"):
    """Show an existing workflow template."""

    @classmethod
    def configure(cls, parser: argparse.ArgumentParser) -> None:
        """Configure the ArgumentParser for this subcommand."""
        super().configure(parser)
        parser.add_argument(
            "name", type=str, default="_", help="workflow template name or id"
        )

    def run(self) -> None:
        """Run the command."""
        with self._api_call_or_fail():
            instance = self.debusine.workflow_template_get(
                self.workspace, self.args.name
            )
        self.show(instance)


class Manage(OptionalInputDataCommand, InputBase, group="workflow-template"):
    """Configure an existing collection."""

    @classmethod
    def configure(cls, parser: argparse.ArgumentParser) -> None:
        """Configure the ArgumentParser for this subcommand."""
        super().configure(parser)
        parser.add_argument(
            "name", type=str, default="_", help="workflow template name or id"
        )
        parser.add_argument("--rename", type=str, help="rename collection")

    def run(self) -> None:
        """Run the command."""
        with self._api_call_or_fail():
            instance = self.debusine.workflow_template_get(
                self.workspace, self.args.name
            )

        if self.new_priority is not None:
            instance.priority = self.new_priority
        if self.input_data is not None:
            instance.task_data = self.input_data
        if (name := self.args.rename) is not None:
            instance.name = name

        with self._api_call_or_fail():
            instance = self.debusine.workflow_template_update(
                self.workspace, instance
            )

        self.show(instance)
