<?php declare(strict_types=1);
/**
 * This file is part of the SplendidBear Websites' projects.
 *
 * Copyright (c) 2026 @ www.splendidbear.org
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace App\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
 * Class Version20260420120000
 *
 * Creates user_stats materialized view for ProfileController optimization.
 *
 * @package  App\Migrations
 * @author   Lang <https://www.splendidbear.org>
 * @category Class
 * @license  https://www.gnu.org/licenses/lgpl-3.0.en.html GNU Lesser General Public License
 * @link     www.splendidbear.org
 * @since    2026. 04. 20.
 */
final class Version20260420120000 extends AbstractMigration
{
    public function getDescription(): string
    {
        return 'Create user_stats materialized view for profile statistics.';
    }

    public function up(Schema $schema): void
    {
        $this->addSql('
            CREATE MATERIALIZED VIEW user_stats AS
            SELECT
                u.id AS user_id,
                COUNT(CASE WHEN pg.red_id = u.id OR pg.blue_id = u.id THEN 1 END) AS total_games,
                COUNT(CASE WHEN 
                    pg.red_id = u.id AND (pg.red_points > pg.blue_points AND pg.resign IS NULL)
                    OR pg.blue_id = u.id AND (pg.blue_points > pg.red_points AND pg.resign IS NULL)
                    OR pg.red_id = u.id AND pg.resign = \'blue\'
                    OR pg.blue_id = u.id AND pg.resign = \'red\'
                THEN 1 END) AS wins,
                COUNT(CASE WHEN 
                    pg.red_id = u.id AND (pg.blue_points > pg.red_points AND pg.resign IS NULL)
                    OR pg.blue_id = u.id AND (pg.red_points > pg.blue_points AND pg.resign IS NULL)
                    OR pg.red_id = u.id AND pg.resign = \'red\'
                    OR pg.blue_id = u.id AND pg.resign = \'blue\'
                THEN 1 END) AS losses,
                COUNT(CASE WHEN 
                    (pg.red_id = u.id OR pg.blue_id = u.id)
                    AND pg.red_points IS NOT NULL
                    AND pg.blue_points IS NOT NULL
                    AND pg.resign IS NULL
                    AND pg.red_points = pg.blue_points
                THEN 1 END) AS draws,
                COALESCE(SUM(
                    CASE WHEN pg.red_id = u.id THEN pg.red_points ELSE pg.blue_points END
                ), 0) AS total_mines,
                COALESCE(SUM(
                    CASE WHEN pg.red_id = u.id THEN pg.red_bonus_points ELSE pg.blue_bonus_points END
                ), 0)::numeric(10,1) AS total_bonus_points,
                COALESCE(AVG(
                    CASE WHEN pg.red_id = u.id THEN pg.red_bonus_points ELSE pg.blue_bonus_points END
                ), 0)::numeric(10,1) AS avg_bonus,
                COALESCE(MAX(
                    CASE WHEN pg.red_id = u.id THEN (pg.red_bonus_stats->>\'chainBest\')::int ELSE (pg.blue_bonus_stats->>\'chainBest\')::int END
                ), 0) AS best_chain,
                COALESCE(SUM(
                    CASE WHEN pg.red_id = u.id THEN (pg.red_bonus_stats->>\'blindHits\')::int ELSE (pg.blue_bonus_stats->>\'blindHits\')::int END
                ), 0) AS blind_hits,
                COALESCE(SUM(
                    CASE WHEN pg.red_id = u.id THEN (pg.red_bonus_stats->>\'edgeMines\')::int ELSE (pg.blue_bonus_stats->>\'edgeMines\')::int END
                ), 0) AS edge_mines,
                COUNT(CASE WHEN 
                    pg.red_id = u.id AND pg.red_points IS NOT NULL
                    OR pg.blue_id = u.id AND pg.blue_points IS NOT NULL
                THEN 1 END) AS games_with_scores,
                MAX(pg.updated) AS last_game_at
            FROM app_user u
            LEFT JOIN played_game pg ON (pg.red_id = u.id OR pg.blue_id = u.id)
                AND (pg.red_points IS NOT NULL OR pg.blue_points IS NOT NULL OR pg.resign IS NOT NULL)
            GROUP BY u.id
        ');

        $this->addSql('CREATE UNIQUE INDEX idx_user_stats_user_id ON user_stats (user_id)');
        $this->addSql('CREATE INDEX idx_user_stats_last_game_at ON user_stats (last_game_at DESC)');

        $this->addSql('
            CREATE OR REPLACE FUNCTION refresh_user_stats()
            RETURNS TRIGGER AS $$
            BEGIN
                REFRESH MATERIALIZED VIEW CONCURRENTLY user_stats;
                RETURN NULL;
            END;
            $$ LANGUAGE plpgsql
        ');

        $this->addSql('
            CREATE TRIGGER trigger_refresh_user_stats
            AFTER INSERT OR UPDATE OR DELETE ON played_game
            FOR EACH STATEMENT
            EXECUTE FUNCTION refresh_user_stats()
        ');
    }

    public function down(Schema $schema): void
    {
        $this->addSql('DROP TRIGGER IF EXISTS trigger_refresh_user_stats ON played_game');
        $this->addSql('DROP FUNCTION IF EXISTS refresh_user_stats()');
        $this->addSql('DROP MATERIALIZED VIEW IF EXISTS user_stats');
    }
}
