"""
Compliance repositories — extracted from compliance/db/repository.py.

Phase 1 Step 5: the monolithic repository module is decomposed per
aggregate. Every repository class is re-exported from
``compliance.db.repository`` for backwards compatibility.
"""

import uuid
from datetime import datetime, date, timezone
from typing import List, Optional, Dict, Any, Tuple

from sqlalchemy.orm import Session as DBSession, selectinload, joinedload
from sqlalchemy import func, and_, or_

from compliance.db.models import (
    RegulationDB, RequirementDB, ControlDB, ControlMappingDB,
    EvidenceDB, RiskDB, AuditExportDB,
    AuditSessionDB, AuditSignOffDB, AuditResultEnum, AuditSessionStatusEnum,
    RegulationTypeEnum, ControlDomainEnum, ControlStatusEnum,
    RiskLevelEnum, EvidenceStatusEnum, ExportStatusEnum,
    ServiceModuleDB, ModuleRegulationMappingDB,
)

class EvidenceRepository:
    """Repository for evidence."""

    def __init__(self, db: DBSession):
        self.db = db

    def create(
        self,
        control_id: str,
        evidence_type: str,
        title: str,
        description: Optional[str] = None,
        artifact_path: Optional[str] = None,
        artifact_url: Optional[str] = None,
        artifact_hash: Optional[str] = None,
        file_size_bytes: Optional[int] = None,
        mime_type: Optional[str] = None,
        valid_until: Optional[datetime] = None,
        source: str = "manual",
        ci_job_id: Optional[str] = None,
        uploaded_by: Optional[str] = None,
    ) -> EvidenceDB:
        """Create evidence record."""
        # Get control UUID
        control = self.db.query(ControlDB).filter(ControlDB.control_id == control_id).first()
        if not control:
            raise ValueError(f"Control {control_id} not found")

        evidence = EvidenceDB(
            id=str(uuid.uuid4()),
            control_id=control.id,
            evidence_type=evidence_type,
            title=title,
            description=description,
            artifact_path=artifact_path,
            artifact_url=artifact_url,
            artifact_hash=artifact_hash,
            file_size_bytes=file_size_bytes,
            mime_type=mime_type,
            valid_until=valid_until,
            source=source,
            ci_job_id=ci_job_id,
            uploaded_by=uploaded_by,
        )
        self.db.add(evidence)
        self.db.commit()
        self.db.refresh(evidence)
        return evidence

    def get_by_id(self, evidence_id: str) -> Optional[EvidenceDB]:
        """Get evidence by ID."""
        return self.db.query(EvidenceDB).filter(EvidenceDB.id == evidence_id).first()

    def get_by_control(
        self,
        control_id: str,
        status: Optional[EvidenceStatusEnum] = None
    ) -> List[EvidenceDB]:
        """Get all evidence for a control."""
        control = self.db.query(ControlDB).filter(ControlDB.control_id == control_id).first()
        if not control:
            return []

        query = self.db.query(EvidenceDB).filter(EvidenceDB.control_id == control.id)
        if status:
            query = query.filter(EvidenceDB.status == status)
        return query.order_by(EvidenceDB.collected_at.desc()).all()

    def get_all(
        self,
        evidence_type: Optional[str] = None,
        status: Optional[EvidenceStatusEnum] = None,
        limit: int = 100,
    ) -> List[EvidenceDB]:
        """Get all evidence with filters."""
        query = self.db.query(EvidenceDB)
        if evidence_type:
            query = query.filter(EvidenceDB.evidence_type == evidence_type)
        if status:
            query = query.filter(EvidenceDB.status == status)
        return query.order_by(EvidenceDB.collected_at.desc()).limit(limit).all()

    def update_status(self, evidence_id: str, status: EvidenceStatusEnum) -> Optional[EvidenceDB]:
        """Update evidence status."""
        evidence = self.get_by_id(evidence_id)
        if not evidence:
            return None
        evidence.status = status
        evidence.updated_at = datetime.now(timezone.utc)
        self.db.commit()
        self.db.refresh(evidence)
        return evidence

    def get_statistics(self) -> Dict[str, Any]:
        """Get evidence statistics."""
        total = self.db.query(func.count(EvidenceDB.id)).scalar()

        by_type = dict(
            self.db.query(EvidenceDB.evidence_type, func.count(EvidenceDB.id))
            .group_by(EvidenceDB.evidence_type)
            .all()
        )

        by_status = dict(
            self.db.query(EvidenceDB.status, func.count(EvidenceDB.id))
            .group_by(EvidenceDB.status)
            .all()
        )

        valid = by_status.get(EvidenceStatusEnum.VALID, 0)
        coverage = (valid / total * 100) if total > 0 else 0

        return {
            "total": total,
            "by_type": by_type,
            "by_status": {str(k.value) if k else "none": v for k, v in by_status.items()},
            "coverage_percent": round(coverage, 1),
        }

