from __future__ import annotations

import random
from datetime import date

from sqlmodel import Session, select

from app.models import (
    AppSetting,
    Child,
    ChildSettings,
    CoinTransaction,
    CoinTransactionType,
    DailyKnowledgeAssignment,
    DailyKnowledgeAttempt,
    DailyKnowledgeQuestion,
)


def coin_setting(session: Session, key: str = "knowledge_quiz_coin_value", default: int = 1) -> int:
    row = session.get(AppSetting, key)
    if row is None:
        return default
    try:
        return max(0, int(row.value))
    except ValueError:
        return default


def _settings_for_child(session: Session, child: Child) -> ChildSettings:
    settings = session.exec(select(ChildSettings).where(ChildSettings.child_id == child.id)).first()
    if settings is None:
        level = 3 if child.name.lower().startswith("emilia") else 2
        settings = ChildSettings(
            child_id=child.id,
            language_quiz_language="fr" if child.name.lower().startswith("emilia") else "en",
            language_quiz_level=1 if child.name.lower().startswith("emilia") else 2,
            math_quiz_level=level,
            knowledge_quiz_level=level,
        )
        session.add(settings)
        session.commit()
        session.refresh(settings)
    return settings


def ensure_assignment(session: Session, child: Child, target_date: date) -> DailyKnowledgeAssignment:
    existing = session.exec(
        select(DailyKnowledgeAssignment).where(
            DailyKnowledgeAssignment.child_id == child.id,
            DailyKnowledgeAssignment.day == target_date,
        )
    ).first()
    if existing is not None:
        return existing

    settings = _settings_for_child(session, child)
    level = max(1, min(4, int(settings.knowledge_quiz_level or 2)))
    questions = session.exec(
        select(DailyKnowledgeQuestion).where(
            DailyKnowledgeQuestion.active == True,  # noqa: E712
            DailyKnowledgeQuestion.level == level,
            (DailyKnowledgeQuestion.child_id == None) | (DailyKnowledgeQuestion.child_id == child.id),  # noqa: E711
        ).order_by(DailyKnowledgeQuestion.id)
    ).all()
    if not questions:
        questions = session.exec(select(DailyKnowledgeQuestion).where(DailyKnowledgeQuestion.active == True).order_by(DailyKnowledgeQuestion.id)).all()  # noqa: E712
    if not questions:
        raise ValueError("Kein Allgemeinwissen-Fragenpool vorhanden")
    rng = random.Random(f"knowledge:{child.id}:{target_date.isoformat()}:{level}")
    question = questions[rng.randrange(len(questions))]
    assignment = DailyKnowledgeAssignment(
        child_id=child.id,
        day=target_date,
        question_id=question.id or 0,
        level_snapshot=question.level,
        category=question.category,
        question=question.question,
        correct_answer=question.correct_answer,
        wrong_option_1=question.wrong_option_1,
        wrong_option_2=question.wrong_option_2,
        explanation=question.explanation,
    )
    session.add(assignment)
    session.commit()
    session.refresh(assignment)
    return assignment


def _serialize(session: Session, child: Child, assignment: DailyKnowledgeAssignment, *, quiz: bool) -> dict:
    attempt = session.exec(
        select(DailyKnowledgeAttempt).where(
            DailyKnowledgeAttempt.child_id == child.id,
            DailyKnowledgeAttempt.day == assignment.day,
            DailyKnowledgeAttempt.question_id == assignment.question_id,
        )
    ).first()
    options = [assignment.correct_answer, assignment.wrong_option_1, assignment.wrong_option_2]
    rng = random.Random(f"knowledge-options:{child.id}:{assignment.day.isoformat()}:{assignment.question_id}")
    rng.shuffle(options)
    question = {
        "id": assignment.question_id,
        "level": assignment.level_snapshot,
        "category": assignment.category,
        "question": assignment.question,
        "options": options,
    }
    if attempt is not None or not quiz:
        question["correct_answer"] = assignment.correct_answer
        question["explanation"] = assignment.explanation
    return {
        "child_id": child.id,
        "child_name": child.name,
        "child_avatar": child.avatar,
        "date": assignment.day.isoformat(),
        "answered": attempt is not None,
        "coin_value": coin_setting(session),
        "attempt": None if attempt is None else {
            "selected_answer": attempt.selected_answer,
            "is_correct": attempt.is_correct,
            "answered_at": attempt.answered_at.isoformat(),
        },
        "question": question,
    }


def assignments_for_day(session: Session, target_date: date, *, quiz: bool = True) -> dict:
    children = session.exec(select(Child).order_by(Child.sort_order)).all()
    items = []
    for child in children:
        assignment = ensure_assignment(session, child, target_date)
        items.append(_serialize(session, child, assignment, quiz=quiz))
    return {"date": target_date.isoformat(), "items": items}


def answer_quiz(session: Session, child_id: int, target_date: date, question_id: int, selected_answer: str) -> dict:
    child = session.get(Child, child_id)
    if child is None:
        raise ValueError("Kind nicht gefunden")
    assignment = session.exec(
        select(DailyKnowledgeAssignment).where(
            DailyKnowledgeAssignment.child_id == child_id,
            DailyKnowledgeAssignment.day == target_date,
            DailyKnowledgeAssignment.question_id == question_id,
        )
    ).first()
    if assignment is None:
        raise ValueError("Allgemeinwissen-Frage ist diesem Kind heute nicht zugewiesen")
    existing = session.exec(
        select(DailyKnowledgeAttempt).where(
            DailyKnowledgeAttempt.child_id == child_id,
            DailyKnowledgeAttempt.day == target_date,
            DailyKnowledgeAttempt.question_id == question_id,
        )
    ).first()
    coin_value = coin_setting(session)
    if existing is not None:
        return {
            "correct": existing.is_correct,
            "correct_answer": assignment.correct_answer,
            "coin_awarded": False,
            "coin_value": coin_value,
            "idempotent": True,
            "attempt": {"selected_answer": existing.selected_answer, "is_correct": existing.is_correct, "answered_at": existing.answered_at.isoformat()},
        }
    is_correct = selected_answer == assignment.correct_answer
    attempt = DailyKnowledgeAttempt(child_id=child_id, day=target_date, question_id=question_id, selected_answer=selected_answer, is_correct=is_correct)
    session.add(attempt)
    coin_awarded = False
    if is_correct and coin_value > 0:
        tx_key = f"daily-knowledge-quiz:{child_id}:{target_date.isoformat()}:{question_id}"
        if session.exec(select(CoinTransaction).where(CoinTransaction.idempotency_key == tx_key)).first() is None:
            session.add(CoinTransaction(
                child_id=child_id,
                transaction_type=CoinTransactionType.earned,
                amount=coin_value,
                reserved_amount=0,
                comment="Allgemeinwissen des Tages richtig beantwortet",
                idempotency_key=tx_key,
            ))
            coin_awarded = True
    session.commit()
    session.refresh(attempt)
    return {
        "correct": is_correct,
        "correct_answer": assignment.correct_answer,
        "coin_awarded": coin_awarded,
        "coin_value": coin_value,
        "idempotent": False,
        "attempt": {"selected_answer": attempt.selected_answer, "is_correct": attempt.is_correct, "answered_at": attempt.answered_at.isoformat()},
    }
