"""
Domain models for history tracking.

Defines data structures for commits, branches, diffs, and history entries.
"""

import uuid
from dataclasses import dataclass, field
from datetime import datetime
from typing import Dict, Any, List, Optional
from enum import Enum


class DiffType(Enum):
    """Type of diff line."""
    CONTEXT = "context"   # Unchanged line
    ADDITION = "addition"  # Added line
    DELETION = "deletion"  # Removed line


@dataclass
class DiffLine:
    """
    A single line in a diff.

    Attributes:
        line_type: Type of line (context, addition, deletion)
        content: Line content
        old_line_no: Line number in old version (None for additions)
        new_line_no: Line number in new version (None for deletions)
    """
    line_type: DiffType
    content: str
    old_line_no: Optional[int] = None
    new_line_no: Optional[int] = None


@dataclass
class DiffResult:
    """
    Result of comparing two versions.

    Attributes:
        path: File path being compared
        old_version: Old version identifier
        new_version: New version identifier
        lines: List of diff lines
        additions: Number of lines added
        deletions: Number of lines deleted
        is_binary: Whether the file is binary
    """
    path: str
    old_version: Optional[str] = None
    new_version: Optional[str] = None
    lines: List[DiffLine] = field(default_factory=list)
    additions: int = 0
    deletions: int = 0
    is_binary: bool = False

    def to_unified_diff(self) -> str:
        """Generate unified diff format string."""
        result = []
        result.append(f"--- a/{self.path}")
        result.append(f"+++ b/{self.path}")

        for line in self.lines:
            if line.line_type == DiffType.ADDITION:
                result.append(f"+{line.content}")
            elif line.line_type == DiffType.DELETION:
                result.append(f"-{line.content}")
            else:
                result.append(f" {line.content}")

        return "\n".join(result)


@dataclass
class Commit:
    """
    Represents a commit in the history.

    Attributes:
        id: Commit identifier (hash)
        message: Commit message
        author: Author name/email
        timestamp: Commit timestamp
        parent_ids: Parent commit IDs
        space_id: Associated space ID
        files_changed: List of changed file paths
        metadata: Additional commit metadata
    """
    id: str
    message: str
    author: str = "markitect"
    timestamp: datetime = field(default_factory=datetime.now)
    parent_ids: List[str] = field(default_factory=list)
    space_id: Optional[str] = None
    files_changed: List[str] = field(default_factory=list)
    metadata: Dict[str, Any] = field(default_factory=dict)

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "id": self.id,
            "message": self.message,
            "author": self.author,
            "timestamp": self.timestamp.isoformat(),
            "parent_ids": self.parent_ids,
            "space_id": self.space_id,
            "files_changed": self.files_changed,
            "metadata": self.metadata,
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Commit":
        """Create from dictionary."""
        timestamp = data.get("timestamp")
        if isinstance(timestamp, str):
            timestamp = datetime.fromisoformat(timestamp)
        elif timestamp is None:
            timestamp = datetime.now()

        return cls(
            id=data["id"],
            message=data.get("message", ""),
            author=data.get("author", "markitect"),
            timestamp=timestamp,
            parent_ids=data.get("parent_ids", []),
            space_id=data.get("space_id"),
            files_changed=data.get("files_changed", []),
            metadata=data.get("metadata", {}),
        )

    @property
    def short_id(self) -> str:
        """Get short commit ID (first 7 chars)."""
        return self.id[:7] if len(self.id) >= 7 else self.id


@dataclass
class Branch:
    """
    Represents a branch in the history.

    Attributes:
        name: Branch name
        head_commit_id: Current commit ID
        is_current: Whether this is the current branch
        tracking: Remote tracking branch (if any)
        created_at: When the branch was created
    """
    name: str
    head_commit_id: str
    is_current: bool = False
    tracking: Optional[str] = None
    created_at: Optional[datetime] = None

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "name": self.name,
            "head_commit_id": self.head_commit_id,
            "is_current": self.is_current,
            "tracking": self.tracking,
            "created_at": self.created_at.isoformat() if self.created_at else None,
        }


@dataclass
class HistoryEntry:
    """
    A single entry in the history log.

    Combines commit information with change summary.

    Attributes:
        commit: The commit
        summary: Short summary of changes
        files_added: Number of files added
        files_modified: Number of files modified
        files_deleted: Number of files deleted
    """
    commit: Commit
    summary: str = ""
    files_added: int = 0
    files_modified: int = 0
    files_deleted: int = 0

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "commit": self.commit.to_dict(),
            "summary": self.summary,
            "files_added": self.files_added,
            "files_modified": self.files_modified,
            "files_deleted": self.files_deleted,
        }


@dataclass
class HistoryConfig:
    """
    Configuration for history tracking.

    Attributes:
        enabled: Whether history tracking is enabled
        backend: Backend type ("git" or "sqlite")
        auto_commit: Whether to auto-commit on changes
        commit_on_events: List of event types that trigger commits
        author_name: Default author name
        author_email: Default author email
        directory: Canonical directory path (for git)
        options: Additional backend-specific options
    """
    enabled: bool = False
    backend: str = "git"
    auto_commit: bool = True
    commit_on_events: List[str] = field(default_factory=lambda: [
        "DOCUMENT_ADDED",
        "DOCUMENT_REMOVED",
        "DOCUMENT_CONTENT_CHANGED",
    ])
    author_name: str = "markitect"
    author_email: str = "markitect@local"
    directory: Optional[str] = None
    options: Dict[str, Any] = field(default_factory=dict)

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "enabled": self.enabled,
            "backend": self.backend,
            "auto_commit": self.auto_commit,
            "commit_on_events": self.commit_on_events,
            "author_name": self.author_name,
            "author_email": self.author_email,
            "directory": self.directory,
            "options": self.options,
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "HistoryConfig":
        """Create from dictionary."""
        return cls(
            enabled=data.get("enabled", False),
            backend=data.get("backend", "git"),
            auto_commit=data.get("auto_commit", True),
            commit_on_events=data.get("commit_on_events", [
                "DOCUMENT_ADDED",
                "DOCUMENT_REMOVED",
                "DOCUMENT_CONTENT_CHANGED",
            ]),
            author_name=data.get("author_name", "markitect"),
            author_email=data.get("author_email", "markitect@local"),
            directory=data.get("directory"),
            options=data.get("options", {}),
        )
