"""
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 ServiceModuleRepository:
    """Repository for service modules (Sprint 3)."""

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

    def create(
        self,
        name: str,
        display_name: str,
        service_type: str,
        description: Optional[str] = None,
        port: Optional[int] = None,
        technology_stack: Optional[List[str]] = None,
        repository_path: Optional[str] = None,
        docker_image: Optional[str] = None,
        data_categories: Optional[List[str]] = None,
        processes_pii: bool = False,
        processes_health_data: bool = False,
        ai_components: bool = False,
        criticality: str = "medium",
        owner_team: Optional[str] = None,
        owner_contact: Optional[str] = None,
    ) -> "ServiceModuleDB":
        """Create a service module."""
        from .models import ServiceModuleDB, ServiceTypeEnum

        module = ServiceModuleDB(
            id=str(uuid.uuid4()),
            name=name,
            display_name=display_name,
            description=description,
            service_type=ServiceTypeEnum(service_type),
            port=port,
            technology_stack=technology_stack or [],
            repository_path=repository_path,
            docker_image=docker_image,
            data_categories=data_categories or [],
            processes_pii=processes_pii,
            processes_health_data=processes_health_data,
            ai_components=ai_components,
            criticality=criticality,
            owner_team=owner_team,
            owner_contact=owner_contact,
        )
        self.db.add(module)
        self.db.commit()
        self.db.refresh(module)
        return module

    def get_by_id(self, module_id: str) -> Optional["ServiceModuleDB"]:
        """Get module by ID."""
        from .models import ServiceModuleDB
        return self.db.query(ServiceModuleDB).filter(ServiceModuleDB.id == module_id).first()

    def get_by_name(self, name: str) -> Optional["ServiceModuleDB"]:
        """Get module by name."""
        from .models import ServiceModuleDB
        return self.db.query(ServiceModuleDB).filter(ServiceModuleDB.name == name).first()

    def get_all(
        self,
        service_type: Optional[str] = None,
        criticality: Optional[str] = None,
        processes_pii: Optional[bool] = None,
        ai_components: Optional[bool] = None,
    ) -> List["ServiceModuleDB"]:
        """Get all modules with filters."""
        from .models import ServiceModuleDB, ServiceTypeEnum

        query = self.db.query(ServiceModuleDB).filter(ServiceModuleDB.is_active)

        if service_type:
            query = query.filter(ServiceModuleDB.service_type == ServiceTypeEnum(service_type))
        if criticality:
            query = query.filter(ServiceModuleDB.criticality == criticality)
        if processes_pii is not None:
            query = query.filter(ServiceModuleDB.processes_pii == processes_pii)
        if ai_components is not None:
            query = query.filter(ServiceModuleDB.ai_components == ai_components)

        return query.order_by(ServiceModuleDB.name).all()

    def get_with_regulations(self, module_id: str) -> Optional["ServiceModuleDB"]:
        """Get module with regulation mappings loaded."""
        from .models import ServiceModuleDB, ModuleRegulationMappingDB
        from sqlalchemy.orm import selectinload

        return (
            self.db.query(ServiceModuleDB)
            .options(
                selectinload(ServiceModuleDB.regulation_mappings)
                    .selectinload(ModuleRegulationMappingDB.regulation)
            )
            .filter(ServiceModuleDB.id == module_id)
            .first()
        )

    def add_regulation_mapping(
        self,
        module_id: str,
        regulation_id: str,
        relevance_level: str = "medium",
        notes: Optional[str] = None,
        applicable_articles: Optional[List[str]] = None,
    ) -> "ModuleRegulationMappingDB":
        """Add a regulation mapping to a module."""
        from .models import ModuleRegulationMappingDB, RelevanceLevelEnum

        mapping = ModuleRegulationMappingDB(
            id=str(uuid.uuid4()),
            module_id=module_id,
            regulation_id=regulation_id,
            relevance_level=RelevanceLevelEnum(relevance_level),
            notes=notes,
            applicable_articles=applicable_articles,
        )
        self.db.add(mapping)
        self.db.commit()
        self.db.refresh(mapping)
        return mapping

    def get_overview(self) -> Dict[str, Any]:
        """Get overview statistics for all modules."""
        from .models import ModuleRegulationMappingDB

        modules = self.get_all()
        total = len(modules)

        by_type = {}
        by_criticality = {}
        pii_count = 0
        ai_count = 0

        for m in modules:
            type_key = m.service_type.value if m.service_type else "unknown"
            by_type[type_key] = by_type.get(type_key, 0) + 1
            by_criticality[m.criticality] = by_criticality.get(m.criticality, 0) + 1
            if m.processes_pii:
                pii_count += 1
            if m.ai_components:
                ai_count += 1

        # Get regulation coverage
        regulation_coverage = {}
        mappings = self.db.query(ModuleRegulationMappingDB).all()
        for mapping in mappings:
            reg = mapping.regulation
            if reg:
                code = reg.code
                regulation_coverage[code] = regulation_coverage.get(code, 0) + 1

        # Calculate average compliance score
        scores = [m.compliance_score for m in modules if m.compliance_score is not None]
        avg_score = sum(scores) / len(scores) if scores else None

        return {
            "total_modules": total,
            "modules_by_type": by_type,
            "modules_by_criticality": by_criticality,
            "modules_processing_pii": pii_count,
            "modules_with_ai": ai_count,
            "average_compliance_score": round(avg_score, 1) if avg_score else None,
            "regulations_coverage": regulation_coverage,
        }

    def seed_from_data(self, services_data: List[Dict[str, Any]], force: bool = False) -> Dict[str, int]:
        """Seed modules from service_modules.py data."""

        modules_created = 0
        mappings_created = 0

        for svc in services_data:
            # Check if module exists
            existing = self.get_by_name(svc["name"])
            if existing and not force:
                continue

            if existing and force:
                # Delete existing module (cascades to mappings)
                self.db.delete(existing)
                self.db.commit()

            # Create module
            module = self.create(
                name=svc["name"],
                display_name=svc["display_name"],
                description=svc.get("description"),
                service_type=svc["service_type"],
                port=svc.get("port"),
                technology_stack=svc.get("technology_stack"),
                repository_path=svc.get("repository_path"),
                docker_image=svc.get("docker_image"),
                data_categories=svc.get("data_categories"),
                processes_pii=svc.get("processes_pii", False),
                processes_health_data=svc.get("processes_health_data", False),
                ai_components=svc.get("ai_components", False),
                criticality=svc.get("criticality", "medium"),
                owner_team=svc.get("owner_team"),
            )
            modules_created += 1

            # Create regulation mappings
            for reg_data in svc.get("regulations", []):
                # Find regulation by code
                reg = self.db.query(RegulationDB).filter(
                    RegulationDB.code == reg_data["code"]
                ).first()

                if reg:
                    self.add_regulation_mapping(
                        module_id=module.id,
                        regulation_id=reg.id,
                        relevance_level=reg_data.get("relevance", "medium"),
                        notes=reg_data.get("notes"),
                    )
                    mappings_created += 1

        return {
            "modules_created": modules_created,
            "mappings_created": mappings_created,
        }

