"""
app/player/routes.py
---------------------
The player-facing side of the app. Kept deliberately simple: a handful of
screens designed to work well on a phone. No admin capability lives here.

Per our plan: matches use a "both sides submit, system cross-checks"
model. Reporting is handled by report_match() below.
"""

from datetime import date, datetime

from flask import Blueprint, render_template, redirect, url_for, flash, request
from flask_login import login_required, current_user

from app.extensions import db
from app.models import Competition, Entry, Match, MatchResultSubmission, Dispute

player_bp = Blueprint("player", __name__, template_folder="../templates/player")


@player_bp.route("/")
@login_required
def dashboard():
    """
    The player's home screen: competitions currently open for signup, and
    any of their own upcoming/unreported matches, front and center.
    """
    open_for_signup = Competition.query.filter_by(stage="registration").all()

    # Every Entry belonging to the current user, across all competitions
    my_entries = Entry.query.filter(
        (Entry.player_id == current_user.id) | (Entry.partner_id == current_user.id)
    ).all()
    my_entry_ids = [e.id for e in my_entries]

    my_matches = Match.query.filter(
        (Match.entry1_id.in_(my_entry_ids)) | (Match.entry2_id.in_(my_entry_ids))
    ).filter(Match.status != "confirmed").all() if my_entry_ids else []

    return render_template(
        "player/dashboard.html",
        open_for_signup=open_for_signup,
        my_matches=my_matches,
    )


@player_bp.route("/competitions")
@login_required
def all_competitions():
    """
    Per our plan: ALL members can view any live or completed competition,
    even ones they didn't enter — this is the public "view of the
    competition" you described.
    """
    competitions = Competition.query.filter(Competition.stage.in_(["live", "complete"])).all()
    return render_template("player/all_competitions.html", competitions=competitions)


@player_bp.route("/competitions/<int:competition_id>")
@login_required
def view_competition(competition_id):
    """
    Public, read-only view of a competition's draw. Per our plan: ANY
    member can view this, whether or not they're entered themselves.
    """
    from app.models import Draw

    competition = Competition.query.get_or_404(competition_id)

    # A competition can technically have more than one Draw if it was
    # regenerated, so we always show the most recently generated one.
    latest_draw = (
        Draw.query.filter_by(competition_id=competition.id)
        .order_by(Draw.generated_date.desc())
        .first()
    )
    matches = latest_draw.matches if latest_draw else []

    return render_template("player/view_competition.html", competition=competition, matches=matches)


@player_bp.route("/competitions/<int:competition_id>/signup", methods=["GET", "POST"])
@login_required
def signup(competition_id):
    """
    The auto-generated signup form for a competition in its registration
    window. For doubles competitions, a player can optionally name a
    partner right away (partner_id filled in) or leave it blank and be
    paired later by the admin during the draw.
    """
    competition = Competition.query.get_or_404(competition_id)

    if competition.stage != "registration" or date.today() > competition.registration_closes:
        flash("Registration for this competition is not currently open.", "error")
        return redirect(url_for("player.dashboard"))

    already_entered = Entry.query.filter_by(
        competition_id=competition.id, player_id=current_user.id
    ).first()
    if already_entered:
        flash("You're already entered in this competition.", "info")
        return redirect(url_for("player.dashboard"))

    if request.method == "POST":
        partner_id = request.form.get("partner_id") or None
        entry = Entry(
            competition_id=competition.id,
            player_id=current_user.id,
            partner_id=partner_id,
        )
        db.session.add(entry)
        db.session.commit()
        flash("You're entered! You'll be notified once the draw is made.", "success")
        return redirect(url_for("player.dashboard"))

    # For doubles competitions, offer a dropdown of other members to pick as a partner
    partner_options = []
    if competition.template.format_type == "doubles":
        from app.models import User
        partner_options = User.query.filter(User.id != current_user.id).all()

    return render_template("player/signup.html", competition=competition, partner_options=partner_options)


@player_bp.route("/matches/<int:match_id>/report", methods=["GET", "POST"])
@login_required
def report_match(match_id):
    """
    Either player in a match submits what they believe the result was.
    Once BOTH sides have submitted, we compare them:
      - agree    -> Match is auto-confirmed
      - disagree -> Match is marked "disputed" and a Dispute is raised for
                    an admin to sort out
    """
    match = Match.query.get_or_404(match_id)

    # Work out which Entry the current user belongs to in this match
    my_entry = None
    for entry in (match.entry1, match.entry2):
        if entry and (entry.player_id == current_user.id or entry.partner_id == current_user.id):
            my_entry = entry

    if my_entry is None:
        flash("You're not part of this match.", "error")
        return redirect(url_for("player.dashboard"))

    if request.method == "POST":
        claimed_winner_id = request.form.get("winner_entry_id")
        result_summary = request.form.get("result_summary", "").strip()

        submission = MatchResultSubmission(
            match_id=match.id,
            submitted_by_entry_id=my_entry.id,
            claimed_winner_entry_id=claimed_winner_id,
            result_summary=result_summary,
        )
        db.session.add(submission)
        match.status = "reported"
        db.session.commit()

        _check_for_agreement(match)

        flash("Result submitted. Once your opponent also reports, it'll be confirmed automatically.", "success")
        return redirect(url_for("player.dashboard"))

    return render_template("player/report_match.html", match=match, my_entry=my_entry)


def _check_for_agreement(match):
    """
    Internal helper (not a route): looks at all submissions for a match and
    decides whether both sides agree. Kept separate from report_match()
    itself so the "what happens once both sides have reported" logic is
    easy to find and modify on its own.
    """
    submissions = MatchResultSubmission.query.filter_by(match_id=match.id).all()

    # Group submissions by which Entry submitted them, keeping only the most
    # recent one per entry (in case someone corrects their own submission).
    latest_by_entry = {}
    for s in submissions:
        latest_by_entry[s.submitted_by_entry_id] = s

    entry_ids_in_match = {e for e in (match.entry1_id, match.entry2_id) if e}
    if set(latest_by_entry.keys()) < entry_ids_in_match:
        return  # still waiting on one side

    values = list(latest_by_entry.values())
    all_agree = all(
        v.claimed_winner_entry_id == values[0].claimed_winner_entry_id
        and v.result_summary == values[0].result_summary
        for v in values
    )

    if all_agree:
        match.status = "confirmed"
        match.winner_entry_id = values[0].claimed_winner_entry_id
        match.result_summary = values[0].result_summary
    else:
        match.status = "disputed"
        # Only raise a new Dispute if one isn't already open for this match
        existing = Dispute.query.filter_by(match_id=match.id, status="open").first()
        if not existing:
            dispute = Dispute(
                match_id=match.id,
                raised_by_entry_id=values[0].submitted_by_entry_id,
                reason="Automatic: player-submitted results did not match.",
            )
            db.session.add(dispute)

    db.session.commit()
