<?php

declare(strict_types=1);

namespace Tests\Unit;

use PDO;
use PHPUnit\Framework\TestCase;

final class ScoringTest extends TestCase
{
    private PDO $pdo;

    protected function setUp(): void
    {
        require_once dirname(__DIR__, 2) . '/common.php';
        require_once dirname(__DIR__, 2) . '/lib/scoring.php';

        $this->pdo = new PDO('sqlite::memory:');
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $this->pdo->exec('PRAGMA foreign_keys = ON');
        $schema = file_get_contents(dirname(__DIR__, 2) . '/schema.sql');
        $this->pdo->exec($schema);
    }

    public function testBandForTotal(): void
    {
        $legacy = ['greenMax' => 12, 'yellowMax' => 36];
        $this->assertSame('green', qdb_band_for_total(0, $legacy));
        $this->assertSame('green', qdb_band_for_total(12, $legacy));
        $this->assertSame('yellow', qdb_band_for_total(13, $legacy));
        $this->assertSame('yellow', qdb_band_for_total(36, $legacy));
        $this->assertSame('red', qdb_band_for_total(37, $legacy));

        $custom = [
            'greenMin' => 5,
            'greenMax' => 15,
            'yellowMin' => 16,
            'yellowMax' => 30,
            'redMin' => 31,
        ];
        $this->assertSame('green', qdb_band_for_total(5, $custom));
        $this->assertSame('green', qdb_band_for_total(15, $custom));
        $this->assertSame('yellow', qdb_band_for_total(16, $custom));
        $this->assertSame('red', qdb_band_for_total(31, $custom));
    }

    public function testValidateScoringBands(): void
    {
        $this->assertNull(qdb_validate_scoring_bands(qdb_normalize_scoring_bands(['greenMax' => 12, 'yellowMax' => 36])));
        $this->assertNotNull(qdb_validate_scoring_bands([
            'greenMin' => 0, 'greenMax' => 20,
            'yellowMin' => 15, 'yellowMax' => 36,
            'redMin' => 37,
        ]));
    }

    public function testGlassScoreRulesFromSymptoms(): void
    {
        $rules = qdb_score_rules_from_glass_symptoms([
            ['key' => 'pain', 'scoreLevels' => ['never_glass' => 0, 'extreme_glass' => 5]],
        ]);
        $this->assertCount(5, $rules);
        $painExtreme = array_values(array_filter(
            $rules,
            fn ($r) => $r['scopeKey'] === 'pain' && $r['levelKey'] === 'extreme_glass'
        ));
        $this->assertSame(5, $painExtreme[0]['points'] ?? null);
    }

    private function seedCoachChain(): void
    {
        $this->pdo->exec("INSERT INTO supervisor (supervisorID, username) VALUES ('sup1', 'sup')");
        $this->pdo->exec("INSERT INTO coach (coachID, supervisorID, username) VALUES ('coach1', 'sup1', 'coach')");
    }

    public function testRadioAndGlassScoring(): void
    {
        $this->seedCoachChain();
        $qnID = 'qn_test';
        $this->pdo->exec("INSERT INTO questionnaire (questionnaireID, name, version, state, orderIndex, showPoints, conditionJson, categoryKey)
            VALUES ('$qnID', 'Test', '1', 'active', 0, 0, '{}', '')");

        $radioQ = "{$qnID}__radio";
        $this->pdo->exec("INSERT INTO question (questionID, questionnaireID, defaultText, type, orderIndex, isRequired, configJson)
            VALUES ('$radioQ', '$qnID', 'Pick one', 'radio_question', 0, 1, '{}')");
        $this->pdo->exec("INSERT INTO answer_option (answerOptionID, questionID, defaultText, points, orderIndex, nextQuestionId)
            VALUES ('ao1', '$radioQ', 'yes', 3, 0, '')");

        $glassQ = "{$qnID}__glass";
        $this->pdo->exec("INSERT INTO question (questionID, questionnaireID, defaultText, type, orderIndex, isRequired, configJson)
            VALUES ('$glassQ', '$qnID', 'Symptoms', 'glass_scale_question', 1, 1, '{\"symptoms\":[\"pain\"]}')");
        qdb_sync_question_score_rules($this->pdo, $glassQ, [
            ['scopeKey' => 'pain', 'levelKey' => 'moderate_glass', 'points' => 2],
        ]);

        $client = 'CLIENT-001';
        $this->pdo->exec("INSERT INTO client (clientCode, coachID) VALUES ('$client', 'coach1')");
        $this->pdo->exec("INSERT INTO client_answer (clientCode, questionID, answerOptionID, freeTextValue, numericValue)
            VALUES ('$client', '$radioQ', 'ao1', NULL, NULL)");
        $this->pdo->exec("INSERT INTO client_answer (clientCode, questionID, answerOptionID, freeTextValue, numericValue)
            VALUES ('$client', '$glassQ', NULL, '{\"pain\":\"moderate_glass\"}', NULL)");

        $score = qdb_compute_questionnaire_score($this->pdo, $client, $qnID);
        $this->assertSame(5, $score);
    }

    public function testIncompleteProfileProducesNoResult(): void
    {
        $this->seedCoachChain();
        $qnA = 'qn_a';
        $qnB = 'qn_b';
        foreach ([$qnA, $qnB] as $id) {
            $this->pdo->exec("INSERT INTO questionnaire (questionnaireID, name, version, state, orderIndex, showPoints, conditionJson, categoryKey)
                VALUES ('$id', '$id', '1', 'active', 0, 0, '{}', '')");
        }
        $profileID = 'profile1';
        $this->pdo->exec("INSERT INTO scoring_profile (profileID, name, description, isActive,
            greenMin, greenMax, yellowMin, yellowMax, redMin, createdAt, updatedAt)
            VALUES ('$profileID', 'Combo', '', 1, 0, 10, 11, 20, 21, 0, 0)");
        $this->pdo->exec("INSERT INTO scoring_profile_questionnaire (profileID, questionnaireID, weight, orderIndex)
            VALUES ('$profileID', '$qnA', 1.0, 0), ('$profileID', '$qnB', 1.0, 1)");

        $client = 'CLIENT-002';
        $this->pdo->exec("INSERT INTO client (clientCode, coachID) VALUES ('$client', 'coach1')");
        $this->pdo->exec("INSERT INTO completed_questionnaire (clientCode, questionnaireID, status, sumPoints, completedAt)
            VALUES ('$client', '$qnA', 'completed', 5, 1)");

        qdb_recompute_profile_scores_for_client($this->pdo, $client);

        $stmt = $this->pdo->prepare('SELECT COUNT(*) FROM client_scoring_profile_result WHERE clientCode = :cc');
        $stmt->execute([':cc' => $client]);
        $this->assertSame(0, (int)$stmt->fetchColumn());
    }

    public function testCoachScoringReviewPreservesComputedBand(): void
    {
        $this->seedCoachChain();
        $qnID = 'qn_review';
        $this->pdo->exec("INSERT INTO questionnaire (questionnaireID, name, version, state, orderIndex, showPoints, conditionJson, categoryKey)
            VALUES ('$qnID', 'Review', '1', 'active', 0, 0, '{}', '')");
        $profileID = 'profile_review';
        $this->pdo->exec("INSERT INTO scoring_profile (profileID, name, description, isActive,
            greenMin, greenMax, yellowMin, yellowMax, redMin, createdAt, updatedAt)
            VALUES ('$profileID', 'Review profile', '', 1, 0, 10, 11, 20, 21, 0, 0)");
        $this->pdo->exec("INSERT INTO scoring_profile_questionnaire (profileID, questionnaireID, weight, orderIndex)
            VALUES ('$profileID', '$qnID', 1.0, 0)");

        $client = 'CLIENT-REVIEW';
        $this->pdo->exec("INSERT INTO client (clientCode, coachID) VALUES ('$client', 'coach1')");
        $this->pdo->exec("INSERT INTO completed_questionnaire (clientCode, questionnaireID, status, sumPoints, completedAt)
            VALUES ('$client', '$qnID', 'completed', 15, 1)");

        qdb_recompute_profile_scores_for_client($this->pdo, $client);

        $row = $this->pdo->query(
            "SELECT band, coachBand FROM client_scoring_profile_result WHERE clientCode = '$client'"
        )->fetch(PDO::FETCH_ASSOC);
        $this->assertSame('yellow', $row['band']);
        $this->assertSame('', $row['coachBand']);

        qdb_set_coach_scoring_band($this->pdo, $client, $profileID, 'green', 'user1');

        $row = $this->pdo->query(
            "SELECT band, coachBand FROM client_scoring_profile_result WHERE clientCode = '$client'"
        )->fetch(PDO::FETCH_ASSOC);
        $this->assertSame('yellow', $row['band']);
        $this->assertSame('green', $row['coachBand']);

        qdb_recompute_profile_scores_for_client($this->pdo, $client);
        $row = $this->pdo->query(
            "SELECT band, coachBand FROM client_scoring_profile_result WHERE clientCode = '$client'"
        )->fetch(PDO::FETCH_ASSOC);
        $this->assertSame('yellow', $row['band']);
        $this->assertSame('green', $row['coachBand']);
    }

    public function testSaveCoachReviewFromAppBeforeServerRecompute(): void
    {
        $this->seedCoachChain();
        $qnID = 'qn_pre';
        $this->pdo->exec("INSERT INTO questionnaire (questionnaireID, name, version, state, orderIndex, showPoints, conditionJson, categoryKey)
            VALUES ('$qnID', 'Pre', '1', 'active', 0, 0, '{}', '')");
        $profileID = 'profile_pre';
        $this->pdo->exec("INSERT INTO scoring_profile (profileID, name, description, isActive,
            greenMin, greenMax, yellowMin, yellowMax, redMin, createdAt, updatedAt)
            VALUES ('$profileID', 'Pre profile', '', 1, 0, 10, 11, 20, 21, 0, 0)");
        $this->pdo->exec("INSERT INTO scoring_profile_questionnaire (profileID, questionnaireID, weight, orderIndex)
            VALUES ('$profileID', '$qnID', 1.0, 0)");

        $client = 'CLIENT-PRE';
        $this->pdo->exec("INSERT INTO client (clientCode, coachID) VALUES ('$client', 'coach1')");

        qdb_save_coach_scoring_review(
            $this->pdo,
            $client,
            $profileID,
            'yellow',
            'user1',
            15.0,
            'yellow'
        );

        $row = $this->pdo->query(
            "SELECT band, coachBand, weightedTotal FROM client_scoring_profile_result WHERE clientCode = '$client'"
        )->fetch(PDO::FETCH_ASSOC);
        $this->assertSame('yellow', $row['band']);
        $this->assertSame('yellow', $row['coachBand']);
        $this->assertSame(15.0, (float)$row['weightedTotal']);
    }
}
