<?php

use App\Models\Playlist;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        // 1. Create new column discord_id
        Schema::table('users', function (Blueprint $table) {
            $table->unsignedBigInteger('discord_id')->nullable()->after('id');
        });

        // 2. Migrate Discord Users IDs
        DB::table('users')
            ->where('id', '>', 10000)
            ->update(['discord_id' => DB::raw('id')]);

        // 3. Temporary new auto increment column
        Schema::table('users', function (Blueprint $table) {
            $table->unsignedBigInteger('new_id')->first();
        });

        // 3.5 Count
        DB::statement('
            UPDATE users u
            JOIN (
                SELECT id, ROW_NUMBER() OVER (ORDER BY id) AS rn
                FROM users
            ) t ON u.id = t.id
            SET u.new_id = t.rn
        ');

        // 4. Drop foreign keys
        $this->dropForeignKeys();

        // 5. Fix ID's in other tables
        $this->updateUserIDsInOtherTables();

        // 6. Remove old ID
        Schema::table('users', function (Blueprint $table) {
            $table->bigInteger('id')->unsigned()->change();
            $table->dropPrimary('id');
            $table->dropColumn('id');
        });

        // 7. Rename new_id to id
        Schema::table('users', function (Blueprint $table) {
            $table->renameColumn('new_id', 'id');
        });

        // 8. Change new ID to auto increment and set as primary key
        Schema::table('users', function (Blueprint $table) {
            $table->unsignedBigInteger('id')->autoIncrement()->primary()->change();
        });

        // 9. Remove data that would conflict with constraints
        $this->deleteUnreferencedData();

        // 9. Recreate foreign key constraints
        $this->addForeignKeys();
    }

    /**
     * Drop Foreign Keys referencing the user id
     */
    private function dropForeignKeys(): void
    {
        Schema::table('markable_likes', function (Blueprint $table) {
            $table->dropForeign(['user_id']);
        });

        Schema::table('watched', function (Blueprint $table) {
            $table->dropForeign(['user_id']);
        });

        // Our Schema does include a foreign key, for whatever reason it doesn't exist in the first palce
        // Schema::table('user_downloads', function (Blueprint $table) {
        //     $table->dropForeign(['user_id']);
        // });
    }

    /**
     * Tables to fix the IDs:
     *  - comments ['commenter_id']
     *  - markable_likes ['user_id']
     *  - notifications ['notifiable_id']
     *  - playlists ['user_id']
     *  - user_downloads ['user_id']
     *  - watched ['user_id']
     */
    private function updateUserIDsInOtherTables(): void
    {
        DB::statement('
            UPDATE comments c
            JOIN users u ON c.commenter_id = u.id
            SET c.commenter_id = u.new_id
        ');

        DB::statement('
            UPDATE watched w
            JOIN users u ON w.user_id = u.id
            SET w.user_id = u.new_id
        ');

        DB::statement('
            UPDATE markable_likes ml
            JOIN users u ON ml.user_id = u.id
            SET ml.user_id = u.new_id
        ');

        DB::statement('
            UPDATE notifications n
            JOIN users u ON n.notifiable_id = u.id
            SET n.notifiable_id = u.new_id
        ');

        DB::statement('
            UPDATE playlists p
            JOIN users u ON p.user_id = u.id
            SET p.user_id = u.new_id
        ');

        DB::statement('
            UPDATE user_downloads ud
            JOIN users u ON ud.user_id = u.id
            SET ud.user_id = u.new_id
        ');
    }

    /**
     * Due to incorrect handling of user deletes,
     * we have unreferenced data
     */
    private function deleteUnreferencedData(): void
    {
        // User Downloads Table
        DB::table('user_downloads')
            ->where('user_id', '>', 1_000_000)
            ->delete();

        // User Playlists Table
        $playlists = Playlist::where('user_id', '>', 1_000_000)
            ->get();

        foreach ($playlists as $playlist) {
            DB::table('playlist_episodes')
                ->where('playlist_id', '=', $playlist->id)
                ->delete();

            $playlist->delete();
        }
    }

    /**
     * Re-Add Foreign Keys to tables which we dropped previously
     */
    private function addForeignKeys(): void
    {
        Schema::table('markable_likes', function (Blueprint $table) {
            // Ensure the column is unsigned
            $table->bigInteger('user_id')->unsigned()->change();

            // Add the foreign key constraint
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
        });

        Schema::table('watched', function (Blueprint $table) {
            // Ensure the column is unsigned
            $table->bigInteger('user_id')->unsigned()->change();

            // Add the foreign key constraint
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
        });

        Schema::table('user_downloads', function (Blueprint $table) {
            // Ensure the column is unsigned
            $table->bigInteger('user_id')->unsigned()->change();

            // Add the foreign key constraint
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
        });

        Schema::table('playlist_episodes', function (Blueprint $table) {
            // Ensure the column is unsigned
            $table->bigInteger('playlist_id')->unsigned()->change();

            // Add the foreign key constraint
            $table->foreign('playlist_id')->references('id')->on('playlists')->onDelete('cascade');
        });

        Schema::table('playlists', function (Blueprint $table) {
            // Ensure the column is unsigned
            $table->bigInteger('user_id')->unsigned()->change();

            // Add the foreign key constraint
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
        });
    }
};
