"""
Spec resources for foundry-mcp.

Provides MCP resources for accessing specs, journals, and templates.
"""

import json
import logging
from pathlib import Path
from typing import Optional

from mcp.server.fastmcp import FastMCP

from foundry_mcp.config.server import ServerConfig
from foundry_mcp.core.journal import get_journal_entries
from foundry_mcp.core.spec import (
    find_spec_file,
    find_specs_directory,
    list_specs,
    load_spec,
)

logger = logging.getLogger(__name__)


# Schema version for resource responses
SCHEMA_VERSION = "1.0.0"


def register_spec_resources(mcp: FastMCP, config: ServerConfig) -> None:
    """
    Register spec resources with the FastMCP server.

    Args:
        mcp: FastMCP server instance
        config: Server configuration
    """

    def _get_specs_dir(workspace: Optional[str] = None) -> Optional[Path]:
        """Get the specs directory for the given workspace."""
        if workspace:
            ws_path = Path(workspace)
            if ws_path.is_dir():
                specs_path = ws_path / "specs"
                if specs_path.is_dir():
                    return specs_path
                return find_specs_directory(workspace)
        return config.specs_dir or find_specs_directory()

    def _validate_sandbox(path: Path, workspace: Optional[Path] = None) -> bool:
        """
        Validate that a path is within the workspace sandbox.

        Args:
            path: Path to validate
            workspace: Workspace root (defaults to specs_dir parent)

        Returns:
            True if path is within sandbox, False otherwise
        """
        if workspace is None:
            specs_dir = _get_specs_dir()
            if specs_dir:
                workspace = specs_dir.parent
            else:
                return False

        try:
            path.resolve().relative_to(workspace.resolve())
            return True
        except ValueError:
            return False

    # Resource: foundry://specs/ - List all specs
    @mcp.resource("foundry://specs/")
    def resource_specs_list() -> str:
        """
        List all specifications.

        Returns JSON with all specs across all status folders.
        """
        specs_dir = _get_specs_dir()
        if not specs_dir:
            return json.dumps(
                {
                    "success": False,
                    "schema_version": SCHEMA_VERSION,
                    "error": "No specs directory found",
                },
                separators=(",", ":"),
            )

        specs = list_specs(specs_dir=specs_dir)

        return json.dumps(
            {
                "success": True,
                "schema_version": SCHEMA_VERSION,
                "specs": specs,
                "count": len(specs),
            },
            separators=(",", ":"),
        )

    # Resource: foundry://specs/{status}/ - List specs by status
    @mcp.resource("foundry://specs/{status}/")
    def resource_specs_by_status(status: str) -> str:
        """
        List specifications filtered by status.

        Args:
            status: Status folder (active, pending, completed, archived)

        Returns JSON with specs in the specified status folder.
        """
        valid_statuses = {"active", "pending", "completed", "archived"}
        if status not in valid_statuses:
            return json.dumps(
                {
                    "success": False,
                    "schema_version": SCHEMA_VERSION,
                    "error": f"Invalid status: {status}. Must be one of: {', '.join(sorted(valid_statuses))}",
                },
                separators=(",", ":"),
            )

        specs_dir = _get_specs_dir()
        if not specs_dir:
            return json.dumps(
                {
                    "success": False,
                    "schema_version": SCHEMA_VERSION,
                    "error": "No specs directory found",
                },
                separators=(",", ":"),
            )

        specs = list_specs(specs_dir=specs_dir, status=status)

        return json.dumps(
            {
                "success": True,
                "schema_version": SCHEMA_VERSION,
                "status": status,
                "specs": specs,
                "count": len(specs),
            },
            separators=(",", ":"),
        )

    # Resource: foundry://specs/{status}/{spec_id} - Get specific spec
    @mcp.resource("foundry://specs/{status}/{spec_id}")
    def resource_spec_by_status(status: str, spec_id: str) -> str:
        """
        Get a specification by status and ID.

        Args:
            status: Status folder (active, pending, completed, archived)
            spec_id: Specification ID

        Returns JSON with full spec data.
        """
        valid_statuses = {"active", "pending", "completed", "archived"}
        if status not in valid_statuses:
            return json.dumps(
                {
                    "success": False,
                    "schema_version": SCHEMA_VERSION,
                    "error": f"Invalid status: {status}. Must be one of: {', '.join(sorted(valid_statuses))}",
                },
                separators=(",", ":"),
            )

        specs_dir = _get_specs_dir()
        if not specs_dir:
            return json.dumps(
                {
                    "success": False,
                    "schema_version": SCHEMA_VERSION,
                    "error": "No specs directory found",
                },
                separators=(",", ":"),
            )

        # Verify spec is in the specified status folder
        spec_file = specs_dir / status / f"{spec_id}.json"
        if not spec_file.exists():
            return json.dumps(
                {
                    "success": False,
                    "schema_version": SCHEMA_VERSION,
                    "error": f"Spec not found in {status}: {spec_id}",
                },
                separators=(",", ":"),
            )

        # Validate sandbox
        if not _validate_sandbox(spec_file):
            return json.dumps(
                {
                    "success": False,
                    "schema_version": SCHEMA_VERSION,
                    "error": "Access denied: path outside workspace sandbox",
                },
                separators=(",", ":"),
            )

        spec_data = load_spec(spec_id, specs_dir)
        if spec_data is None:
            return json.dumps(
                {
                    "success": False,
                    "schema_version": SCHEMA_VERSION,
                    "error": f"Failed to load spec: {spec_id}",
                },
                separators=(",", ":"),
            )

        # Calculate progress
        hierarchy = spec_data.get("hierarchy", {})
        total_tasks = len(hierarchy)
        completed_tasks = sum(1 for task in hierarchy.values() if task.get("status") == "completed")

        return json.dumps(
            {
                "success": True,
                "schema_version": SCHEMA_VERSION,
                "spec_id": spec_id,
                "status": status,
                "title": spec_data.get("metadata", {}).get("title", spec_data.get("title", "Untitled")),
                "total_tasks": total_tasks,
                "completed_tasks": completed_tasks,
                "progress_percentage": int((completed_tasks / total_tasks * 100)) if total_tasks > 0 else 0,
                "hierarchy": hierarchy,
                "metadata": spec_data.get("metadata", {}),
                "journal": spec_data.get("journal", []),
            },
            separators=(",", ":"),
        )

    # Resource: foundry://specs/{spec_id}/journal - Get spec journal
    @mcp.resource("foundry://specs/{spec_id}/journal")
    def resource_spec_journal(spec_id: str) -> str:
        """
        Get journal entries for a specification.

        Args:
            spec_id: Specification ID

        Returns JSON with journal entries.
        """
        specs_dir = _get_specs_dir()
        if not specs_dir:
            return json.dumps(
                {
                    "success": False,
                    "schema_version": SCHEMA_VERSION,
                    "error": "No specs directory found",
                },
                separators=(",", ":"),
            )

        # Find spec file (in any status folder)
        spec_file = find_spec_file(spec_id, specs_dir)
        if not spec_file:
            return json.dumps(
                {
                    "success": False,
                    "schema_version": SCHEMA_VERSION,
                    "error": f"Spec not found: {spec_id}",
                },
                separators=(",", ":"),
            )

        # Validate sandbox
        if not _validate_sandbox(spec_file):
            return json.dumps(
                {
                    "success": False,
                    "schema_version": SCHEMA_VERSION,
                    "error": "Access denied: path outside workspace sandbox",
                },
                separators=(",", ":"),
            )

        spec_data = load_spec(spec_id, specs_dir)
        if spec_data is None:
            return json.dumps(
                {
                    "success": False,
                    "schema_version": SCHEMA_VERSION,
                    "error": f"Failed to load spec: {spec_id}",
                },
                separators=(",", ":"),
            )

        # Get journal entries
        entries = get_journal_entries(spec_data)

        # Convert to serializable format
        journal_data = [
            {
                "timestamp": entry.timestamp,
                "entry_type": entry.entry_type,
                "title": entry.title,
                "content": entry.content,
                "author": entry.author,
                "task_id": entry.task_id,
                "metadata": entry.metadata,
            }
            for entry in entries
        ]

        return json.dumps(
            {
                "success": True,
                "schema_version": SCHEMA_VERSION,
                "spec_id": spec_id,
                "journal": journal_data,
                "count": len(journal_data),
            },
            separators=(",", ":"),
        )

    # Resource: foundry://templates/ - List available templates
    @mcp.resource("foundry://templates/")
    def resource_templates_list() -> str:
        """
        List available spec templates.

        Returns JSON with template information.
        """
        specs_dir = _get_specs_dir()
        if not specs_dir:
            return json.dumps(
                {
                    "success": False,
                    "schema_version": SCHEMA_VERSION,
                    "error": "No specs directory found",
                },
                separators=(",", ":"),
            )

        # Look for templates in specs/templates/ directory
        templates_dir = specs_dir / "templates"
        templates = []

        if templates_dir.is_dir():
            for template_file in sorted(templates_dir.glob("*.json")):
                try:
                    with open(template_file, "r") as f:
                        template_data = json.load(f)

                    templates.append(
                        {
                            "template_id": template_file.stem,
                            "title": template_data.get("metadata", {}).get(
                                "title", template_data.get("title", template_file.stem)
                            ),
                            "description": template_data.get("metadata", {}).get("description", ""),
                            "file": str(template_file.name),
                        }
                    )
                except (json.JSONDecodeError, IOError):
                    # Skip invalid templates
                    continue

        # Add built-in templates info
        builtin_templates = [
            {
                "template_id": "basic",
                "title": "Basic Spec",
                "description": "Minimal spec with a single phase",
                "builtin": True,
            },
            {
                "template_id": "feature",
                "title": "Feature Development",
                "description": "Standard feature with design, implementation, and verification phases",
                "builtin": True,
            },
            {
                "template_id": "bugfix",
                "title": "Bug Fix",
                "description": "Bug investigation, fix, and verification",
                "builtin": True,
            },
        ]

        return json.dumps(
            {
                "success": True,
                "schema_version": SCHEMA_VERSION,
                "templates": templates,
                "builtin_templates": builtin_templates,
                "count": len(templates),
                "builtin_count": len(builtin_templates),
            },
            separators=(",", ":"),
        )

    # Resource: foundry://templates/{template_id} - Get specific template
    @mcp.resource("foundry://templates/{template_id}")
    def resource_template(template_id: str) -> str:
        """
        Get a specific template by ID.

        Args:
            template_id: Template ID

        Returns JSON with template data.
        """
        specs_dir = _get_specs_dir()
        if not specs_dir:
            return json.dumps(
                {
                    "success": False,
                    "schema_version": SCHEMA_VERSION,
                    "error": "No specs directory found",
                },
                separators=(",", ":"),
            )

        # Check for custom template
        templates_dir = specs_dir / "templates"
        template_file = templates_dir / f"{template_id}.json"

        if template_file.exists():
            # Validate sandbox
            if not _validate_sandbox(template_file):
                return json.dumps(
                    {
                        "success": False,
                        "schema_version": SCHEMA_VERSION,
                        "error": "Access denied: path outside workspace sandbox",
                    },
                    separators=(",", ":"),
                )

            try:
                with open(template_file, "r") as f:
                    template_data = json.load(f)

                return json.dumps(
                    {
                        "success": True,
                        "schema_version": SCHEMA_VERSION,
                        "template_id": template_id,
                        "template": template_data,
                        "builtin": False,
                    },
                    separators=(",", ":"),
                )
            except (json.JSONDecodeError, IOError) as e:
                return json.dumps(
                    {
                        "success": False,
                        "schema_version": SCHEMA_VERSION,
                        "error": f"Failed to load template: {e}",
                    },
                    separators=(",", ":"),
                )

        # Check for builtin template
        builtin_templates = {
            "basic": _get_basic_template(),
            "feature": _get_feature_template(),
            "bugfix": _get_bugfix_template(),
        }

        if template_id in builtin_templates:
            return json.dumps(
                {
                    "success": True,
                    "schema_version": SCHEMA_VERSION,
                    "template_id": template_id,
                    "template": builtin_templates[template_id],
                    "builtin": True,
                },
                separators=(",", ":"),
            )

        return json.dumps(
            {
                "success": False,
                "schema_version": SCHEMA_VERSION,
                "error": f"Template not found: {template_id}",
            },
            separators=(",", ":"),
        )

    logger.debug(
        "Registered spec resources: foundry://specs/, foundry://specs/{status}/, "
        "foundry://specs/{status}/{spec_id}, foundry://specs/{spec_id}/journal, "
        "foundry://templates/, foundry://templates/{template_id}"
    )


def _get_basic_template() -> dict:
    """Get the basic builtin template."""
    return {
        "spec_id": "{{spec_id}}",
        "title": "{{title}}",
        "metadata": {
            "title": "{{title}}",
            "description": "{{description}}",
            "created_at": "{{timestamp}}",
        },
        "hierarchy": {
            "spec-root": {
                "type": "spec",
                "title": "{{title}}",
                "status": "pending",
                "children": ["phase-1"],
            },
            "phase-1": {
                "type": "phase",
                "title": "Implementation",
                "status": "pending",
                "parent": "spec-root",
                "children": ["task-1-1"],
            },
            "task-1-1": {
                "type": "task",
                "title": "Initial task",
                "status": "pending",
                "parent": "phase-1",
                "children": [],
            },
        },
        "journal": [],
    }


def _get_feature_template() -> dict:
    """Get the feature development builtin template."""
    return {
        "spec_id": "{{spec_id}}",
        "title": "{{title}}",
        "metadata": {
            "title": "{{title}}",
            "description": "{{description}}",
            "created_at": "{{timestamp}}",
        },
        "hierarchy": {
            "spec-root": {
                "type": "spec",
                "title": "{{title}}",
                "status": "pending",
                "children": ["phase-1", "phase-2", "phase-3"],
            },
            "phase-1": {
                "type": "phase",
                "title": "Design",
                "status": "pending",
                "parent": "spec-root",
                "children": ["task-1-1"],
            },
            "task-1-1": {
                "type": "task",
                "title": "Design document",
                "status": "pending",
                "parent": "phase-1",
                "children": [],
            },
            "phase-2": {
                "type": "phase",
                "title": "Implementation",
                "status": "pending",
                "parent": "spec-root",
                "children": ["task-2-1"],
            },
            "task-2-1": {
                "type": "task",
                "title": "Core implementation",
                "status": "pending",
                "parent": "phase-2",
                "children": [],
            },
            "phase-3": {
                "type": "phase",
                "title": "Verification",
                "status": "pending",
                "parent": "spec-root",
                "children": ["verify-3-1"],
            },
            "verify-3-1": {
                "type": "verify",
                "title": "All tests pass",
                "status": "pending",
                "parent": "phase-3",
                "children": [],
                "metadata": {
                    "verification_type": "run-tests",
                },
            },
        },
        "journal": [],
    }


def _get_bugfix_template() -> dict:
    """Get the bugfix builtin template."""
    return {
        "spec_id": "{{spec_id}}",
        "title": "{{title}}",
        "metadata": {
            "title": "{{title}}",
            "description": "{{description}}",
            "created_at": "{{timestamp}}",
        },
        "hierarchy": {
            "spec-root": {
                "type": "spec",
                "title": "{{title}}",
                "status": "pending",
                "children": ["phase-1", "phase-2"],
            },
            "phase-1": {
                "type": "phase",
                "title": "Investigation",
                "status": "pending",
                "parent": "spec-root",
                "children": ["task-1-1", "task-1-2"],
            },
            "task-1-1": {
                "type": "task",
                "title": "Reproduce bug",
                "status": "pending",
                "parent": "phase-1",
                "children": [],
            },
            "task-1-2": {
                "type": "task",
                "title": "Root cause analysis",
                "status": "pending",
                "parent": "phase-1",
                "children": [],
            },
            "phase-2": {
                "type": "phase",
                "title": "Fix & Verify",
                "status": "pending",
                "parent": "spec-root",
                "children": ["task-2-1", "verify-2-1"],
            },
            "task-2-1": {
                "type": "task",
                "title": "Implement fix",
                "status": "pending",
                "parent": "phase-2",
                "children": [],
            },
            "verify-2-1": {
                "type": "verify",
                "title": "Bug no longer reproduces",
                "status": "pending",
                "parent": "phase-2",
                "children": [],
                "metadata": {
                    "verification_type": "manual",
                },
            },
        },
        "journal": [],
    }
