<?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 Version20260420130000
 *
 * Creates recent_battles 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 Version20260420130000 extends AbstractMigration
{
    public function getDescription(): string
    {
        return 'Create recent_battles materialized view for profile recent games list.';
    }

    public function up(Schema $schema): void
    {
        $this->addSql('
            CREATE MATERIALIZED VIEW recent_battles AS
            SELECT
                pg.id                                                           AS game_id,
                pg.uuid::text                                                   AS uuid,
                u.id                                                            AS user_id,
                CASE WHEN pg.red_id = u.id THEN true ELSE false END             AS is_red,

                COALESCE(ru.username, ra.user_name, \'Guest\')                  AS red_name,
                COALESCE(bu.username, ba.user_name, \'Guest\')                  AS blue_name,

                ru.avatar_path                                                  AS red_avatar_path,
                bu.avatar_path                                                  AS blue_avatar_path,

                pg.red_points,
                pg.blue_points,
                pg.red_exploded_bomb,
                pg.blue_exploded_bomb,
                pg.resign,

                COALESCE(pg.red_bonus_points, 0.0)                             AS red_bonus_points,
                COALESCE(pg.blue_bonus_points, 0.0)                            AS blue_bonus_points,
                COALESCE(pg.red_bonus_stats,  \'[]\'::json)                     AS red_bonus_stats,
                COALESCE(pg.blue_bonus_stats, \'[]\'::json)                     AS blue_bonus_stats,

                CASE
                    WHEN pg.red_id  = u.id AND pg.resign = \'red\'  THEN \'loss\'
                    WHEN pg.blue_id = u.id AND pg.resign = \'blue\' THEN \'loss\'
                    WHEN pg.red_id  = u.id AND pg.resign = \'blue\' THEN \'win\'
                    WHEN pg.blue_id = u.id AND pg.resign = \'red\'  THEN \'win\'
                    WHEN pg.red_id  = u.id AND pg.red_points  IS NOT NULL AND pg.blue_points IS NOT NULL
                         AND pg.red_points  > pg.blue_points                   THEN \'win\'
                    WHEN pg.blue_id = u.id AND pg.blue_points IS NOT NULL AND pg.red_points  IS NOT NULL
                         AND pg.blue_points > pg.red_points                    THEN \'win\'
                    WHEN pg.red_id  = u.id AND pg.red_points  IS NOT NULL AND pg.blue_points IS NOT NULL
                         AND pg.red_points  < pg.blue_points                   THEN \'loss\'
                    WHEN pg.blue_id = u.id AND pg.blue_points IS NOT NULL AND pg.red_points  IS NOT NULL
                         AND pg.blue_points < pg.red_points                    THEN \'loss\'
                    ELSE \'draw\'
                END                                                             AS result,

                -- Whether the opponent in this game is an anonymous guest (no app_user account)
                CASE
                    WHEN pg.red_id  = u.id AND pg.blue_id IS NULL THEN true
                    WHEN pg.blue_id = u.id AND pg.red_id  IS NULL THEN true
                    ELSE false
                END                                                             AS opp_is_guest,

                pg.created,
                pg.updated
            FROM app_user u
            JOIN played_game pg
                ON  pg.red_id  = u.id
                 OR pg.blue_id = u.id
            LEFT JOIN app_user ru ON ru.id = pg.red_id
            LEFT JOIN app_user bu ON bu.id = pg.blue_id
            LEFT JOIN gamer     ra ON ra.id = pg.red_anon
            LEFT JOIN gamer     ba ON ba.id = pg.blue_anon
        ');

        $this->addSql('CREATE UNIQUE INDEX idx_recent_battles_pk       ON recent_battles (user_id, game_id)');
        $this->addSql('CREATE INDEX        idx_recent_battles_user_upd ON recent_battles (user_id, updated DESC)');

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

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

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