"""LLM-based SpecCard generation."""

import json
from pathlib import Path
from typing import Optional

from guardrail.llm.client import LLMClient
from guardrail.models import SpecCard


class SpecGenerator:
    """Generates SpecCard JSON from function source using LLM."""

    SYSTEM_PROMPT = """You are an expert Python code analyzer. Your task is to analyze Python functions and generate detailed specifications in JSON format.

Generate a SpecCard JSON object with the following structure:
- function_name: Name of the function
- module_path: Module path (e.g., "my_module" or "my_package.my_module")
- signature: Full function signature as a string
- description: Clear description of what the function does
- inputs: List of objects with "name", "type" (if available), and "description" fields
- expected_behavior: List of 2-5 bullet points describing expected behavior
- edge_cases: List of 5-12 edge cases that should be tested
- side_effects: Boolean indicating if function has side effects
- side_effects_reason: String explaining side effects if any (null if none)
- confidence: "low", "medium", or "high" based on how clear the function is

Return ONLY valid JSON, no markdown, no code blocks, just the JSON object."""

    PROMPT_TEMPLATE = """Analyze the following Python function CAREFULLY by reading the actual code:

Function source code:
```python
{function_source}
```

File path: {file_path}

CRITICAL INSTRUCTIONS:
1. FIRST, read and understand what the function ACTUALLY does by analyzing the code implementation
2. Identify the REAL parameter types from the code (look at how parameters are used, not assumptions)
3. Only suggest edge cases that make logical sense for this specific function's implementation
4. Do NOT suggest edge cases that would cause type errors (e.g., don't suggest passing a boolean to a function that performs arithmetic)
5. Base edge cases on the function's actual implementation and parameter usage, not generic patterns
6. If the function uses specific types (int, float, str), only suggest edge cases with compatible types

Generate a comprehensive SpecCard JSON that accurately reflects what this function ACTUALLY does:
1. Understanding the function's REAL purpose and behavior from the code
2. Identifying actual input parameter types from code usage
3. Listing expected behaviors based on implementation
4. Identifying REALISTIC edge cases (boundary conditions, error cases, special inputs that make sense)
5. Determining if there are any side effects

Return ONLY the JSON object, no other text."""

    def __init__(self, llm_client: Optional[LLMClient] = None):
        """
        Initialize spec generator.

        Args:
            llm_client: Optional LLM client. If None, creates one with default settings.
        """
        self.llm_client = llm_client or LLMClient()

    def generate_spec(
        self, function_source: str, file_path: Path, function_name: str
    ) -> SpecCard:
        """
        Generate SpecCard for a function.

        Args:
            function_source: Source code of the function.
            file_path: Path to the file containing the function.
            function_name: Name of the function.

        Returns:
            SpecCard object.

        Raises:
            ValueError: If generated JSON is invalid.
            RuntimeError: If LLM call fails.
        """
        # Convert file path to module path
        module_path = self._path_to_module_path(file_path)

        # Build prompt
        prompt = self.PROMPT_TEMPLATE.format(
            function_source=function_source, file_path=str(file_path)
        )

        # Generate spec using LLM
        response = self.llm_client.generate(
            prompt=prompt,
            system_prompt=self.SYSTEM_PROMPT,
            temperature=0.3,
            max_tokens=2000,
        )

        # Parse JSON response
        try:
            # Try to extract JSON if wrapped in markdown code blocks
            response = response.strip()
            if response.startswith("```"):
                # Remove markdown code blocks
                lines = response.split("\n")
                response = "\n".join(lines[1:-1]) if len(lines) > 2 else response
            if response.startswith("```json"):
                lines = response.split("\n")
                response = "\n".join(lines[1:-1]) if len(lines) > 2 else response

            spec_dict = json.loads(response)
        except json.JSONDecodeError as e:
            raise ValueError(
                f"Failed to parse LLM response as JSON: {e}\nResponse: {response[:500]}"
            ) from e

        # Validate and create SpecCard
        try:
            spec_card = SpecCard(**spec_dict)
            # Override with actual values
            spec_card.function_name = function_name
            spec_card.module_path = module_path
            return spec_card
        except Exception as e:
            raise ValueError(
                f"Failed to create SpecCard from LLM response: {e}\nResponse: {response[:500]}"
            ) from e

    def _path_to_module_path(self, file_path: Path) -> str:
        """
        Convert file path to module path.

        Args:
            file_path: Path to Python file.

        Returns:
            Module path string (e.g., "my_module" or "my_package.my_module").
        """
        # Remove .py extension
        stem = file_path.stem

        # If it's __init__.py, use parent directory
        if stem == "__init__":
            stem = file_path.parent.name

        # For now, just return the stem
        # In a full implementation, we'd resolve relative to project root
        return stem

