<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        $addedTokenHashColumn = false;

        Schema::table('event_join_tokens', function (Blueprint $table) use (&$addedTokenHashColumn) {
            if (!Schema::hasColumn('event_join_tokens', 'token_hash')) {
                $table->string('token_hash', 128)->nullable()->after('token');
                $table->index('token_hash', 'event_join_tokens_token_hash_index');
                $addedTokenHashColumn = true;
            }

            if (!Schema::hasColumn('event_join_tokens', 'token_encrypted')) {
                $table->text('token_encrypted')->nullable()->after('token_hash');
            }

            if (!Schema::hasColumn('event_join_tokens', 'token_preview')) {
                $table->string('token_preview', 32)->nullable()->after('token_encrypted');
            }
        });

        DB::table('event_join_tokens')
            ->whereNull('token_hash')
            ->whereNotNull('token')
            ->orderBy('id')
            ->chunkById(200, function ($tokens) {
            foreach ($tokens as $token) {
                $hash = hash('sha256', $token->token);
                $preview = $this->previewToken($token->token);

                DB::table('event_join_tokens')
                    ->where('id', $token->id)
                    ->update([
                        'token_hash' => $hash,
                        'token_encrypted' => Crypt::encryptString($token->token),
                        'token_preview' => $preview,
                        'token' => $hash,
                    ]);
            }
        });

        if ($addedTokenHashColumn) {
            Schema::table('event_join_tokens', function (Blueprint $table) {
                $table->unique('token_hash', 'event_join_tokens_token_hash_unique');
            });
        }
    }

    public function down(): void
    {
        Schema::table('event_join_tokens', function (Blueprint $table) {
            if (Schema::hasColumn('event_join_tokens', 'token_hash')) {
                try {
                    $table->dropUnique('event_join_tokens_token_hash_unique');
                } catch (\Throwable $e) {
                    // Unique index might already be absent (e.g. partial rollback).
                }

                try {
                    $table->dropIndex('event_join_tokens_token_hash_index');
                } catch (\Throwable $e) {
                    // Index might already be absent.
                }
            }

            $columns = collect(['token_hash', 'token_encrypted', 'token_preview'])
                ->filter(fn ($column) => Schema::hasColumn('event_join_tokens', $column))
                ->all();

            if (!empty($columns)) {
                $table->dropColumn($columns);
            }
        });
    }

    private function previewToken(string $token): string
    {
        $length = strlen($token);

        if ($length <= 10) {
            return $token;
        }

        return substr($token, 0, 6).'…'.substr($token, -4);
    }
};
