"""
Dispute resolution service.
"""
from datetime import datetime
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.models import (
    Dispute, DisputeStatus, DisputeVote,
    Assignment, AssignmentStatus, Participant, Marathon, Challenge, Game,
)
from app.services.telegram_notifier import telegram_notifier


class DisputeService:
    """Service for dispute resolution logic"""

    async def resolve_dispute(self, db: AsyncSession, dispute_id: int) -> tuple[str, int, int]:
        """
        Resolve a dispute based on votes.

        Returns:
            Tuple of (result_status, votes_valid, votes_invalid)
        """
        # Get dispute with votes and assignment
        result = await db.execute(
            select(Dispute)
            .options(
                selectinload(Dispute.votes),
                selectinload(Dispute.assignment).selectinload(Assignment.participant),
            )
            .where(Dispute.id == dispute_id)
        )
        dispute = result.scalar_one_or_none()

        if not dispute:
            raise ValueError(f"Dispute {dispute_id} not found")

        if dispute.status != DisputeStatus.OPEN.value:
            raise ValueError(f"Dispute {dispute_id} is already resolved")

        # Count votes
        votes_valid = sum(1 for v in dispute.votes if v.vote is True)
        votes_invalid = sum(1 for v in dispute.votes if v.vote is False)

        # Determine result: tie goes to the accused (valid)
        if votes_invalid > votes_valid:
            # Proof is invalid - mark assignment as RETURNED
            result_status = DisputeStatus.RESOLVED_INVALID.value
            await self._handle_invalid_proof(db, dispute)
        else:
            # Proof is valid (or tie)
            result_status = DisputeStatus.RESOLVED_VALID.value

        # Update dispute
        dispute.status = result_status
        dispute.resolved_at = datetime.utcnow()

        await db.commit()

        # Send Telegram notification about dispute resolution
        await self._notify_dispute_resolved(db, dispute, result_status == DisputeStatus.RESOLVED_INVALID.value)

        return result_status, votes_valid, votes_invalid

    async def _notify_dispute_resolved(
        self,
        db: AsyncSession,
        dispute: Dispute,
        is_valid: bool
    ) -> None:
        """Send notification about dispute resolution to the assignment owner."""
        try:
            # Get assignment with challenge and marathon info
            result = await db.execute(
                select(Assignment)
                .options(
                    selectinload(Assignment.participant),
                    selectinload(Assignment.challenge).selectinload(Challenge.game)
                )
                .where(Assignment.id == dispute.assignment_id)
            )
            assignment = result.scalar_one_or_none()
            if not assignment:
                return

            participant = assignment.participant
            challenge = assignment.challenge
            game = challenge.game if challenge else None

            # Get marathon
            result = await db.execute(
                select(Marathon).where(Marathon.id == game.marathon_id if game else 0)
            )
            marathon = result.scalar_one_or_none()

            if marathon and participant:
                await telegram_notifier.notify_dispute_resolved(
                    db,
                    user_id=participant.user_id,
                    marathon_title=marathon.title,
                    challenge_title=challenge.title if challenge else "Unknown",
                    is_valid=is_valid
                )
        except Exception as e:
            print(f"[DisputeService] Failed to send notification: {e}")

    async def _handle_invalid_proof(self, db: AsyncSession, dispute: Dispute) -> None:
        """
        Handle the case when proof is determined to be invalid.

        - Mark assignment as RETURNED
        - Subtract points from participant
        - Reset streak if it was affected
        """
        assignment = dispute.assignment
        participant = assignment.participant

        # Subtract points that were earned
        points_to_subtract = assignment.points_earned
        participant.total_points = max(0, participant.total_points - points_to_subtract)

        # Reset assignment
        assignment.status = AssignmentStatus.RETURNED.value
        assignment.points_earned = 0
        # Keep proof data so it can be reviewed

        print(f"[DisputeService] Assignment {assignment.id} marked as RETURNED, "
              f"subtracted {points_to_subtract} points from participant {participant.id}")

    async def get_pending_disputes(self, db: AsyncSession, older_than_hours: int = 24) -> list[Dispute]:
        """Get all open disputes older than specified hours"""
        from datetime import timedelta

        cutoff_time = datetime.utcnow() - timedelta(hours=older_than_hours)

        result = await db.execute(
            select(Dispute)
            .where(
                Dispute.status == DisputeStatus.OPEN.value,
                Dispute.created_at < cutoff_time,
            )
        )
        return list(result.scalars().all())


# Global service instance
dispute_service = DisputeService()
