from __future__ import annotations

from argparse import Namespace
from dataclasses import dataclass


USER_DOMAIN = "user"
BUILDER_DOMAIN = "builder"


@dataclass(frozen=True, slots=True)
class PublicToolSpec:
    domain: str
    tool_name: str
    method_names: tuple[str, ...] = ()
    cli_route: tuple[str, ...] | None = None
    mcp_public: bool = True
    cli_public: bool = True
    has_contract: bool = False
    cli_show_effective_context: bool = False
    cli_context_write: bool = False

    @property
    def trim_key(self) -> str:
        return tool_key(self.domain, self.tool_name)


def tool_key(domain: str, tool_name: str) -> str:
    return f"{domain}:{tool_name}"


USER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
    PublicToolSpec(USER_DOMAIN, "auth_login", cli_route=("auth", "login"), mcp_public=False),
    PublicToolSpec(USER_DOMAIN, "auth_use_credential", ("auth_use_credential",), ("auth", "use-credential")),
    PublicToolSpec(USER_DOMAIN, "auth_whoami", ("auth_whoami",), ("auth", "whoami")),
    PublicToolSpec(USER_DOMAIN, "auth_logout", ("auth_logout",), ("auth", "logout")),
    PublicToolSpec(USER_DOMAIN, "workspace_list", ("workspace_list",), ("workspace", "list")),
    PublicToolSpec(USER_DOMAIN, "workspace_get", ("workspace_get",), ("workspace", "get")),
    PublicToolSpec(USER_DOMAIN, "workspace_select", ("workspace_select",), ("workspace", "select")),
    PublicToolSpec(USER_DOMAIN, "app_list", ("app_list",), ("app", "list"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "app_get", ("app_get",), ("app", "get"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "portal_list", ("portal_list",), ("portal", "list"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "portal_get", ("portal_get",), ("portal", "get"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "view_get", ("view_get",), ("view", "get"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "chart_get", ("chart_get",), ("chart", "get"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "file_get_upload_info", ("file_get_upload_info",), cli_public=False),
    PublicToolSpec(USER_DOMAIN, "file_upload_local", ("file_upload_local",), cli_public=False),
    PublicToolSpec(USER_DOMAIN, "feedback_submit", ("feedback_submit",), cli_public=False),
    PublicToolSpec(
        USER_DOMAIN,
        "record_schema_get",
        cli_route=("record", "schema", "applicant"),
        mcp_public=False,
        cli_public=False,
    ),
    PublicToolSpec(
        USER_DOMAIN,
        "record_browse_schema_get",
        ("record_browse_schema_get_public",),
        ("record", "schema", "browse"),
        cli_show_effective_context=True,
    ),
    PublicToolSpec(
        USER_DOMAIN,
        "record_insert_schema_get",
        ("record_insert_schema_get_public",),
        ("record", "schema", "insert"),
        cli_show_effective_context=True,
    ),
    PublicToolSpec(
        USER_DOMAIN,
        "record_update_schema_get",
        ("record_update_schema_get_public",),
        ("record", "schema", "update"),
        cli_show_effective_context=True,
    ),
    PublicToolSpec(USER_DOMAIN, "record_import_schema_get", ("record_import_schema_get",), ("record", "schema", "import"), cli_show_effective_context=True),
    PublicToolSpec(
        USER_DOMAIN,
        "record_code_block_schema_get",
        ("record_code_block_schema_get_public",),
        ("record", "schema", "code-block"),
        cli_show_effective_context=True,
    ),
    PublicToolSpec(USER_DOMAIN, "record_member_candidates", ("record_member_candidates",), ("record", "member-candidates"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "record_department_candidates", ("record_department_candidates",), ("record", "department-candidates"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "record_analyze", ("record_analyze",), ("record", "analyze"), mcp_public=False, cli_public=False),
    PublicToolSpec(USER_DOMAIN, "record_list", ("record_list",), ("record", "list"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "record_access", ("record_access",), ("record", "access"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "record_get", ("record_get_public",), ("record", "get"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "record_logs_get", ("record_logs_get",), ("record", "logs"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "record_insert", ("record_insert_public",), ("record", "insert"), cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(USER_DOMAIN, "record_update", ("record_update_public",), ("record", "update"), cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(USER_DOMAIN, "record_delete", ("record_delete_public",), ("record", "delete"), cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(USER_DOMAIN, "record_import_template_get", ("record_import_template_get",), ("import", "template"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "record_import_verify", ("record_import_verify",), ("import", "verify"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "record_import_repair_local", ("record_import_repair_local",), ("import", "repair")),
    PublicToolSpec(USER_DOMAIN, "record_import_start", ("record_import_start",), ("import", "start"), cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(USER_DOMAIN, "record_import_status_get", ("record_import_status_get",), ("import", "status"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "record_export_start", ("record_export_start",), ("export", "start"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "record_export_status_get", ("record_export_status_get",), ("export", "status"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "record_export_get", ("record_export_get",), ("export", "get"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "record_export_direct", ("record_export_direct",), ("export", "direct"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "record_code_block_run", ("record_code_block_run",), ("record", "code-block-run"), cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(USER_DOMAIN, "task_list", ("task_list",), ("task", "list"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "task_get", ("task_get",), ("task", "get"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "task_action_execute", ("task_action_execute",), ("task", "action"), cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(
        USER_DOMAIN,
        "task_associated_report_detail_get",
        ("task_associated_report_detail_get",),
        ("task", "report"),
        cli_show_effective_context=True,
    ),
    PublicToolSpec(USER_DOMAIN, "task_workflow_log_get", ("task_workflow_log_get",), ("task", "log"), cli_show_effective_context=True),
    PublicToolSpec(USER_DOMAIN, "directory_search", ("directory_search",), cli_public=False),
    PublicToolSpec(USER_DOMAIN, "directory_list_internal_users", ("directory_list_internal_users",), mcp_public=False, cli_public=False),
    PublicToolSpec(USER_DOMAIN, "directory_list_all_internal_users", ("directory_list_all_internal_users",), mcp_public=False, cli_public=False),
    PublicToolSpec(USER_DOMAIN, "directory_list_internal_departments", ("directory_list_internal_departments",), mcp_public=False, cli_public=False),
    PublicToolSpec(USER_DOMAIN, "directory_list_all_departments", ("directory_list_all_departments",), mcp_public=False, cli_public=False),
    PublicToolSpec(USER_DOMAIN, "directory_list_sub_departments", ("directory_list_sub_departments",), mcp_public=False, cli_public=False),
    PublicToolSpec(USER_DOMAIN, "directory_list_external_members", ("directory_list_external_members",), mcp_public=False, cli_public=False),
)


BUILDER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
    PublicToolSpec(BUILDER_DOMAIN, "auth_use_credential", ("auth_use_credential",), ("builder", "auth", "use-credential"), cli_public=False),
    PublicToolSpec(BUILDER_DOMAIN, "auth_whoami", ("auth_whoami",), ("builder", "auth", "whoami"), cli_public=False),
    PublicToolSpec(BUILDER_DOMAIN, "auth_logout", ("auth_logout",), ("builder", "auth", "logout"), cli_public=False),
    PublicToolSpec(BUILDER_DOMAIN, "workspace_list", ("workspace_list",), ("builder", "workspace", "list"), cli_public=False),
    PublicToolSpec(BUILDER_DOMAIN, "workspace_get", ("workspace_get",), ("builder", "workspace", "get"), cli_public=False),
    PublicToolSpec(BUILDER_DOMAIN, "file_upload_local", ("file_upload_local",), ("builder", "file", "upload-local"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(BUILDER_DOMAIN, "feedback_submit", ("feedback_submit",), ("builder", "feedback", "submit"), has_contract=True),
    PublicToolSpec(BUILDER_DOMAIN, "builder_tool_contract", ("builder_tool_contract",), ("builder", "contract"), has_contract=False),
    PublicToolSpec(BUILDER_DOMAIN, "workspace_icon_catalog_get", ("workspace_icon_catalog_get",), ("builder", "icon", "catalog"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "package_list", ("package_list",), ("builder", "package", "list"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "package_get", ("package_get",), ("builder", "package", "get"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "package_apply", ("package_apply",), ("builder", "package", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(BUILDER_DOMAIN, "solution_install", ("solution_install",), ("builder", "solution", "install"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(BUILDER_DOMAIN, "member_search", ("member_search",), ("builder", "member", "search"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "role_search", ("role_search",), ("builder", "role", "search"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "role_create", ("role_create",), ("builder", "role", "create"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_release_edit_lock_if_mine", ("app_release_edit_lock_if_mine",), ("builder", "app", "release-edit-lock-if-mine"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_resolve", ("app_resolve",), ("builder", "app", "resolve"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "button_style_catalog_get", ("button_style_catalog_get",), ("builder", "button", "catalog"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_custom_buttons_apply", ("app_custom_buttons_apply",), ("builder", "button", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_associated_resources_apply", ("app_associated_resources_apply",), ("builder", "associated-resource", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_get", ("app_get",), ("builder", "app", "get", "summary"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_get_fields", ("app_get_fields",), ("builder", "app", "get", "fields"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_repair_code_blocks", ("app_repair_code_blocks",), ("builder", "app", "repair-code-blocks"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_get_layout", ("app_get_layout",), ("builder", "app", "get", "layout"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_get_views", ("app_get_views",), ("builder", "app", "get", "views"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_get_flow", ("app_get_flow",), ("builder", "app", "get", "flow"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_get_charts", ("app_get_charts",), ("builder", "app", "get", "charts"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_get_buttons", ("app_get_buttons",), ("builder", "app", "get", "buttons"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_get_associated_resources", ("app_get_associated_resources",), ("builder", "app", "get", "associated-resources"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "portal_list", ("portal_list",), ("builder", "portal", "list"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "portal_get", ("portal_get",), ("builder", "portal", "get"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "view_get", ("view_get",), ("builder", "view", "get"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "chart_get", ("chart_get",), ("builder", "chart", "get"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_schema_apply", ("app_schema_apply",), ("builder", "schema", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_layout_apply", ("app_layout_apply",), ("builder", "layout", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_flow_get_schema", ("app_flow_get_schema",), ("builder", "flow", "schema"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_flow_get", ("app_flow_get",), ("builder", "flow", "get"), has_contract=True, cli_show_effective_context=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_flow_apply", ("app_flow_apply",), ("builder", "flow", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_views_apply", ("app_views_apply",), ("builder", "views", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_charts_apply", ("app_charts_apply",), ("builder", "charts", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(BUILDER_DOMAIN, "portal_apply", ("portal_apply",), ("builder", "portal", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(BUILDER_DOMAIN, "portal_delete", ("portal_delete",), ("builder", "portal", "delete"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
    PublicToolSpec(BUILDER_DOMAIN, "app_publish_verify", ("app_publish_verify",), ("builder", "publish", "verify"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
)


ALL_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = USER_PUBLIC_TOOL_SPECS + BUILDER_PUBLIC_TOOL_SPECS
PUBLIC_TOOL_BY_KEY: dict[str, PublicToolSpec] = {spec.trim_key: spec for spec in ALL_PUBLIC_TOOL_SPECS}
PUBLIC_TOOL_BY_CLI_ROUTE: dict[tuple[str, ...], PublicToolSpec] = {
    spec.cli_route: spec
    for spec in ALL_PUBLIC_TOOL_SPECS
    if spec.cli_public and spec.cli_route is not None
}
PUBLIC_TOOL_BY_CLI_ROUTE.update(
    {
        ("builder", "button", "get"): PUBLIC_TOOL_BY_KEY[tool_key(BUILDER_DOMAIN, "app_get_buttons")],
        ("builder", "associated-resource", "get"): PUBLIC_TOOL_BY_KEY[
            tool_key(BUILDER_DOMAIN, "app_get_associated_resources")
        ],
    }
)


def public_tool_specs(domain: str | None = None) -> tuple[PublicToolSpec, ...]:
    if domain is None:
        return ALL_PUBLIC_TOOL_SPECS
    return tuple(spec for spec in ALL_PUBLIC_TOOL_SPECS if spec.domain == domain)


def public_mcp_tool_names(domain: str) -> set[str]:
    return {spec.tool_name for spec in public_tool_specs(domain) if spec.mcp_public}


def public_builder_contract_tool_names() -> list[str]:
    return sorted(
        spec.tool_name
        for spec in BUILDER_PUBLIC_TOOL_SPECS
        if spec.has_contract and (spec.mcp_public or spec.cli_public)
    )


def server_method_map(domain: str) -> dict[str, str]:
    mapping: dict[str, str] = {}
    for spec in public_tool_specs(domain):
        if not spec.mcp_public:
            continue
        for method_name in spec.method_names:
            mapping[method_name] = spec.trim_key
    return mapping


def cli_trim_key_from_namespace(args: Namespace) -> str | None:
    route = cli_route_from_namespace(args)
    if route is None:
        return None
    spec = PUBLIC_TOOL_BY_CLI_ROUTE.get(route)
    return spec.trim_key if spec is not None else None


def cli_public_tool_spec_from_namespace(args: Namespace) -> PublicToolSpec | None:
    route = cli_route_from_namespace(args)
    if route is None:
        return None
    return PUBLIC_TOOL_BY_CLI_ROUTE.get(route)


def cli_route_from_namespace(args: Namespace) -> tuple[str, ...] | None:
    command = getattr(args, "command", None)
    if not isinstance(command, str) or not command:
        return None
    command = "builder" if command == "build" else command
    if command == "record":
        record_command = getattr(args, "record_command", None)
        if record_command == "schema":
            schema_command = getattr(args, "record_schema_command", None)
            return (command, "schema", schema_command) if isinstance(schema_command, str) and schema_command else None
        return (command, record_command) if isinstance(record_command, str) and record_command else None
    if command == "task":
        task_command = getattr(args, "task_command", None)
        return (command, task_command) if isinstance(task_command, str) and task_command else None
    if command != "builder":
        child_attr = f"{command}_command"
        child = getattr(args, child_attr, None)
        return (command, child) if isinstance(child, str) and child else None

    builder_command = getattr(args, "builder_command", None)
    if not isinstance(builder_command, str) or not builder_command:
        return None
    builder_command = "associated-resource" if builder_command == "associated-resources" else builder_command
    if builder_command == "contract":
        return ("builder", "contract")
    if builder_command == "app":
        app_command = getattr(args, "builder_app_command", None)
        if app_command == "get":
            section = getattr(args, "builder_app_get_section", "summary") or "summary"
            return ("builder", "app", "get", str(section))
        return ("builder", "app", app_command) if isinstance(app_command, str) and app_command else None
    child_attr = f"builder_{builder_command.replace('-', '_')}_command"
    child = getattr(args, child_attr, None)
    return ("builder", builder_command, child) if isinstance(child, str) and child else None
