"""MCP tools for application management: create, switch, check plan."""

import asyncio
import json
import os
import secrets
from urllib.parse import urlencode

import httpx
from mcp.types import TextContent, Tool

from maeris_mcp.maeris.client import MaerisClient


def create_application_tool() -> Tool:
    return Tool(
        name="create_application",
        description="Create a new application and set it as the active app.",
        inputSchema={
            "type": "object",
            "properties": {
                "name": {"type": "string", "description": "Application name."},
                "url": {"type": "string", "description": "Application URL."},
            },
            "required": ["name", "url"],
        },
    )


def switch_application_tool() -> Tool:
    return Tool(
        name="switch_application",
        description=(
            "Switch the active application by name or URL. "
            "If neither is provided, returns the list of available applications."
        ),
        inputSchema={
            "type": "object",
            "properties": {
                "name": {"type": "string", "description": "Application name to switch to."},
                "url": {"type": "string", "description": "Application URL to switch to."},
            },
            "required": [],
        },
    )


def check_plan_tool() -> Tool:
    return Tool(
        name="check_plan",
        description="Show the active subscription plan for the current application.",
        inputSchema={"type": "object", "properties": {}, "required": []},
    )


def get_applications_tool() -> Tool:
    return Tool(
        name="get_applications",
        description="List all applications the user has access to.",
        inputSchema={"type": "object", "properties": {}, "required": []},
    )


async def handle_create_application(arguments: dict) -> list[TextContent]:
    name = arguments.get("name", "")
    url = arguments.get("url", "")

    if not name or not url:
        return [TextContent(type="text", text="Error: both 'name' and 'url' are required.")]

    async with MaerisClient() as client:
        result = await client.create_application(name, url)

    app_id = result.get("application_id", "")

    # Save as active app
    if app_id:
        _save_active_app(app_id, name)

    return [TextContent(type="text", text=json.dumps({
        "status": "created",
        "application_id": app_id,
        "name": name,
        "url": url,
    }))]


async def handle_switch_application(arguments: dict) -> list[TextContent]:
    name = arguments.get("name")
    url = arguments.get("url")

    async with MaerisClient() as client:
        apps = await client.get_applications()

    if not apps:
        return [TextContent(type="text", text="Error: no applications found for your account.")]

    selected = None

    # Match by name
    if name:
        needle = name.lower()
        selected = next(
            (a for a in apps if (a.get("name") or "").lower() == needle), None
        ) or next(
            (a for a in apps if (a.get("name") or "").lower().startswith(needle)), None
        )
        if not selected:
            return [TextContent(type="text", text=f"Error: no application matching name '{name}'.")]

    # Match by URL (exact, then domain-based)
    if not selected and url:
        needle_url = url.rstrip("/").lower()
        selected = next(
            (a for a in apps if (a.get("url") or "").rstrip("/").lower() == needle_url), None
        )
        if not selected:
            selected = next(
                (a for a in apps if needle_url.startswith((a.get("url") or "").rstrip("/").lower())
                 or (a.get("url") or "").rstrip("/").lower().startswith(needle_url)), None
            )
        if not selected:
            return [TextContent(type="text", text=f"Error: no application matching URL '{url}'.")]

    # No filter — return list for user to choose
    if not selected:
        app_list = [
            {"name": a.get("name", ""), "id": a.get("appid", ""), "url": a.get("url", "")}
            for a in apps
        ]
        return [TextContent(type="text", text=json.dumps({
            "status": "pick_one",
            "message": "No name or URL provided. Here are the available applications:",
            "applications": app_list,
        }))]

    app_id = selected.get("appid", "")
    app_name = selected.get("name") or app_id

    if app_id:
        _save_active_app(app_id, app_name)

    return [TextContent(type="text", text=json.dumps({
        "status": "switched",
        "application_id": app_id,
        "name": app_name,
    }))]


async def handle_check_plan(arguments: dict) -> list[TextContent]:
    async with MaerisClient() as client:
        active_plan = await client.get_active_plan()
        all_plans = await client.get_plans()

    plan_id = active_plan.get("plan_id")
    plan_name = None
    for p in all_plans:
        if p.get("plan_id") == plan_id or p.get("id") == plan_id:
            plan_name = p.get("name") or p.get("plan_name")
            break

    return [TextContent(type="text", text=json.dumps({
        "plan_id": plan_id,
        "plan_name": plan_name,
        "status": active_plan.get("status", "unknown"),
        "subscribed_on": active_plan.get("subscribed_on"),
        "expires_on": active_plan.get("expires_on"),
    }))]


async def handle_get_applications(arguments: dict) -> list[TextContent]:
    async with MaerisClient() as client:
        apps = await client.get_applications()

    app_list = [
        {"name": a.get("name", ""), "id": a.get("appid", ""), "url": a.get("url", "")}
        for a in apps
    ]
    return [TextContent(type="text", text=json.dumps(app_list))]


def login_tool() -> Tool:
    return Tool(
        name="login",
        description=(
            "Check if the user is logged in to Maeris. "
            "If not logged in, returns a login_url — share it with the user as a clickable link. "
            "The server polls for the token in the background, so once the user completes login "
            "in their browser, credentials are saved automatically. "
            "Do NOT ask users for a token. "
            "Only use the token parameter for programmatic/CI use cases where the user explicitly provides one."
        ),
        inputSchema={
            "type": "object",
            "properties": {
                "token": {
                    "type": "string",
                    "description": "Firebase ID token. Optional — only for programmatic use. Do not ask users for this.",
                },
            },
            "required": [],
        },
    )


def logout_tool() -> Tool:
    return Tool(
        name="logout",
        description="Sign out and remove stored credentials for the current repository.",
        inputSchema={"type": "object", "properties": {}, "required": []},
    )


def status_tool() -> Tool:
    return Tool(
        name="maeris_status",
        description="Show current authentication status, active app, and plan info.",
        inputSchema={"type": "object", "properties": {}, "required": []},
    )


async def handle_login(arguments: dict) -> list[TextContent]:
    token = arguments.get("token", "")
    token_store = _get_token_store()

    # If a token is provided (programmatic use), save it
    if token:
        try:
            token_store.save_token(token)
            email = token_store.get_email() or "unknown"
            app_name = token_store.get_app_name()
            app_id = token_store.get_app_id()
            return [TextContent(type="text", text=json.dumps({
                "status": "authenticated",
                "email": email,
                "app_id": app_id,
                "app_name": app_name,
            }))]
        except Exception as e:
            return [TextContent(type="text", text=f"Error: invalid token — {e}")]

    # No token — check if already logged in
    if token_store.is_authenticated() and not token_store.is_token_expired():
        email = token_store.get_email()
        app_name = token_store.get_app_name()
        return [TextContent(type="text", text=json.dumps({
            "status": "already_authenticated",
            "email": email,
            "app_name": app_name,
            "message": "Already logged in.",
        }))]

    # Not logged in — generate login URL and poll for token
    from maeris_mcp.config import ENV as _ENV
    app_url = _ENV.app_url.rstrip("/")
    state = secrets.token_urlsafe(32)
    redirect_uri = f"{app_url}/auth/callback"

    auth_params = {
        "client_id": "maeris-mcp",
        "redirect_uri": redirect_uri,
        "state": state,
        "response_type": "token",
    }
    login_url = f"{app_url}/auth/login?{urlencode(auth_params)}"

    # Start polling for the token in the background
    asyncio.create_task(_poll_and_save_token(state, app_url, token_store))

    return [TextContent(type="text", text=json.dumps({
        "status": "not_authenticated",
        "login_url": login_url,
        "message": "Click the link below to log in via your browser. Once you complete login, credentials will be saved automatically.",
    }))]


async def _poll_and_save_token(state: str, app_url: str, token_store) -> None:
    """Poll the auth endpoint in the background and save the token when received."""
    poll_url = f"{app_url}/api/auth/poll"
    timeout = 300  # 5 minutes

    async with httpx.AsyncClient(timeout=5.0) as client:
        elapsed = 0.0
        while elapsed < timeout:
            try:
                resp = await client.get(poll_url, params={"state": state})
                if resp.status_code == 200:
                    data = resp.json()
                    if "token" in data:
                        token_store.save_token(data["token"])
                        return
                    if "error" in data:
                        return
            except httpx.RequestError:
                pass
            await asyncio.sleep(2)
            elapsed += 2


async def handle_logout(arguments: dict) -> list[TextContent]:
    token_store = _get_token_store()

    if not token_store.is_authenticated():
        return [TextContent(type="text", text=json.dumps({
            "status": "not_authenticated",
            "message": "Not currently authenticated.",
        }))]

    token_store.clear()
    return [TextContent(type="text", text=json.dumps({
        "status": "logged_out",
        "message": "Signed out successfully. Credentials removed.",
    }))]


async def handle_status(arguments: dict) -> list[TextContent]:
    from maeris_mcp.auth.ai_config import AIConfig
    from maeris_mcp.auth.scopes import load_maeris_scope, load_codebase_scope

    ai_config = AIConfig()
    mode = ai_config.get_ai_mode()
    token_store = _get_token_store()
    authenticated = token_store.is_authenticated()
    current_maeris_scope = load_maeris_scope()
    current_codebase_scope = load_codebase_scope()

    _scope_info = {
        "maeris_scope": current_maeris_scope,
        "codebase_scope": current_codebase_scope,
    }

    if mode is None:
        return [TextContent(type="text", text=json.dumps({
            "ai_mode": None,
            "message": "AI mode not configured. Run 'maeris app init' to set up.",
            **_scope_info,
        }))]

    if mode == "external_llm":
        result = {
            "ai_mode": "external_llm",
            "ai_mode_label": "External LLM (Claude Code / Codex / Cursor)",
            "portal_authenticated": authenticated,
            **_scope_info,
        }
        if authenticated:
            result["email"] = token_store.get_email()
            result["app_id"] = token_store.get_app_id()
            result["app_name"] = token_store.get_app_name()
        return [TextContent(type="text", text=json.dumps(result))]

    # maeris mode
    if not authenticated:
        return [TextContent(type="text", text=json.dumps({
            "ai_mode": "maeris",
            "authenticated": False,
            "message": "Not authenticated. Run 'maeris login' first.",
            **_scope_info,
        }))]

    if token_store.is_token_expired():
        return [TextContent(type="text", text=json.dumps({
            "ai_mode": "maeris",
            "authenticated": True,
            "expired": True,
            "message": "Token expired. Run 'maeris login' to re-authenticate.",
            **_scope_info,
        }))]

    result = {
        "ai_mode": "maeris",
        "authenticated": True,
        "expired": False,
        "email": token_store.get_email(),
        "app_id": token_store.get_app_id(),
        "app_name": token_store.get_app_name(),
        **_scope_info,
    }

    # Try to fetch plan info
    if token_store.get_app_id():
        try:
            async with MaerisClient() as client:
                active_plan = await client.get_active_plan()
                all_plans = await client.get_plans()

            plan_id = active_plan.get("plan_id")
            plan_name = None
            for p in all_plans:
                if p.get("plan_id") == plan_id or p.get("id") == plan_id:
                    plan_name = p.get("name") or p.get("plan_name")
                    break
            result["plan_name"] = plan_name
            result["plan_status"] = active_plan.get("status", "unknown")
        except Exception:
            pass

    return [TextContent(type="text", text=json.dumps(result))]


def _get_token_store():
    """Get the token store for the current repo."""
    import hashlib
    import os
    from pathlib import Path

    from maeris_mcp.auth.token_store import TokenStore

    config_dir_env = os.getenv("MAERIS_CONFIG_DIR")
    if config_dir_env:
        config_dir = Path(config_dir_env)
    else:
        repo_hash = hashlib.sha256(str(Path.cwd().resolve()).encode()).hexdigest()[:16]
        config_dir = Path.home() / ".maeris" / "repos" / repo_hash

    return TokenStore(config_dir=config_dir)


def _save_active_app(app_id: str, app_name: str) -> None:
    """Save the active app to the token store."""
    token_store = _get_token_store()
    token_store.save_app_id(app_id, app_name)
