"""Spec tools for planning phase of coding agent - free-form markdown."""

from pathlib import Path
from typing import Optional

from emdash.agent.tools.base import BaseTool, ToolResult, ToolCategory
from emdash.agent.spec_schema import Spec, SPEC_TEMPLATE


class SpecState:
    """Holds current spec in memory with optional disk persistence."""

    _instance: Optional["SpecState"] = None

    def __init__(self):
        self.spec: Optional[Spec] = None
        self.save_path: Optional[Path] = None
        self.approved: bool = False
        self.refinements: list[str] = []

    @classmethod
    def get_instance(cls) -> "SpecState":
        """Get or create singleton instance."""
        if cls._instance is None:
            cls._instance = cls()
        return cls._instance

    @classmethod
    def reset(cls):
        """Reset the singleton instance."""
        cls._instance = None

    def configure(self, save_path: Optional[Path] = None):
        """Configure spec state with optional save path."""
        self.save_path = save_path
        if save_path:
            save_path.parent.mkdir(parents=True, exist_ok=True)

    def submit(self, title: str, content: str) -> bool:
        """Submit a new spec.

        Args:
            title: Feature title
            content: Markdown content

        Returns:
            True if saved successfully
        """
        self.spec = Spec(title=title, content=content)
        self.approved = False

        if self.save_path:
            return self._write_to_disk()
        return True

    def update(self, content: str) -> bool:
        """Update the current spec content.

        Args:
            content: New markdown content

        Returns:
            True if updated successfully
        """
        if not self.spec:
            return False

        self.spec.content = content

        if self.save_path:
            return self._write_to_disk()
        return True

    def approve(self):
        """Mark spec as approved."""
        self.approved = True

    def add_refinement(self, refinement: str):
        """Track a user refinement."""
        self.refinements.append(refinement)

    def _write_to_disk(self) -> bool:
        """Write spec to disk as markdown."""
        if not self.save_path or not self.spec:
            return False

        try:
            with open(self.save_path, "w") as f:
                f.write(self.spec.to_markdown())
            return True
        except Exception:
            return False

    def get_markdown(self) -> str:
        """Get spec as markdown."""
        if not self.spec:
            return ""
        return self.spec.to_markdown()

    def get_summary(self) -> dict:
        """Get a summary of the current spec state."""
        if not self.spec:
            return {"has_spec": False}

        return {
            "has_spec": True,
            "title": self.spec.title,
            "approved": self.approved,
            "saved_to_disk": self.save_path is not None,
            "refinements": len(self.refinements),
        }


class SubmitSpecTool(BaseTool):
    """Submit a feature specification in markdown format."""

    name = "submit_spec"
    description = """Submit a feature specification in markdown format. Call this after exploring
the codebase and understanding the requirements. Write a clear, structured spec that will
guide the implementation phase.

The spec should include:
- Title and summary
- Problem description
- Solution overview
- Implementation steps
- Related files
- Edge cases (optional)
- Open questions (optional)"""
    category = ToolCategory.PLANNING

    def execute(self, title: str, content: str) -> ToolResult:
        """Submit a spec.

        Args:
            title: Feature name/title
            content: Markdown content of the spec (problem, solution, implementation, etc.)

        Returns:
            ToolResult with submission status
        """
        if not title or not title.strip():
            return ToolResult.error_result("Title is required")

        if not content or not content.strip():
            return ToolResult.error_result("Content is required")

        state = SpecState.get_instance()
        success = state.submit(title.strip(), content.strip())

        if success:
            return ToolResult.success_result({
                "status": "submitted",
                "title": title,
                "saved_to_disk": state.save_path is not None,
                "message": "Spec submitted. Waiting for user approval before implementation.",
            })
        else:
            return ToolResult.error_result("Failed to save spec")

    def get_schema(self) -> dict:
        """Return the OpenAI function schema."""
        return self._make_schema(
            properties={
                "title": {
                    "type": "string",
                    "description": "Feature name/title",
                },
                "content": {
                    "type": "string",
                    "description": "Markdown content of the spec including problem, solution, implementation steps, related files, edge cases, etc.",
                },
            },
            required=["title", "content"],
        )


class GetSpecTool(BaseTool):
    """Retrieve the current spec."""

    name = "get_spec"
    description = "Get the current feature specification. Use this to reference the plan during implementation."
    category = ToolCategory.PLANNING

    def execute(self) -> ToolResult:
        """Get the current spec.

        Returns:
            ToolResult with spec data
        """
        state = SpecState.get_instance()

        if not state.spec:
            return ToolResult.error_result(
                "No spec has been submitted yet",
                suggestions=["Use submit_spec to create a specification first"],
            )

        return ToolResult.success_result({
            "title": state.spec.title,
            "content": state.spec.content,
            "markdown": state.spec.to_markdown(),
            "approved": state.approved,
        })

    def get_schema(self) -> dict:
        return self._make_schema(properties={}, required=[])


class UpdateSpecTool(BaseTool):
    """Update the current spec with new content."""

    name = "update_spec"
    description = """Update the current spec based on user feedback. IMPORTANT: Preserve ALL existing content
and only modify the specific parts that need to change based on the feedback. Do NOT shorten or summarize
the spec - keep all implementation steps, edge cases, related files, and open questions intact.
Only add, modify, or remove content that directly addresses the user's feedback."""
    category = ToolCategory.PLANNING

    def execute(self, content: str) -> ToolResult:
        """Update the spec.

        Args:
            content: New markdown content for the spec

        Returns:
            ToolResult with update status
        """
        state = SpecState.get_instance()

        if not state.spec:
            return ToolResult.error_result(
                "No spec to update",
                suggestions=["Use submit_spec first"],
            )

        if not content or not content.strip():
            return ToolResult.error_result("Content is required")

        success = state.update(content.strip())

        if success:
            return ToolResult.success_result({
                "status": "updated",
                "title": state.spec.title,
                "saved_to_disk": state.save_path is not None,
            })
        else:
            return ToolResult.error_result("Failed to update spec")

    def get_schema(self) -> dict:
        return self._make_schema(
            properties={
                "content": {
                    "type": "string",
                    "description": "New markdown content for the spec",
                },
            },
            required=["content"],
        )
