"""Сервіс оцінювань — створення, відповіді, запуск scoring engine."""
from __future__ import annotations

from datetime import date, timedelta

from sqlmodel import Session, delete as sql_delete, select

from db.session import get_engine
from engine.scoring import ScoringResult, evaluate_assessment
from models import (
    AssessmentResults,
    Assessments,
    CertificationLevels,
    Companies,
    Nonconformities,
    Requirements,
    TechnologyClasses,
)


def get_companies() -> list[Companies]:
    with Session(get_engine()) as s:
        return s.exec(select(Companies).order_by(Companies.name)).all()


def create_assessment(company_id: int, assessment_date: date, expert: str) -> int:
    record = Assessments(
        company_id=company_id,
        assessment_date=assessment_date,
        expert=expert,
        status="draft",
    )
    with Session(get_engine()) as s:
        s.add(record)
        s.commit()
        s.refresh(record)
        return record.id


def save_answers(assessment_id: int, answers: dict[int, str]) -> int:
    """Ідемпотентне збереження: видаляє попередні відповіді перед записом."""
    rows = [
        AssessmentResults(
            assessment_id=assessment_id,
            requirement_id=req_id,
            value=value,
            meets=None,             # NULL до запуску scoring engine
            index_contribution=None,
        )
        for req_id, value in answers.items()
    ]
    with Session(get_engine()) as s:
        s.exec(sql_delete(AssessmentResults).where(
            AssessmentResults.assessment_id == assessment_id
        ))
        for row in rows:
            s.add(row)
        s.commit()
    return len(rows)


def run_assessment(assessment_id: int) -> ScoringResult:
    """Запускає scoring engine і записує результати в БД."""
    with Session(get_engine()) as s:
        reqs = s.exec(select(Requirements)).all()
        results = s.exec(
            select(AssessmentResults).where(AssessmentResults.assessment_id == assessment_id)
        ).all()
        results_by_req: dict[int, str] = {r.requirement_id: r.value for r in results}

        levels = s.exec(select(CertificationLevels)).all()
        classes = s.exec(select(TechnologyClasses)).all()
        class_id_by_code = {tc.code: tc.id for tc in classes}

        # Engine — чиста функція, БД не торкається
        scoring = evaluate_assessment(reqs, results_by_req, levels, class_id_by_code)

        # Записуємо meets та index_contribution назад у AssessmentResults
        for row in results:
            row.meets = scoring.req_met.get(row.requirement_id, False)
            row.index_contribution = 1.0 if row.meets else 0.0
            s.add(row)

        # Замінюємо невідповідності для цього оцінювання (§С.5)
        s.exec(
            sql_delete(Nonconformities).where(Nonconformities.assessment_id == assessment_id)
        )
        req_by_id = {r.id: r for r in reqs}
        today = date.today()
        for req_id in scoring.nonconformity_req_ids:
            req = req_by_id.get(req_id)
            if req:
                s.add(Nonconformities(
                    assessment_id=assessment_id,
                    requirement_id=req_id,
                    severity=req.criticality,
                    deadline=today + timedelta(days=req.deadline_days),
                    recommendation=req.recommendation,
                ))

        assessment = s.get(Assessments, assessment_id)
        if assessment:
            assessment.certification_level = scoring.certification_level
            assessment.status = "scored"
            s.add(assessment)

        s.commit()

    return scoring


def get_requirements_by_class() -> list[tuple[TechnologyClasses, list[Requirements]]]:
    with Session(get_engine()) as s:
        classes = s.exec(select(TechnologyClasses).order_by(TechnologyClasses.code)).all()
        result = []
        for tc in classes:
            reqs = s.exec(
                select(Requirements)
                .where(Requirements.class_id == tc.id)
                .order_by(Requirements.code)
            ).all()
            result.append((tc, reqs))
        return result
