"""
Utility functions for saving patches to the database.
"""
import hashlib
import logging
from typing import Dict, Any, Optional
from sqlmodel import Session, select
from app.models import PatchRecord, PatchType, PatchStatus
from app.utils.patch_utils import count_hunks

logger = logging.getLogger(__name__)


def calculate_file_hash(file_path: str) -> Optional[str]:
    """Calculate SHA256 hash of file content.

    Args:
        file_path: Path to the file

    Returns:
        Hex string of SHA256 hash, or None if file cannot be read
    """
    try:
        with open(file_path, "rb") as f:
            content = f.read()
        return hashlib.sha256(content).hexdigest()
    except Exception as e:
        logger.error(f"Failed to calculate hash for {file_path}: {e}")
        return None


def save_patch_to_db(
    session: Session,
    file_path: str,
    finding_fingerprint: str,
    patch_diff: str,
    patch_type: PatchType,
    source_data: Dict[str, Any],
    user_id: str,
    lines_added: int = 0,
    lines_removed: int = 0,
) -> Optional[str]:
    """Save or update a patch record in the database.

    Args:
        session: Database session
        file_path: Absolute path to the target file
        finding_fingerprint: Unique identifier for the finding
        patch_diff: Git unified diff format patch
        patch_type: Type of patch (SCANNER or AI)
        source_data: Metadata about the finding/fix
        user_id: User who owns this patch (from JWT token)
        lines_added: Number of lines added
        lines_removed: Number of lines removed

    Returns:
        Patch record ID if successful, None otherwise
    """
    try:
        # Calculate file hash (best-effort — may fail for relative or moved paths)
        content_hash = calculate_file_hash(file_path) or "unknown"

        # Count hunks
        hunks = count_hunks(patch_diff)

        # Check if patch already exists for this finding
        statement = select(PatchRecord).where(
            PatchRecord.file_path == file_path,
            PatchRecord.finding_fingerprint == finding_fingerprint
        )
        existing = session.exec(statement).first()

        if existing:
            # Update existing patch
            existing.patch_diff = patch_diff
            existing.patch_type = patch_type
            existing.content_hash = content_hash
            existing.user_id = user_id  # Update user_id to current user
            existing.lines_added = lines_added
            existing.lines_removed = lines_removed
            existing.hunks_count = hunks
            existing.source_data = source_data
            existing.status = PatchStatus.PENDING  # Reset to pending on update
            existing.applied_at = None

            session.add(existing)
            session.commit()
            session.refresh(existing)

            logger.info(f"Updated existing patch {existing.id} for {file_path}")
            return existing.id
        else:
            # Create new patch
            patch = PatchRecord(
                file_path=file_path,
                finding_fingerprint=finding_fingerprint,
                patch_diff=patch_diff,
                patch_type=patch_type,
                content_hash=content_hash,
                user_id=user_id,
                lines_added=lines_added,
                lines_removed=lines_removed,
                hunks_count=hunks,
                source_data=source_data,
                status=PatchStatus.PENDING,
            )

            session.add(patch)
            session.commit()
            session.refresh(patch)

            logger.info(f"Created new patch {patch.id} for {file_path}")
            return patch.id

    except Exception as e:
        logger.exception(f"Failed to save patch for {file_path}: {e}")
        session.rollback()
        return None
