from __future__ import annotations

from importlib import import_module
from typing import Any, Dict, List, Sequence
import sys

from SYS.logger import log
from . import _shared as sh

Cmdlet = sh.Cmdlet
CmdletArg = sh.CmdletArg
SharedArgs = sh.SharedArgs


class File(Cmdlet):
    """Unified file command: file -search|-add|-delete|-download|-merge|..."""

    _ACTION_FLAGS = {
        "search": {"-search", "--search"},
        "add": {"-add", "--add"},
        "delete": {"-delete", "--delete", "-del", "--del"},
        "merge": {"-merge", "--merge"},
        "download": {"-download", "--download", "-dl", "--dl"},
        "convert": {"-convert", "--convert"},
        "trim": {"-trim", "--trim"},
        "archive": {"-archive", "--archive"},
        "screenshot": {"-screenshot", "--screenshot", "-screen-shot", "--screen-shot", "-shot", "--shot"},
    }

    _ACTION_MODULE = {
        "add": "cmdlet.file.add",
        "delete": "cmdlet.file.delete",
        "merge": "cmdlet.file.merge",
        "download": "cmdlet.file.download",
        "search": "cmdlet.file.search",
        "convert": "cmdlet.file.convert",
        "trim": "cmdlet.file.trim",
        "archive": "cmdlet.file.archive",
        "screenshot": "cmdlet.file.screenshot",
    }

    def __init__(self) -> None:
        super().__init__(
            name="file",
            summary="Manage file operations with one command",
            usage='file -query <query> [args] | file (-search|-add|-delete|-merge|-download|-convert|-trim|-archive|-screenshot) [args]',
            arg=[
                SharedArgs.QUERY,
                SharedArgs.PLUGIN,
                SharedArgs.INSTANCE,
                CmdletArg("-search", type="flag", required=False, description="Run search-file"),
                CmdletArg("-add", type="flag", required=False, description="Run add-file"),
                CmdletArg("-delete", type="flag", required=False, description="Run delete-file", alias="del"),
                CmdletArg("-merge", type="flag", required=False, description="Run merge-file"),
                CmdletArg("-download", type="flag", required=False, description="Run download-file", alias="dl"),
                CmdletArg("-convert", type="flag", required=False, description="Run convert-file"),
                CmdletArg("-trim", type="flag", required=False, description="Run trim-file"),
                CmdletArg("-archive", type="flag", required=False, description="Run archive-file"),
                CmdletArg("-screenshot", type="flag", required=False, description="Run screen-shot", alias="shot"),
            ],
            detail=[
                "- Use -search for explicit search mode, then add -plugin/-instance and -query as needed.",
                "- Plain -query still routes to search-file for direct search entry.",
                "- Otherwise, exactly one non-search action flag is required.",
                "- Remaining args are passed through to the selected file cmdlet.",
                "- Examples: file -search -plugin hydrusnetwork -query ..., file -add ..., file -delete ...",
            ],
            exec=self.run,
        )
        self.register()

    @staticmethod
    def _has_query_arg(args: Sequence[str]) -> bool:
        query_flags = {"-query", "--query"}
        for token in args or []:
            text = str(token or "").strip().lower()
            if text in query_flags:
                return True
            if any(text.startswith(f"{flag}=") for flag in query_flags):
                return True
        return False

    @classmethod
    def _extract_action(cls, args: Sequence[str]) -> tuple[str | None, List[str], List[str]]:
        matched_actions: List[str] = []
        passthrough: List[str] = []

        for token in args or []:
            text = str(token or "")
            lower = text.strip().lower()
            matched = None
            for action_name, variants in cls._ACTION_FLAGS.items():
                if lower in variants:
                    matched = action_name
                    break
            if matched:
                matched_actions.append(matched)
                continue
            passthrough.append(text)

        unique_actions: List[str] = []
        for action in matched_actions:
            if action not in unique_actions:
                unique_actions.append(action)

        if not unique_actions and cls._has_query_arg(passthrough):
            return "search", passthrough, unique_actions

        if len(unique_actions) != 1:
            return None, passthrough, unique_actions
        return unique_actions[0], passthrough, unique_actions

    @classmethod
    def _dispatch(cls, action: str, result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
        module_name = cls._ACTION_MODULE.get(action)
        if not module_name:
            log(f"file: unsupported action '{action}'", file=sys.stderr)
            return 1

        module = import_module(module_name)

        cmdlet_obj = getattr(module, "CMDLET", None)
        if cmdlet_obj is not None:
            exec_fn = getattr(cmdlet_obj, "exec", None)
            if callable(exec_fn):
                return int(exec_fn(result, args, config))

        log(f"file: cannot dispatch action '{action}' via module '{module_name}'", file=sys.stderr)
        return 1

    def run(self, result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
        action, passthrough_args, seen = self._extract_action(args)

        if action is None:
            if not seen:
                log(
                    "file: missing action; use -search/-query for search or choose exactly one of -search, -add, -delete, -merge, -download, -convert, -trim, -archive, -screenshot",
                    file=sys.stderr,
                )
            else:
                rendered = ", ".join(f"-{name}" for name in seen)
                log(f"file: conflicting actions ({rendered}); choose exactly one", file=sys.stderr)
            return 1

        return self._dispatch(action, result, passthrough_args, config)


CMDLET = File()
