"""mureo's STRATEGY.md / STATE.json MCP tool surface.

Seven tools that expose mureo's context layer over MCP, so any MCP host
(Claude Desktop chat, claude.ai web, Codex/Cursor, …) can read and
update STRATEGY.md / STATE.json without direct filesystem access.

The Claude Code path keeps working through its built-in ``Read`` tool;
these MCP tools are additive — they unlock the same capability for
hosts that lack ``Read``. Workflow skills can be migrated to call
these tools (Phase 2/3) so a single skill prompt runs everywhere.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

from mcp.types import Tool

from mureo.mcp._handlers_mureo_context import (
    handle_state_action_log_append,
    handle_state_get,
    handle_state_platform_metrics_set,
    handle_state_report_set,
    handle_state_set_conversion_events,
    handle_state_upsert_campaign,
    handle_strategy_get,
    handle_strategy_set,
)

if TYPE_CHECKING:
    from mcp.types import TextContent


_PATH_PROPERTY = {
    "type": "string",
    "description": (
        "Optional path to the file. Defaults to STRATEGY.md / STATE.json "
        "in the MCP server's current working directory. Paths outside "
        "cwd are refused."
    ),
}


_ACTION_LOG_ENTRY_PROPERTY = {
    "type": "object",
    "description": (
        "An action_log entry. Required: timestamp (ISO 8601), action "
        "(short description), platform (google_ads / meta_ads / etc.). "
        "Optional: campaign_id, summary, command, metrics_at_action, "
        "observation_due, reversible_params, rollback_of."
    ),
    "properties": {
        "timestamp": {"type": "string"},
        "action": {"type": "string"},
        "platform": {"type": "string"},
        "campaign_id": {"type": "string"},
        "summary": {"type": "string"},
        "command": {"type": "string"},
        "metrics_at_action": {"type": "object"},
        "observation_due": {"type": "string"},
        "reversible_params": {"type": "object"},
        "rollback_of": {"type": "integer"},
    },
    "required": ["timestamp", "action", "platform"],
}


_CAMPAIGN_PROPERTY = {
    "type": "object",
    "description": (
        "A CampaignSnapshot for STATE.json plus its platform context. "
        "Required: campaign_id, campaign_name, status, platform, "
        "account_id. The platform + account_id populate the per-platform "
        "``platforms`` section the dashboard reads (omit them and the "
        "client renders as inactive). Optional fields mirror the snapshot "
        "schema in docs/strategy-context.md, including ``metrics`` "
        "(spend / impressions / clicks / conversions / cpa / ctr / "
        "result_indicator / period / fetched_at) for dashboard KPIs."
    ),
    "properties": {
        "campaign_id": {"type": "string"},
        "campaign_name": {"type": "string"},
        "status": {"type": "string"},
        "platform": {
            "type": "string",
            "description": (
                "Platform key this campaign belongs to, e.g. "
                "``google_ads`` / ``meta_ads``."
            ),
        },
        "account_id": {
            "type": "string",
            "description": (
                "Platform account id (Google ``customer_id`` / Meta "
                "``act_*``) written onto the platform entry."
            ),
        },
        "bidding_strategy_type": {"type": "string"},
        "bidding_details": {"type": "object"},
        "daily_budget": {"type": "number"},
        "device_targeting": {"type": "array"},
        "campaign_goal": {"type": "string"},
        "notes": {"type": "string"},
        "metrics": {
            "type": "object",
            "description": (
                "Optional performance metrics for the reporting dashboard: "
                "spend, impressions, clicks, conversions, cpa, ctr, "
                "result_indicator (Meta: clicks vs leads), period (e.g. "
                "``LAST_30_DAYS``), fetched_at (ISO 8601)."
            ),
        },
    },
    "required": [
        "campaign_id",
        "campaign_name",
        "status",
        "platform",
        "account_id",
    ],
}


TOOLS: list[Tool] = [
    Tool(
        name="mureo_strategy_get",
        description=(
            "Read STRATEGY.md and return its raw markdown text plus an "
            "exists flag. Returns empty markdown when the file is "
            "absent (skills should treat that as 'no strategy yet', "
            "not as an error). Use this when the host has no direct "
            "filesystem access (Claude Desktop chat, web, remote MCP)."
        ),
        inputSchema={
            "type": "object",
            "properties": {"path": _PATH_PROPERTY},
        },
    ),
    Tool(
        name="mureo_strategy_set",
        description=(
            "Atomically replace STRATEGY.md with the provided markdown. "
            "The content is parsed via parse_strategy() before writing "
            "to ensure it is well-formed; a malformed input raises "
            "rather than corrupts the file. Use this to update goals, "
            "constraints, or operation mode from a chat-only host."
        ),
        inputSchema={
            "type": "object",
            "properties": {
                "markdown": {
                    "type": "string",
                    "description": "The full new content of STRATEGY.md.",
                },
                "path": _PATH_PROPERTY,
            },
            "required": ["markdown"],
        },
    ),
    Tool(
        name="mureo_state_get",
        description=(
            "Read STATE.json and return its parsed v2 document: "
            "version, last_synced_at, platforms (per-platform "
            "campaigns), legacy v1 campaigns, and action_log. Returns "
            "an empty default doc when the file is absent."
        ),
        inputSchema={
            "type": "object",
            "properties": {"path": _PATH_PROPERTY},
        },
    ),
    Tool(
        name="mureo_state_action_log_append",
        description=(
            "Atomically append a single action_log entry to STATE.json. "
            "Use this whenever a workflow takes an action that should "
            "be evaluable later (budget changes, campaign pauses, "
            "negative-keyword adds). Returns the updated state document."
        ),
        inputSchema={
            "type": "object",
            "properties": {
                "entry": _ACTION_LOG_ENTRY_PROPERTY,
                "path": _PATH_PROPERTY,
            },
            "required": ["entry"],
        },
    ),
    Tool(
        name="mureo_state_upsert_campaign",
        description=(
            "Atomically upsert a CampaignSnapshot into STATE.json (root "
            "campaigns array). Use this to keep STATE.json in sync with "
            "campaign metadata changes the agent observes via vendor "
            "MCPs or BYOD imports. Pass the optional ``metrics`` object to "
            "persist the campaign's performance numbers (spend, clicks, "
            "conversions, cpa, ctr, …) so the reporting dashboard can "
            "render KPIs from STATE.json."
        ),
        inputSchema={
            "type": "object",
            "properties": {
                "campaign": _CAMPAIGN_PROPERTY,
                "path": _PATH_PROPERTY,
            },
            "required": ["campaign"],
        },
    ),
    Tool(
        name="mureo_state_report_set",
        description=(
            "Atomically persist a structured analysis report summary into "
            "STATE.json's ``reports`` section so the read-only configure "
            "dashboard can render the latest daily / weekly / goal report "
            "without re-running the agent. ``report`` selects the kind "
            "(daily / weekly / goal); ``summary`` is a free-form object — by "
            "convention generated_at (ISO 8601), period, kpis (per-platform "
            "/ totals headline numbers), flags (notable items), narrative "
            "(short text). Other report kinds are preserved. Best-effort: a "
            "skill should skip this silently where the context MCP is "
            "unavailable. Returns the updated state document."
        ),
        inputSchema={
            "type": "object",
            "properties": {
                "report": {
                    "type": "string",
                    "enum": ["daily", "weekly", "goal"],
                    "description": (
                        "Report kind: ``daily`` (daily-check), ``weekly`` "
                        "(weekly-report), or ``goal`` (goal-review)."
                    ),
                },
                "summary": {
                    "type": "object",
                    "description": (
                        "Free-form summary object. Convention: generated_at "
                        "(ISO 8601), period, kpis (per-platform / totals "
                        "headline numbers), flags (list of notable items), "
                        "narrative (short text)."
                    ),
                },
                "path": _PATH_PROPERTY,
            },
            "required": ["report", "summary"],
        },
    ),
    Tool(
        name="mureo_state_platform_metrics_set",
        description=(
            "Atomically set a platform's metric ROLLUP in STATE.json's v2 "
            "``platforms`` section so the read-only reporting dashboard can "
            "render per-platform KPIs (and the YESTERDAY / LAST_30_DAYS period "
            "toggle) without re-querying. This writes the PLATFORM-LEVEL "
            "rollup — distinct from mureo_state_upsert_campaign, which writes "
            "per-campaign metrics. Pass ``totals`` + ``metrics_period`` for the "
            "single most-recent window, and/or ``periods`` "
            '({"YESTERDAY": {…}, "LAST_30_DAYS": {…}}) for the per-window '
            "rollups the toggle reads. ``periods`` is merged per window key "
            "(a YESTERDAY write keeps a prior LAST_30_DAYS bucket); omitted "
            "fields preserve their existing value. Campaigns and every other "
            "platform are preserved. ``account_id`` is required and always "
            "written onto the entry. Returns the updated state document."
        ),
        inputSchema={
            "type": "object",
            "properties": {
                "platform": {
                    "type": "string",
                    "description": (
                        "Platform key: a built-in (``google_ads`` / "
                        "``meta_ads`` / ``search_console`` / ``ga4``) or a "
                        "plugin bridge ``plugin:<dist>``."
                    ),
                },
                "account_id": {
                    "type": "string",
                    "description": (
                        "The platform account id (Google customer_id / Meta "
                        "act_*). Always written onto the platform entry."
                    ),
                },
                "totals": {
                    "type": "object",
                    "description": (
                        "Single-rollup totals for the most recent window "
                        "(spend, impressions, clicks, conversions, cpa, ctr, "
                        "result_indicator, period, fetched_at). Omit to "
                        "preserve the existing value."
                    ),
                },
                "metrics_period": {
                    "type": "string",
                    "description": (
                        "The window ``totals`` covers (e.g. ``LAST_30_DAYS``). "
                        "Omit to preserve the existing value."
                    ),
                },
                "periods": {
                    "type": "object",
                    "description": (
                        "Per-window rollups keyed by period token "
                        "(``YESTERDAY`` / ``LAST_30_DAYS`` / …); each value is "
                        "a totals-shaped object. Merged per key into the "
                        "existing map. Omit to preserve the existing map."
                    ),
                },
                "path": _PATH_PROPERTY,
            },
            "required": ["platform", "account_id"],
        },
    ),
    Tool(
        name="mureo_state_set_conversion_events",
        description=(
            "Declare which Meta Insights ``action_type`` rows count as THIS "
            "account's conversions, overriding mureo's built-in deduped generic "
            "set (lead / purchase / complete_registration). Use this when an "
            "advertiser's real conversion is a CUSTOM pixel event "
            "(``offsite_conversion.custom.<id>``) — otherwise it reports 0 "
            "conversions — or when their account only emits a component row "
            "(e.g. ``offsite_conversion.fb_pixel_lead``) with no generic "
            "aggregate. Replacement semantics: the listed action_types are the "
            "COMPLETE conversion set (never summed on top of the defaults), so "
            "overlapping alias rows can't double-count. Tip: to avoid typos, "
            "first call meta_ads_insights_report / _breakdown to see the "
            "account's real action_type labels, confirm with the operator, then "
            "set the exact string(s) here. Pass an empty list (or omit "
            "``conversion_action_types``) to CLEAR the override and restore the "
            "default. Stored on ``platforms[platform]`` and preserved across "
            "syncs. Returns the updated state document."
        ),
        inputSchema={
            "type": "object",
            "properties": {
                "platform": {
                    "type": "string",
                    "description": (
                        "Platform key — normally ``meta_ads`` (the override "
                        "only affects the Meta conversion counters)."
                    ),
                },
                "account_id": {
                    "type": "string",
                    "description": (
                        "The Meta ad account id (``act_*``). Always written "
                        "onto the platform entry."
                    ),
                },
                "conversion_action_types": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": (
                        "Exact Meta ``action_type`` strings to count as "
                        "conversions (e.g. "
                        '["offsite_conversion.custom.123"]). Empty / omitted '
                        "clears the override."
                    ),
                },
                "path": _PATH_PROPERTY,
            },
            "required": ["platform", "account_id"],
        },
    ),
]


_HANDLERS = {
    "mureo_strategy_get": handle_strategy_get,
    "mureo_strategy_set": handle_strategy_set,
    "mureo_state_get": handle_state_get,
    "mureo_state_action_log_append": handle_state_action_log_append,
    "mureo_state_upsert_campaign": handle_state_upsert_campaign,
    "mureo_state_report_set": handle_state_report_set,
    "mureo_state_platform_metrics_set": handle_state_platform_metrics_set,
    "mureo_state_set_conversion_events": handle_state_set_conversion_events,
}


async def handle_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
    """Dispatch a tool call to its handler.

    Raises:
        ValueError: when the tool name is unknown or required parameters
            are missing.
    """
    handler = _HANDLERS.get(name)
    if handler is None:
        raise ValueError(f"Unknown tool: {name}")
    return await handler(arguments)
