from datetime import datetime
from typing import Any

from sqlalchemy import desc
from sqlalchemy.orm import Session
from common_logging import get_logger, log_execution

logger = get_logger(__name__)

class ExpertProfileManager:

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

    @log_execution(logger)
    def create_profile(self, user_id: int, expertise_areas: list[str], skills: list[str], bio: str | None=None, metadata: dict | None=None) -> dict[str, Any]:
        from app.models import ExpertProfile
        profile = ExpertProfile(user_id=user_id, expertise_areas=expertise_areas, skills=skills, bio=bio, total_annotations=0, total_reviews=0, avg_quality_score=0.0, reputation_score=0, meta_data=metadata or {})
        self.db.add(profile)
        self.db.commit()
        self.db.refresh(profile)
        logger.info(f'Created expert profile for user {user_id}')
        return self._profile_to_dict(profile)

    def update_profile(self, user_id: int, expertise_areas: list[str] | None=None, skills: list[str] | None=None, bio: str | None=None) -> dict[str, Any]:
        from app.models import ExpertProfile
        profile = self.db.query(ExpertProfile).filter(ExpertProfile.user_id == user_id).first()
        if not profile:
            raise ValueError(f'Profile for user {user_id} not found')
        if expertise_areas is not None:
            profile.expertise_areas = expertise_areas
        if skills is not None:
            profile.skills = skills
        if bio is not None:
            profile.bio = bio
        profile.updated_at = datetime.utcnow()
        self.db.commit()
        self.db.refresh(profile)
        logger.info(f'Updated expert profile for user {user_id}')
        return self._profile_to_dict(profile)

    @log_execution(logger)
    def record_annotation(self, user_id: int, task_id: int, quality_score: float | None=None) -> dict[str, Any]:
        from app.models import ExpertContribution, ExpertProfile
        profile = self.db.query(ExpertProfile).filter(ExpertProfile.user_id == user_id).first()
        if not profile:
            raise ValueError(f'Profile for user {user_id} not found')
        contribution = ExpertContribution(user_id=user_id, contribution_type='annotation', task_id=task_id, quality_score=quality_score)
        self.db.add(contribution)
        profile.total_annotations += 1
        if quality_score:
            self._update_avg_quality(profile, quality_score)
            self._update_reputation(profile)
        self.db.commit()
        self.db.refresh(profile)
        logger.info(f'Recorded annotation for user {user_id}, task {task_id}')
        return self._profile_to_dict(profile)

    def record_review(self, user_id: int, task_id: int) -> dict[str, Any]:
        from app.models import ExpertContribution, ExpertProfile
        profile = self.db.query(ExpertProfile).filter(ExpertProfile.user_id == user_id).first()
        if not profile:
            raise ValueError(f'Profile for user {user_id} not found')
        contribution = ExpertContribution(user_id=user_id, contribution_type='review', task_id=task_id)
        self.db.add(contribution)
        profile.total_reviews += 1
        self._update_reputation(profile)
        self.db.commit()
        self.db.refresh(profile)
        logger.info(f'Recorded review for user {user_id}, task {task_id}')
        return self._profile_to_dict(profile)

    def get_profile(self, user_id: int) -> dict[str, Any]:
        from app.models import ExpertProfile
        profile = self.db.query(ExpertProfile).filter(ExpertProfile.user_id == user_id).first()
        if not profile:
            raise ValueError(f'Profile for user {user_id} not found')
        return self._profile_to_dict(profile)

    def get_leaderboard(self, metric: str='reputation_score', limit: int=10) -> list[dict[str, Any]]:
        from app.models import ExpertProfile
        order_column = getattr(ExpertProfile, metric, ExpertProfile.reputation_score)
        profiles = self.db.query(ExpertProfile).order_by(desc(order_column)).limit(limit).all()
        return [self._profile_to_dict(p) for p in profiles]

    def find_experts_by_expertise(self, expertise_area: str, min_quality_score: float=0.0) -> list[dict[str, Any]]:
        from app.models import ExpertProfile
        profiles = self.db.query(ExpertProfile).filter(ExpertProfile.expertise_areas.contains([expertise_area]), ExpertProfile.avg_quality_score >= min_quality_score).order_by(desc(ExpertProfile.reputation_score)).all()
        return [self._profile_to_dict(p) for p in profiles]

    def get_contributions(self, user_id: int, contribution_type: str | None=None) -> list[dict[str, Any]]:
        from app.models import ExpertContribution
        query = self.db.query(ExpertContribution).filter(ExpertContribution.user_id == user_id)
        if contribution_type:
            query = query.filter(ExpertContribution.contribution_type == contribution_type)
        contributions = query.order_by(desc(ExpertContribution.created_at)).all()
        return [self._contribution_to_dict(c) for c in contributions]

    def _update_avg_quality(self, profile, new_score: float):
        total = profile.total_annotations
        current_avg = profile.avg_quality_score or 0.0
        profile.avg_quality_score = (current_avg * (total - 1) + new_score) / total

    def _update_reputation(self, profile):
        profile.reputation_score = profile.total_annotations * 10 + profile.total_reviews * 5 + (profile.avg_quality_score or 0) * 100

    def _profile_to_dict(self, profile) -> dict[str, Any]:
        return {'id': profile.id, 'user_id': profile.user_id, 'expertise_areas': profile.expertise_areas, 'skills': profile.skills, 'bio': profile.bio, 'total_annotations': profile.total_annotations, 'total_reviews': profile.total_reviews, 'avg_quality_score': profile.avg_quality_score, 'reputation_score': profile.reputation_score, 'created_at': profile.created_at.isoformat() if profile.created_at else None, 'updated_at': profile.updated_at.isoformat() if profile.updated_at else None, 'meta_data': profile.meta_data}

    def _contribution_to_dict(self, contribution) -> dict[str, Any]:
        return {'id': contribution.id, 'user_id': contribution.user_id, 'contribution_type': contribution.contribution_type, 'task_id': contribution.task_id, 'quality_score': contribution.quality_score, 'created_at': contribution.created_at.isoformat() if contribution.created_at else None}

def get_expert_profile_manager(db: Session) -> ExpertProfileManager:
    return ExpertProfileManager(db)
