"""MCP (Model Context Protocol) server for stablestack.

This module provides an MCP server that exposes stablestack functionality
to AI assistants like Claude.

Usage:
    stablestack mcp-server             # Start MCP server (stdio transport)
    stablestack mcp-server --port 8080 # Start with SSE transport

Tools exposed:
    - stablestack_run: Run checks on paths and return findings
    - stablestack_explain: Get detailed explanation of a rule
    - stablestack_fix: Apply auto-fix for a finding
    - stablestack_list_rules: List all available rules
"""

from __future__ import annotations

import json
import sys
from pathlib import Path
from typing import Any


def create_server():
    """Create and configure the MCP server."""
    try:
        from mcp.server.fastmcp import FastMCP
    except ImportError:
        raise ImportError(
            "MCP server requires the 'mcp' package. "
            "Install with: pip install 'stablestack[mcp]' or pip install mcp"
        )

    mcp = FastMCP("stablestack")

    @mcp.tool()
    def stablestack_run(
        paths: list[str],
        tier: str = "recommended",
        enable: list[str] | None = None,
        disable: list[str] | None = None,
    ) -> dict[str, Any]:
        """
        Run stablestack on specified paths and return findings.

        Args:
            paths: List of file or directory paths to check
            tier: Maximum tier to check ("critical", "recommended", "optional", "preference")
            enable: List of rule IDs to enable
            disable: List of rule IDs to disable

        Returns:
            Dictionary containing findings and summary statistics
        """
        from stablestack.config import Config
        from stablestack.models import Tier
        from stablestack.runner import run_checks

        # Configure
        config = Config()
        tier_map = {
            "critical": Tier.CRITICAL,
            "recommended": Tier.RECOMMENDED,
            "optional": Tier.OPTIONAL,
            "preference": Tier.PREFERENCE,
        }
        config.max_tier = tier_map.get(tier, Tier.RECOMMENDED)

        if enable:
            config.enable = set(enable)
        if disable:
            config.disable = set(disable)

        # Run checks
        path_list = [Path(p) for p in paths]
        findings, is_free_tier, total_checkers = run_checks(path_list, config)

        # Format findings for AI consumption
        findings_data = []
        for f in findings:
            findings_data.append({
                "file": f.file,
                "line": f.line,
                "column": f.column,
                "rule_id": f.rule_id,
                "rule_name": f.rule_name,
                "message": f.message,
                "severity": f.severity.value,
                "code": f.code,
                "fix_hint": f.fix_hint,
                "why": f.why,
            })

        # Summary
        from stablestack.models import Severity
        error_count = sum(1 for f in findings if f.severity == Severity.ERROR)
        warning_count = sum(1 for f in findings if f.severity == Severity.WARNING)
        info_count = sum(1 for f in findings if f.severity == Severity.INFO)

        return {
            "findings": findings_data,
            "summary": {
                "total_findings": len(findings),
                "errors": error_count,
                "warnings": warning_count,
                "info": info_count,
                "files_checked": len(set(f.file for f in findings)),
            },
            "is_free_tier": is_free_tier,
            "total_checkers_available": total_checkers,
        }

    @mcp.tool()
    def stablestack_explain(rule_id: str) -> dict[str, Any]:
        """
        Get detailed explanation of a stablestack rule.

        Args:
            rule_id: The rule ID (e.g., "DET001", "SEC001")

        Returns:
            Dictionary containing rule details, examples, and fix guidance
        """
        from stablestack.checkers import ALL_CHECKERS
        from stablestack.models import Tier

        rule_id = rule_id.upper()

        for checker_cls in ALL_CHECKERS:
            if checker_cls.rule_id == rule_id:
                tier = getattr(checker_cls, 'tier', Tier.RECOMMENDED)
                return {
                    "rule_id": checker_cls.rule_id,
                    "rule_name": checker_cls.rule_name,
                    "description": checker_cls.description,
                    "why": checker_cls.why,
                    "severity": checker_cls.severity.value,
                    "tier": tier.name,
                    "supports_autofix": checker_cls.supports_autofix,
                    "file_extensions": list(checker_cls.file_extensions),
                }

        return {"error": f"Unknown rule: {rule_id}"}

    @mcp.tool()
    def stablestack_list_rules(
        tier: str | None = None,
        include_details: bool = False,
    ) -> dict[str, Any]:
        """
        List all available stablestack rules.

        Args:
            tier: Filter by tier ("critical", "recommended", "optional", "preference")
            include_details: Include full details for each rule

        Returns:
            Dictionary containing rules grouped by tier
        """
        from stablestack.checkers import ALL_CHECKERS
        from stablestack.models import Tier

        tier_filter = None
        if tier:
            tier_map = {
                "critical": Tier.CRITICAL,
                "recommended": Tier.RECOMMENDED,
                "optional": Tier.OPTIONAL,
                "preference": Tier.PREFERENCE,
            }
            tier_filter = tier_map.get(tier.lower())

        # Group by tier
        by_tier: dict[str, list] = {
            "CRITICAL": [],
            "RECOMMENDED": [],
            "OPTIONAL": [],
            "PREFERENCE": [],
        }

        for checker_cls in ALL_CHECKERS:
            checker_tier = getattr(checker_cls, 'tier', Tier.RECOMMENDED)

            if tier_filter and checker_tier != tier_filter:
                continue

            rule_info: dict[str, Any] = {
                "rule_id": checker_cls.rule_id,
                "rule_name": checker_cls.rule_name,
                "severity": checker_cls.severity.value,
                "supports_autofix": checker_cls.supports_autofix,
            }

            if include_details:
                rule_info["description"] = checker_cls.description
                rule_info["why"] = checker_cls.why
                rule_info["file_extensions"] = list(checker_cls.file_extensions)

            by_tier[checker_tier.name].append(rule_info)

        # Sort each tier by rule_id
        for tier_name in by_tier:
            by_tier[tier_name].sort(key=lambda x: x["rule_id"])

        return {
            "rules_by_tier": by_tier,
            "total_rules": len(ALL_CHECKERS),
        }

    @mcp.tool()
    def stablestack_fix(
        file_path: str,
        rule_id: str,
        line: int,
        dry_run: bool = True,
    ) -> dict[str, Any]:
        """
        Apply auto-fix for a specific finding.

        Args:
            file_path: Path to the file to fix
            rule_id: The rule ID of the finding to fix
            line: The line number of the finding
            dry_run: If True, return what would change without modifying the file

        Returns:
            Dictionary with fix result (original, fixed content, or error)
        """
        from stablestack.checkers import ALL_CHECKERS
        from stablestack.config import Config
        from stablestack.runner import run_checks

        path = Path(file_path)
        if not path.exists():
            return {"error": f"File not found: {file_path}"}

        # Find the checker
        checker = None
        for checker_cls in ALL_CHECKERS:
            if checker_cls.rule_id == rule_id.upper():
                checker = checker_cls()
                break

        if not checker:
            return {"error": f"Unknown rule: {rule_id}"}

        if not checker.supports_autofix:
            return {"error": f"Rule {rule_id} does not support auto-fix"}

        # Read file
        try:
            content = path.read_text(encoding="utf-8")
        except (OSError, UnicodeDecodeError) as e:
            return {"error": f"Could not read file: {e}"}

        # Find the finding
        findings = checker.check_file(path, content)
        target_finding = None
        for f in findings:
            if f.line == line:
                target_finding = f
                break

        if not target_finding:
            return {"error": f"No finding at line {line} for rule {rule_id}"}

        # Apply fix
        fixed_content = checker.autofix(path, content, target_finding)
        if fixed_content is None or fixed_content == content:
            return {"error": "Could not apply auto-fix"}

        result = {
            "file": file_path,
            "rule_id": rule_id,
            "line": line,
            "original_line": content.splitlines()[line - 1] if line <= len(content.splitlines()) else "",
            "fixed_line": fixed_content.splitlines()[line - 1] if line <= len(fixed_content.splitlines()) else "",
            "dry_run": dry_run,
        }

        if not dry_run:
            try:
                path.write_text(fixed_content, encoding="utf-8")
                result["success"] = True
            except OSError as e:
                return {"error": f"Could not write file: {e}"}
        else:
            result["success"] = True
            result["message"] = "Dry run - no changes made"

        return result

    return mcp


def run_server(transport: str = "stdio", port: int | None = None):
    """Run the MCP server with the specified transport."""
    mcp = create_server()

    if transport == "stdio":
        mcp.run(transport="stdio")
    elif transport == "sse":
        if port is None:
            port = 8080
        mcp.run(transport="sse", sse_port=port)
    else:
        raise ValueError(f"Unknown transport: {transport}")
