# 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, workspace management commands."""

import argparse
import sys
from typing import Any, override

from debusine.client.commands.base import WorkspaceCommand
from debusine.client.exceptions import DebusineError
from debusine.client.models import WorkspaceInheritanceChain
from debusine.utils.input import YamlEditor


class Inheritance(WorkspaceCommand, group="workspace"):
    """Query and change the inheritance chain of a workspace."""

    def __init__(
        self, parser: argparse.ArgumentParser, args: argparse.Namespace
    ) -> None:
        """Handle workspace also as a (deprecated) positional argument."""
        super().__init__(parser, args)
        # Override with the workspace given as positional argument, if present
        if self.args.workspace_arg:
            self.workspace = self.args.workspace_arg

    @override
    @classmethod
    def configure(cls, parser: argparse.ArgumentParser) -> None:
        super().configure(parser)
        parser.add_argument("workspace_arg", nargs="?", help="workspace name")
        parser.add_argument(
            "--append",
            type=str,
            nargs="+",
            help="workspaces (id, 'name', or 'scope/name')"
            " to add to the end of the parent list",
        )
        parser.add_argument(
            "--prepend",
            type=str,
            nargs="+",
            help="workspaces (id, 'name', or 'scope/name')"
            " to add to the beginning of the parent list",
        )
        parser.add_argument(
            "--remove",
            type=str,
            nargs="+",
            help="workspaces (id, 'name', or 'scope/name')"
            " to remove from the parent list",
        )
        parser.add_argument(
            "--set",
            type=str,
            nargs="+",
            help="workspaces (id, 'name', or 'scope/name')"
            " that compose the new parent list",
        )
        parser.add_argument(
            "--edit",
            action="store_true",
            help="edit the parent list using $EDITOR",
        )

    def _apply_inheritance(
        self,
        *,
        workspace: str,
        chain: WorkspaceInheritanceChain,
    ) -> WorkspaceInheritanceChain:
        """
        Set inheritance.

        If API call fails because of an error not caused by the parameters
         fail.

        :raises DebusineError: if an error setting the inheritance happens
          (e.g. invalid chain because workspace does not exist).
        """
        with self._api_call_or_fail():
            return self.debusine.set_workspace_inheritance(
                workspace, chain=chain
            )

    def _edit_and_apply_inheritance(
        self,
        workspace: str,
        current: WorkspaceInheritanceChain,
    ) -> WorkspaceInheritanceChain | None:
        """Run the interactive YAML edit flow and return new inheritance."""
        editor: YamlEditor[list[Any]] = YamlEditor(
            current.model_dump()["chain"],
            help_text=[
                "Here you can configure the inheritance"
                f" of workspace '{workspace}'.",
                "",
                "This is a YAML list of dictionaries with"
                " 'id/scope/workspace' keys.",
                "Usually you specify the workspace,"
                " and optionally the scope. The scope",
                "is only required to refer to a workspace"
                " outside of the current scope.",
                "",
                "For example:",
                "",
                "- workspace: base",
                "- scope: some-other-scope",
                "  workspace: foobar",
            ],
        )

        return editor.edit_apply_loop(
            apply=lambda new: self._apply_inheritance(
                workspace=workspace, chain=WorkspaceInheritanceChain(chain=new)
            ),
            apply_exception_class=DebusineError,
            print_exception=self.print_server_error,
            content_description="workspace inheritance",
        )

    @override
    def run(self) -> None:
        workspace = self.workspace
        with self._api_call_or_fail():
            try:
                current = self.debusine.get_workspace_inheritance(workspace)
            except DebusineError as err:
                output = {"result": "failure", "error": err.asdict()}
                self._print_yaml(output, file=sys.stderr)
                raise SystemExit(3)

        new = current
        if self.args.append:
            new = new + WorkspaceInheritanceChain.from_strings(self.args.append)
        if self.args.prepend:
            new = (
                WorkspaceInheritanceChain.from_strings(self.args.prepend) + new
            )
        if self.args.remove:
            new = new - WorkspaceInheritanceChain.from_strings(self.args.remove)
        if self.args.set:
            new = WorkspaceInheritanceChain.from_strings(self.args.set)
        if self.args.edit:
            result_applied = self._edit_and_apply_inheritance(
                workspace, current
            )
            to_print = result_applied if result_applied is not None else current
            self._print_yaml(to_print.model_dump())
            return

        if new != current:
            try:
                new = self._apply_inheritance(workspace=workspace, chain=new)
            except DebusineError as error:
                print("Error setting inheritance:\n", file=sys.stderr)
                self.print_server_error(error)
                raise SystemExit(3)

        self._print_yaml(new.model_dump())


class LegacyInheritance(
    Inheritance,
    name="workspace-inheritance",
    deprecated="see `workspace inheritance`",
):
    """Query and change the inheritance chain of a workspace."""
