"""
Homework & Material Repositories.

CRUD-Operationen fuer Hausaufgaben (Feature f20) und Phasen-Materialien (Feature f19).
"""
from datetime import datetime
from typing import Optional, List

from sqlalchemy.orm import Session as DBSession

from .db_models import (
    HomeworkDB, HomeworkStatusEnum, PhaseMaterialDB, MaterialTypeEnum,
)
from .models import (
    Homework, HomeworkStatus, PhaseMaterial, MaterialType,
)


class HomeworkRepository:
    """Repository fuer Hausaufgaben-Tracking (Feature f20)."""

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

    # ==================== CREATE ====================

    def create(self, homework: Homework) -> HomeworkDB:
        """Erstellt eine neue Hausaufgabe."""
        db_homework = HomeworkDB(
            id=homework.homework_id,
            teacher_id=homework.teacher_id,
            class_id=homework.class_id,
            subject=homework.subject,
            title=homework.title,
            description=homework.description,
            session_id=homework.session_id,
            due_date=homework.due_date,
            status=HomeworkStatusEnum(homework.status.value),
        )
        self.db.add(db_homework)
        self.db.commit()
        self.db.refresh(db_homework)
        return db_homework

    # ==================== READ ====================

    def get_by_id(self, homework_id: str) -> Optional[HomeworkDB]:
        """Holt eine Hausaufgabe nach ID."""
        return self.db.query(HomeworkDB).filter(
            HomeworkDB.id == homework_id
        ).first()

    def get_by_teacher(
        self,
        teacher_id: str,
        status: Optional[str] = None,
        limit: int = 50
    ) -> List[HomeworkDB]:
        """Holt alle Hausaufgaben eines Lehrers."""
        query = self.db.query(HomeworkDB).filter(
            HomeworkDB.teacher_id == teacher_id
        )
        if status:
            query = query.filter(HomeworkDB.status == HomeworkStatusEnum(status))
        return query.order_by(
            HomeworkDB.due_date.asc().nullslast(),
            HomeworkDB.created_at.desc()
        ).limit(limit).all()

    def get_by_class(
        self,
        class_id: str,
        teacher_id: str,
        include_completed: bool = False,
        limit: int = 20
    ) -> List[HomeworkDB]:
        """Holt alle Hausaufgaben einer Klasse."""
        query = self.db.query(HomeworkDB).filter(
            HomeworkDB.class_id == class_id,
            HomeworkDB.teacher_id == teacher_id
        )
        if not include_completed:
            query = query.filter(HomeworkDB.status != HomeworkStatusEnum.COMPLETED)
        return query.order_by(
            HomeworkDB.due_date.asc().nullslast(),
            HomeworkDB.created_at.desc()
        ).limit(limit).all()

    def get_by_session(self, session_id: str) -> List[HomeworkDB]:
        """Holt alle Hausaufgaben einer Session."""
        return self.db.query(HomeworkDB).filter(
            HomeworkDB.session_id == session_id
        ).order_by(HomeworkDB.created_at.desc()).all()

    def get_pending(
        self,
        teacher_id: str,
        days_ahead: int = 7
    ) -> List[HomeworkDB]:
        """Holt anstehende Hausaufgaben der naechsten X Tage."""
        from datetime import timedelta
        cutoff = datetime.utcnow() + timedelta(days=days_ahead)
        return self.db.query(HomeworkDB).filter(
            HomeworkDB.teacher_id == teacher_id,
            HomeworkDB.status.in_([HomeworkStatusEnum.ASSIGNED, HomeworkStatusEnum.IN_PROGRESS]),
            HomeworkDB.due_date <= cutoff
        ).order_by(HomeworkDB.due_date.asc()).all()

    # ==================== UPDATE ====================

    def update_status(
        self,
        homework_id: str,
        status: HomeworkStatus
    ) -> Optional[HomeworkDB]:
        """Aktualisiert den Status einer Hausaufgabe."""
        db_homework = self.get_by_id(homework_id)
        if not db_homework:
            return None

        db_homework.status = HomeworkStatusEnum(status.value)
        self.db.commit()
        self.db.refresh(db_homework)
        return db_homework

    def update(self, homework: Homework) -> Optional[HomeworkDB]:
        """Aktualisiert eine Hausaufgabe."""
        db_homework = self.get_by_id(homework.homework_id)
        if not db_homework:
            return None

        db_homework.title = homework.title
        db_homework.description = homework.description
        db_homework.due_date = homework.due_date
        db_homework.status = HomeworkStatusEnum(homework.status.value)

        self.db.commit()
        self.db.refresh(db_homework)
        return db_homework

    # ==================== DELETE ====================

    def delete(self, homework_id: str) -> bool:
        """Loescht eine Hausaufgabe."""
        db_homework = self.get_by_id(homework_id)
        if not db_homework:
            return False

        self.db.delete(db_homework)
        self.db.commit()
        return True

    # ==================== CONVERSION ====================

    def to_dataclass(self, db_homework: HomeworkDB) -> Homework:
        """Konvertiert DB-Model zu Dataclass."""
        return Homework(
            homework_id=db_homework.id,
            teacher_id=db_homework.teacher_id,
            class_id=db_homework.class_id,
            subject=db_homework.subject,
            title=db_homework.title,
            description=db_homework.description or "",
            session_id=db_homework.session_id,
            due_date=db_homework.due_date,
            status=HomeworkStatus(db_homework.status.value),
            created_at=db_homework.created_at,
            updated_at=db_homework.updated_at,
        )


class MaterialRepository:
    """Repository fuer Phasen-Materialien (Feature f19)."""

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

    # ==================== CREATE ====================

    def create(self, material: PhaseMaterial) -> PhaseMaterialDB:
        """Erstellt ein neues Material."""
        db_material = PhaseMaterialDB(
            id=material.material_id,
            teacher_id=material.teacher_id,
            title=material.title,
            material_type=MaterialTypeEnum(material.material_type.value),
            url=material.url,
            description=material.description,
            phase=material.phase,
            subject=material.subject,
            grade_level=material.grade_level,
            tags=material.tags,
            is_public=material.is_public,
            usage_count=material.usage_count,
            session_id=material.session_id,
        )
        self.db.add(db_material)
        self.db.commit()
        self.db.refresh(db_material)
        return db_material

    # ==================== READ ====================

    def get_by_id(self, material_id: str) -> Optional[PhaseMaterialDB]:
        """Holt ein Material nach ID."""
        return self.db.query(PhaseMaterialDB).filter(
            PhaseMaterialDB.id == material_id
        ).first()

    def get_by_teacher(
        self,
        teacher_id: str,
        phase: Optional[str] = None,
        subject: Optional[str] = None,
        limit: int = 50
    ) -> List[PhaseMaterialDB]:
        """Holt alle Materialien eines Lehrers."""
        query = self.db.query(PhaseMaterialDB).filter(
            PhaseMaterialDB.teacher_id == teacher_id
        )
        if phase:
            query = query.filter(PhaseMaterialDB.phase == phase)
        if subject:
            query = query.filter(PhaseMaterialDB.subject == subject)

        return query.order_by(
            PhaseMaterialDB.usage_count.desc(),
            PhaseMaterialDB.created_at.desc()
        ).limit(limit).all()

    def get_by_phase(
        self,
        phase: str,
        teacher_id: str,
        include_public: bool = True
    ) -> List[PhaseMaterialDB]:
        """Holt alle Materialien fuer eine bestimmte Phase."""
        if include_public:
            return self.db.query(PhaseMaterialDB).filter(
                PhaseMaterialDB.phase == phase,
                (PhaseMaterialDB.teacher_id == teacher_id) |
                (PhaseMaterialDB.is_public == True)
            ).order_by(
                PhaseMaterialDB.usage_count.desc()
            ).all()
        else:
            return self.db.query(PhaseMaterialDB).filter(
                PhaseMaterialDB.phase == phase,
                PhaseMaterialDB.teacher_id == teacher_id
            ).order_by(
                PhaseMaterialDB.created_at.desc()
            ).all()

    def get_by_session(self, session_id: str) -> List[PhaseMaterialDB]:
        """Holt alle Materialien einer Session."""
        return self.db.query(PhaseMaterialDB).filter(
            PhaseMaterialDB.session_id == session_id
        ).order_by(PhaseMaterialDB.phase, PhaseMaterialDB.created_at).all()

    def get_public_materials(
        self,
        phase: Optional[str] = None,
        subject: Optional[str] = None,
        limit: int = 20
    ) -> List[PhaseMaterialDB]:
        """Holt oeffentliche Materialien."""
        query = self.db.query(PhaseMaterialDB).filter(
            PhaseMaterialDB.is_public == True
        )
        if phase:
            query = query.filter(PhaseMaterialDB.phase == phase)
        if subject:
            query = query.filter(PhaseMaterialDB.subject == subject)

        return query.order_by(
            PhaseMaterialDB.usage_count.desc()
        ).limit(limit).all()

    def search_by_tags(
        self,
        tags: List[str],
        teacher_id: Optional[str] = None
    ) -> List[PhaseMaterialDB]:
        """Sucht Materialien nach Tags."""
        query = self.db.query(PhaseMaterialDB)
        if teacher_id:
            query = query.filter(
                (PhaseMaterialDB.teacher_id == teacher_id) |
                (PhaseMaterialDB.is_public == True)
            )
        else:
            query = query.filter(PhaseMaterialDB.is_public == True)

        # Filter by tags - vereinfachte Implementierung
        results = []
        for material in query.all():
            if material.tags and any(tag in material.tags for tag in tags):
                results.append(material)
        return results[:50]

    # ==================== UPDATE ====================

    def update(self, material: PhaseMaterial) -> Optional[PhaseMaterialDB]:
        """Aktualisiert ein Material."""
        db_material = self.get_by_id(material.material_id)
        if not db_material:
            return None

        db_material.title = material.title
        db_material.material_type = MaterialTypeEnum(material.material_type.value)
        db_material.url = material.url
        db_material.description = material.description
        db_material.phase = material.phase
        db_material.subject = material.subject
        db_material.grade_level = material.grade_level
        db_material.tags = material.tags
        db_material.is_public = material.is_public

        self.db.commit()
        self.db.refresh(db_material)
        return db_material

    def increment_usage(self, material_id: str) -> Optional[PhaseMaterialDB]:
        """Erhoeht den Usage-Counter eines Materials."""
        db_material = self.get_by_id(material_id)
        if not db_material:
            return None

        db_material.usage_count += 1
        self.db.commit()
        self.db.refresh(db_material)
        return db_material

    def attach_to_session(
        self,
        material_id: str,
        session_id: str
    ) -> Optional[PhaseMaterialDB]:
        """Verknuepft ein Material mit einer Session."""
        db_material = self.get_by_id(material_id)
        if not db_material:
            return None

        db_material.session_id = session_id
        db_material.usage_count += 1
        self.db.commit()
        self.db.refresh(db_material)
        return db_material

    # ==================== DELETE ====================

    def delete(self, material_id: str) -> bool:
        """Loescht ein Material."""
        db_material = self.get_by_id(material_id)
        if not db_material:
            return False

        self.db.delete(db_material)
        self.db.commit()
        return True

    # ==================== CONVERSION ====================

    def to_dataclass(self, db_material: PhaseMaterialDB) -> PhaseMaterial:
        """Konvertiert DB-Model zu Dataclass."""
        return PhaseMaterial(
            material_id=db_material.id,
            teacher_id=db_material.teacher_id,
            title=db_material.title,
            material_type=MaterialType(db_material.material_type.value),
            url=db_material.url,
            description=db_material.description or "",
            phase=db_material.phase,
            subject=db_material.subject or "",
            grade_level=db_material.grade_level or "",
            tags=db_material.tags or [],
            is_public=db_material.is_public,
            usage_count=db_material.usage_count,
            session_id=db_material.session_id,
            created_at=db_material.created_at,
            updated_at=db_material.updated_at,
        )
