from pydantic import BaseModel, Field
from typing import List, Literal, Optional


class FileChange(BaseModel):
    """
    Represents a change to a single file.
    """
    file_path: str = Field(
        ..., 
        description="The full path of the file being modified (e.g., 'src/utils/parser.py')."
    )
    change_type: Literal["modify", "create", "delete"] = Field(
        default="modify",
        description="Whether this file is being edited, created new, or removed."
    )
    diff_content: str = Field(
        ..., 
        description=(
            "The standard Unified Diff of the change. "
            "Must include '@@' headers and 3 lines of context around changes."
        )
    )

    def to_str(self) -> str:
        """Returns a string representation of the FileChange."""
        return f"File: {self.file_path}\nType: {self.change_type}\nDiff:\n{self.diff_content}"



class ChangeSet(BaseModel):
    """
    A logical grouping of changes across multiple files to achieve a single goal.
    """
    goal: str = Field(
        ..., 
        description="High-level description of WHY this change is happening (e.g., 'Fix parsing bug in date conversion')."
    )
    
    # Renaming 'Code_diffs' to 'changes' for clarity
    changes: List[FileChange]
    
    related_infrastructure: str | None = Field(
        None, 
        description="A list of specific system components, external services, or resources "
            "that this code interacts with or affects. "
            "Be specific (e.g., 'PostgreSQL: Users Table', 'Redis Cache', 'AWS S3', 'Stripe API', 'Kafka'). "
            "Do not list generic terms like 'Backend' or 'Server'."
    )
    
    additional_context: str | None = Field(
        None,
        description="Any extra notes, ticket numbers, or constraints which are important to this area of the code and it's meanning."
    )

    def to_embedding_string(self) -> str:
        """Returns a string representation of the ChangeSet for embedding, excluding changes."""
        parts = [f"Goal: {self.goal}"]
        if self.related_infrastructure:
            parts.append(f"Infrastructure: {self.related_infrastructure}")
        if self.additional_context:
            parts.append(f"Context: {self.additional_context}")
        return "\n".join(parts)


class RiskAssessmentFeedback(BaseModel):
    """Feedback on a single risk assessment produced by puli_herd."""

    assessment_severity: Literal["low", "medium", "high", "critical"] = Field(
        ...,
        description="Severity level of the risk assessment.",
    )
    assessment_description: str = Field(
        ...,
        description="Short sentence explaining the risk identified by Puli.",
    )
    was_fixed: bool = Field(
        ...,
        description="Whether the identified risk was actually fixed.",
    )
    feedback: str = Field(
        ...,
        description=(
            "One short sentence about whether this analysis was meaningful, "
            "and if it was rejected, why."
        ),
    )


class CodingSessionAssessmentSummary(BaseModel):
    """Summary of a completed coding session with feedback on Puli's assessments."""

    context: str = Field(
        ...,
        description=(
            "A generic sentence describing the change and relevant domain. "
            "Do NOT include company names, product names, or any sensitive/IP information. "
            "Good example: 'Bugfix in multi-currency conversion'. "
            "Bad example: 'Bug fix in <Company>'s trading product'."
        ),
    )
    report_summary: List[RiskAssessmentFeedback] = Field(
        ...,
        min_length=1,
        max_length=100,
        description="List of individual assessment feedback items from the session (max 100).",
    )
