<?php
declare(strict_types=1);

namespace App\Modules\PlayerAffairs\Services;

use App\Core\App;
use App\Core\EventBus;
use App\Core\Logger;
use App\Modules\PlayerAffairs\Models\PlayerFitnessTest;

final class FitnessService
{
    /**
     * Record a new fitness test for a player.
     */
    public static function recordTest(array $data): PlayerFitnessTest
    {
        // Calculate BMI if weight and height are provided
        if (!empty($data['weight_kg']) && !empty($data['height_cm'])) {
            $heightM = (float) $data['height_cm'] / 100;
            if ($heightM > 0) {
                $data['bmi'] = round((float) $data['weight_kg'] / ($heightM * $heightM), 2);
            }
        }

        // Build results JSON if provided as array
        if (isset($data['results']) && is_array($data['results'])) {
            $data['results_json'] = json_encode($data['results'], JSON_UNESCAPED_UNICODE);
            unset($data['results']);
        }

        // Determine fitness level from overall score
        if (!empty($data['overall_fitness_score']) && empty($data['fitness_level'])) {
            $data['fitness_level'] = PlayerFitnessTest::determineFitnessLevel((float) $data['overall_fitness_score']);
        }

        // Default cleared_for_activity based on fitness level
        if (!isset($data['cleared_for_activity']) && !empty($data['fitness_level'])) {
            $data['cleared_for_activity'] = in_array($data['fitness_level'], ['excellent', 'good', 'average'], true) ? 1 : 0;
        }

        $test = PlayerFitnessTest::create($data);

        // Dispatch event
        EventBus::dispatch('player.fitness_test_recorded', [
            'test_id'        => (int) $test->id,
            'player_id'      => (int) $test->player_id,
            'fitness_level'  => $test->fitness_level,
            'overall_score'  => $test->overall_fitness_score,
            'cleared'        => (bool) $test->cleared_for_activity,
        ]);

        Logger::info("Fitness test recorded for player #{$test->player_id}: level={$test->fitness_level}, score={$test->overall_fitness_score}");

        return $test;
    }

    /**
     * Get progression data for a player (for chart display).
     */
    public static function getProgressionData(int $playerId): array
    {
        $db = App::getInstance()->db();

        $tests = $db->select(
            "SELECT test_date, overall_fitness_score, fitness_level, bmi, weight_kg, resting_heart_rate, test_type
             FROM player_fitness_tests
             WHERE player_id = ?
             ORDER BY test_date ASC",
            [$playerId]
        );

        $progression = [
            'dates'       => [],
            'scores'      => [],
            'bmi'         => [],
            'weight'      => [],
            'heart_rate'  => [],
            'levels'      => [],
        ];

        foreach ($tests as $test) {
            $progression['dates'][]      = $test['test_date'];
            $progression['scores'][]     = $test['overall_fitness_score'] !== null ? (float) $test['overall_fitness_score'] : null;
            $progression['bmi'][]        = $test['bmi'] !== null ? (float) $test['bmi'] : null;
            $progression['weight'][]     = $test['weight_kg'] !== null ? (float) $test['weight_kg'] : null;
            $progression['heart_rate'][] = $test['resting_heart_rate'] !== null ? (int) $test['resting_heart_rate'] : null;
            $progression['levels'][]     = $test['fitness_level'];
        }

        return $progression;
    }

    /**
     * Check if a player is currently fit (has a valid, non-expired fitness test with clearance).
     */
    public static function isPlayerFit(int $playerId): bool
    {
        $db = App::getInstance()->db();

        $latest = $db->selectOne(
            "SELECT cleared_for_activity, next_test_date, test_date
             FROM player_fitness_tests
             WHERE player_id = ?
             ORDER BY test_date DESC
             LIMIT 1",
            [$playerId]
        );

        if (!$latest) {
            return false;
        }

        // Not cleared
        if ((int) ($latest['cleared_for_activity'] ?? 0) !== 1) {
            return false;
        }

        // Check if next test date has passed (test expired)
        if (!empty($latest['next_test_date']) && $latest['next_test_date'] < date('Y-m-d')) {
            return false;
        }

        return true;
    }

    /**
     * Get fitness summary for a player.
     */
    public static function getPlayerSummary(int $playerId): array
    {
        $db = App::getInstance()->db();

        $latest = PlayerFitnessTest::getLatest($playerId);
        $totalTests = $db->selectOne(
            "SELECT COUNT(*) as cnt FROM player_fitness_tests WHERE player_id = ?",
            [$playerId]
        );

        $isFit = self::isPlayerFit($playerId);

        return [
            'latest_test'   => $latest,
            'total_tests'   => (int) ($totalTests['cnt'] ?? 0),
            'is_fit'        => $isFit,
            'current_level' => $latest['fitness_level'] ?? null,
            'current_score' => $latest['overall_fitness_score'] ?? null,
            'next_test_due' => $latest['next_test_date'] ?? null,
        ];
    }

    /**
     * Get players with overdue fitness tests.
     */
    public static function getOverdueTests(): array
    {
        $db = App::getInstance()->db();

        return $db->select(
            "SELECT pft.*, p.full_name_ar AS player_name, p.registration_serial
             FROM player_fitness_tests pft
             INNER JOIN players p ON p.id = pft.player_id AND p.is_archived = 0
             WHERE pft.next_test_date < CURDATE()
               AND pft.id = (
                   SELECT MAX(pft2.id) FROM player_fitness_tests pft2 WHERE pft2.player_id = pft.player_id
               )
             ORDER BY pft.next_test_date ASC"
        );
    }
}
