"""Mariner tracker management commands."""

import logging

from cliff import command, lister, show

from mariner import exceptions
from mariner.utils import color


def _format_link(links: list[str]) -> str:
    """Format a links list as a single summary string."""
    match links:
        case []:
            return ""
        case [single]:
            return single
        case [first, *_]:
            return f"{first}, ..."
        case _:
            return ""


class Add(command.Command):
    """Add a tracker from a Jackett definition."""

    log = logging.getLogger(__name__)

    def get_parser(self, prog_name):
        """Add arguments to argument parser.

        Args:
            prog_name: Application name.

        Returns:
            Instance of argument parser.

        """
        parser = super().get_parser(prog_name)
        parser.add_argument("name", help="Name of the tracker to add")
        parser.add_argument(
            "--url",
            default=None,
            help="Custom URL to download the definition from",
        )
        parser.add_argument(
            "--yes",
            "-y",
            action="store_true",
            help="Skip confirmation prompts",
        )
        return parser

    def take_action(self, parsed_args):
        """Download and add a tracker definition.

        Args:
            parsed_args: List of parsed arguments.

        """
        name = parsed_args.name
        registry = self.app.engine._registry

        existing = registry._trackers.get(name)
        if existing and not parsed_args.yes:
            answer = input(
                f"Tracker {name!r} already exists ({existing.source})."
                " Overwrite? [y/N] ",
            )
            if answer.lower() not in ("y", "yes"):
                self.log.info("Aborted.")
                return

        self.log.warning(
            "Adding third-party trackers may not work correctly."
            " Only trackers shipped with Mariner have been tested.",
        )

        try:
            info = self.app._loop.run_until_complete(
                registry.add(name, url=parsed_args.url),
            )
        except exceptions.HTTPError as e:
            self.log.error("Failed to download tracker: %s", e)
            return

        self.log.info(
            "Added tracker %s.",
            color.green(info.definition.name),
        )


class List(lister.Lister):
    """List all available trackers."""

    log = logging.getLogger(__name__)

    def take_action(self, parsed_args):
        """List all trackers.

        Args:
            parsed_args: List of parsed arguments.

        Returns:
            Tuple of headers and rows.

        """
        registry = self.app.engine._registry
        headers = ("ID", "Name", "Source", "Link")
        colored_headers = [color.cyan(h) for h in headers]

        rows = []
        for tracker_id in sorted(registry.available_trackers):
            info = registry.get_info(tracker_id)
            defn = info.definition
            rows.append(
                (
                    tracker_id,
                    defn.name,
                    info.source,
                    _format_link(defn.spec.links),
                )
            )
        return (colored_headers, rows)


class Remove(command.Command):
    """Remove a user-added tracker."""

    log = logging.getLogger(__name__)

    def get_parser(self, prog_name):
        """Add arguments to argument parser.

        Args:
            prog_name: Application name.

        Returns:
            Instance of argument parser.

        """
        parser = super().get_parser(prog_name)
        parser.add_argument("name", help="Name of the tracker to remove")
        return parser

    def take_action(self, parsed_args):
        """Remove a tracker definition.

        Args:
            parsed_args: List of parsed arguments.

        """
        name = parsed_args.name
        registry = self.app.engine._registry

        try:
            registry.remove(name)
        except exceptions.TrackerNotFoundError as e:
            self.log.error("%s", e)
            return
        except exceptions.TrackerNotUserManagedError as e:
            self.log.error("%s", e)
            return

        self.log.info("Removed tracker %s.", color.yellow(name))


class Update(command.Command):
    """Update a user-added tracker from its source URL."""

    log = logging.getLogger(__name__)

    def get_parser(self, prog_name):
        """Add arguments to argument parser.

        Args:
            prog_name: Application name.

        Returns:
            Instance of argument parser.

        """
        parser = super().get_parser(prog_name)
        parser.add_argument(
            "name",
            nargs="?",
            default=None,
            help="Name of the tracker to update",
        )
        parser.add_argument(
            "--all",
            action="store_true",
            help="Update all trackers",
        )
        return parser

    def take_action(self, parsed_args):
        """Update tracker definition(s).

        Args:
            parsed_args: List of parsed arguments.

        """
        registry = self.app.engine._registry
        update_all = parsed_args.all

        if not parsed_args.name and not update_all:
            self.log.error(
                "Specify a tracker name or use --all to update all.",
            )
            return

        if update_all:
            tracker_ids = registry.available_trackers
            if not tracker_ids:
                self.log.warning("No trackers to update.")
                return
        else:
            tracker_ids = [parsed_args.name]

        for tracker_id in tracker_ids:
            try:
                info = self.app._loop.run_until_complete(
                    registry.update(tracker_id),
                )
                self.log.info(
                    "Updated tracker %s.",
                    color.green(info.definition.name),
                )
            except exceptions.TrackerNotFoundError as e:
                self.log.error("%s", e)
            except exceptions.HTTPError as e:
                self.log.error(
                    "Failed to update %s: %s",
                    tracker_id,
                    e,
                )


class Show(show.ShowOne):
    """Show details of a tracker."""

    log = logging.getLogger(__name__)

    def get_parser(self, prog_name):
        """Add arguments to argument parser.

        Args:
            prog_name: Application name.

        Returns:
            Instance of argument parser.

        """
        parser = super().get_parser(prog_name)
        parser.add_argument("name", help="Name of the tracker to show")
        return parser

    def take_action(self, parsed_args):
        """Show tracker details.

        Args:
            parsed_args: List of parsed arguments.

        Returns:
            Tuple of column names and values.

        """
        name = parsed_args.name
        registry = self.app.engine._registry
        info = registry.get_info(name)
        defn = info.definition

        columns = {
            "ID": defn.id,
            "Name": defn.name,
            "Description": defn.description,
            "Source": info.source,
            "Links": ", ".join(defn.spec.links),
            "Request Delay": f"{defn.spec.request_delay}s",
        }

        if info.source == "user":
            metadata = registry._load_metadata().get(name)
            if metadata:
                columns["Source URL"] = metadata.source_url
                columns["Added"] = metadata.added_at
                columns["Last Updated"] = metadata.updated_at

        colored_keys = [color.cyan(k) for k in columns]
        return (colored_keys, list(columns.values()))
