"""
Data models for Bolor code repair.

This module defines the data structures used to represent code issues,
fixes, and suggestions in the Bolor system.
"""

import uuid
from dataclasses import dataclass, field
from enum import Enum
from pathlib import Path
from typing import Dict, List, Optional, Union, Any


class IssueType(str, Enum):
    """Types of code issues that can be detected."""
    SYNTAX_ERROR = "syntax_error"
    RUNTIME_ERROR = "runtime_error"
    LOGICAL_ERROR = "logical_error"
    PERFORMANCE_ISSUE = "performance_issue"
    SECURITY_VULNERABILITY = "security_vulnerability"
    CODE_SMELL = "code_smell"
    STYLE_ISSUE = "style_issue"
    DEPLOYMENT_ISSUE = "deployment_issue"
    OTHER = "other"


class IssueStatus(str, Enum):
    """Status of an issue in the repair process."""
    DETECTED = "detected"
    ANALYZING = "analyzing"
    FIX_GENERATED = "fix_generated"
    FIXED = "fixed"
    UNFIXABLE = "unfixable"
    IGNORED = "ignored"


@dataclass
class Issue:
    """
    Represents a code issue detected by the scanner.
    
    This class stores information about a detected code issue,
    including its location, description, and relevant code snippets.
    """
    
    # Required fields
    file_path: Path
    issue_type: IssueType
    description: str
    
    # Optional fields with defaults
    line_number: Optional[int] = None
    column_number: Optional[int] = None
    code_snippet: Optional[str] = None
    end_line_number: Optional[int] = None
    end_column_number: Optional[int] = None
    
    # Fields for tracking fixes
    status: IssueStatus = IssueStatus.DETECTED
    fix_attempts: List[Dict[str, Any]] = field(default_factory=list)
    fixed_code_snippet: Optional[str] = None
    confidence_score: Optional[float] = None
    
    # Metadata for additional information
    metadata: Dict[str, Any] = field(default_factory=dict)
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert the issue to a dictionary representation."""
        return {
            "file_path": str(self.file_path),
            "issue_type": self.issue_type.value,
            "description": self.description,
            "line_number": self.line_number,
            "column_number": self.column_number,
            "code_snippet": self.code_snippet,
            "end_line_number": self.end_line_number,
            "end_column_number": self.end_column_number,
            "status": self.status.value,
            "fix_attempts": self.fix_attempts,
            "fixed_code_snippet": self.fixed_code_snippet,
            "confidence_score": self.confidence_score,
            "metadata": self.metadata,
        }
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Issue":
        """Create an issue from a dictionary representation."""
        file_path = Path(data["file_path"])
        issue_type = IssueType(data["issue_type"])
        status = IssueStatus(data["status"]) if "status" in data else IssueStatus.DETECTED
        
        return cls(
            file_path=file_path,
            issue_type=issue_type,
            description=data["description"],
            line_number=data.get("line_number"),
            column_number=data.get("column_number"),
            code_snippet=data.get("code_snippet"),
            end_line_number=data.get("end_line_number"),
            end_column_number=data.get("end_column_number"),
            status=status,
            fix_attempts=data.get("fix_attempts", []),
            fixed_code_snippet=data.get("fixed_code_snippet"),
            confidence_score=data.get("confidence_score"),
            metadata=data.get("metadata", {}),
        )
    
    def __str__(self) -> str:
        """Get a string representation of the issue."""
        location = f"{self.file_path}"
        if self.line_number:
            location += f":{self.line_number}"
            if self.column_number:
                location += f":{self.column_number}"
        
        return f"{self.issue_type.value}: {self.description} at {location}"


@dataclass
class Suggestion:
    """
    Represents an improvement suggestion generated by the planner.
    
    This class stores information about a suggested improvement,
    including its type, description, and example code.
    """
    
    # Required fields
    title: str
    type: str
    description: str
    
    # Optional fields
    file_path: Optional[Path] = None
    line_number: Optional[int] = None
    code_example: Optional[str] = None
    
    # Priority and rationale
    priority: Optional[str] = None
    rationale: Optional[str] = None
    
    # References and sources
    references: List[str] = field(default_factory=list)
    
    # Metadata for additional information
    metadata: Dict[str, Any] = field(default_factory=dict)
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert the suggestion to a dictionary representation."""
        return {
            "title": self.title,
            "type": self.type,
            "description": self.description,
            "file_path": str(self.file_path) if self.file_path else None,
            "line_number": self.line_number,
            "code_example": self.code_example,
            "priority": self.priority,
            "rationale": self.rationale,
            "references": self.references,
            "metadata": self.metadata,
        }
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Suggestion":
        """Create a suggestion from a dictionary representation."""
        file_path = Path(data["file_path"]) if data.get("file_path") else None
        
        return cls(
            title=data["title"],
            type=data["type"],
            description=data["description"],
            file_path=file_path,
            line_number=data.get("line_number"),
            code_example=data.get("code_example"),
            priority=data.get("priority"),
            rationale=data.get("rationale"),
            references=data.get("references", []),
            metadata=data.get("metadata", {}),
        )
    
    def __str__(self) -> str:
        """Get a string representation of the suggestion."""
        result = f"{self.title}: {self.description}"
        if self.file_path:
            result += f" (in {self.file_path})"
        return result


@dataclass
class FixCandidate:
    """
    Represents a candidate fix generated for a code issue.
    
    This class is used in the evolutionary algorithm to represent
    potential fixes that can be evolved and evaluated.
    """
    
    # Required fields
    issue: Issue
    modified_code: str
    
    # Fitness and evaluation
    fitness: float = 0.0
    errors: List[str] = field(default_factory=list)
    is_valid: bool = True
    
    # Metadata for tracking evolution
    generation: int = 0
    parent_ids: List[str] = field(default_factory=list)
    id: str = field(default_factory=lambda: uuid.uuid4().hex)
    
    # Additional metadata
    metadata: Dict[str, Any] = field(default_factory=dict)
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert the fix candidate to a dictionary representation."""
        return {
            "issue": self.issue.to_dict(),
            "modified_code": self.modified_code,
            "fitness": self.fitness,
            "errors": self.errors,
            "is_valid": self.is_valid,
            "generation": self.generation,
            "parent_ids": self.parent_ids,
            "id": self.id,
            "metadata": self.metadata,
        }
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "FixCandidate":
        """Create a fix candidate from a dictionary representation."""
        issue = Issue.from_dict(data["issue"])
        
        return cls(
            issue=issue,
            modified_code=data["modified_code"],
            fitness=data.get("fitness", 0.0),
            errors=data.get("errors", []),
            is_valid=data.get("is_valid", True),
            generation=data.get("generation", 0),
            parent_ids=data.get("parent_ids", []),
            id=data.get("id", uuid.uuid4().hex),
            metadata=data.get("metadata", {}),
        )


@dataclass
class DeploymentIssue:
    """
    Represents a deployment issue detected by the scanner.
    
    This class stores information about a potential deployment issue,
    such as configuration problems or missing dependencies.
    """
    
    # Required fields
    file_path: Optional[Path]
    description: str
    
    # Additional details
    details: Optional[str] = None
    severity: str = "warning"
    
    # Suggestions for fixing
    suggestions: List[str] = field(default_factory=list)
    
    # Metadata for additional information
    metadata: Dict[str, Any] = field(default_factory=dict)
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert the deployment issue to a dictionary representation."""
        return {
            "file_path": str(self.file_path) if self.file_path else None,
            "description": self.description,
            "details": self.details,
            "severity": self.severity,
            "suggestions": self.suggestions,
            "metadata": self.metadata,
        }
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "DeploymentIssue":
        """Create a deployment issue from a dictionary representation."""
        file_path = Path(data["file_path"]) if data.get("file_path") else None
        
        return cls(
            file_path=file_path,
            description=data["description"],
            details=data.get("details"),
            severity=data.get("severity", "warning"),
            suggestions=data.get("suggestions", []),
            metadata=data.get("metadata", {}),
        )
    
    def __str__(self) -> str:
        """Get a string representation of the deployment issue."""
        result = f"{self.severity.upper()}: {self.description}"
        if self.file_path:
            result += f" (in {self.file_path})"
        return result
