<?php

namespace Database\Seeders;

use App\Models\Call;
use App\Models\CallTranslation;
use App\Models\Locations\Location;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

/**
 * Temporary seeder for search testing only. NOT for production.
 * Assigns random tags and calls to users that don't have them.
 *
 * Run:   php artisan db:seed --class=TestSearchDataSeeder
 * Undo:  php artisan db:seed --class=TestSearchDataSeeder --rollback
 *        (or pass --rollback flag)
 */
class TestSearchDataSeeder extends Seeder
{
    // How many users to seed (pick randomly from those without tags/calls)
    private const USERS_TO_SEED = 200;
    private const TAGS_PER_USER = 3;
    private const CALLS_PER_USER = 2;

    public function run(bool $rollback = false): void
    {
        if ($rollback || in_array('--rollback', $_SERVER['argv'] ?? [])) {
            $this->rollback();
            return;
        }

        $this->seedTags();
        $this->seedCalls();

        $this->command->info('Re-indexing affected users in Elasticsearch (synchronously)...');
        $this->reindexAffectedUsers();
        $this->command->info('Done. Run with --rollback to remove test data.');
    }

    private function seedTags(): void
    {
        $users = User::doesntHave('tags')
            ->inRandomOrder()
            ->limit(self::USERS_TO_SEED)
            ->get();

        // Only use tag_ids that have a context with a non-null category_id
        $tagIds = DB::table('taggable_contexts')
            ->whereNotNull('category_id')
            ->inRandomOrder()->limit(500)->pluck('id')->toArray();

        if (empty($tagIds)) {
            $this->command->warn('No tags found, skipping tag seeding.');
            return;
        }

        $rows = [];
        $now = now()->toDateTimeString();

        foreach ($users as $user) {
            $selected = collect($tagIds)->shuffle()->take(self::TAGS_PER_USER);
            foreach ($selected as $tagId) {
                $rows[] = [
                    'tag_id'         => $tagId,
                    'taggable_id'    => $user->id,
                    'taggable_type'  => 'App\Models\User',
                    'created_at'     => $now,
                    'updated_at'     => $now,
                ];
            }
        }

        // Insert in chunks, ignore duplicates
        foreach (array_chunk($rows, 500) as $chunk) {
            DB::table('taggable_taggables')->insertOrIgnore($chunk);
        }

        $this->command->info('Seeded tags for ' . $users->count() . ' users.');
    }

    private function seedCalls(): void
    {
        $users = User::with(['locations.country', 'locations.division', 'locations.city', 'locations.district'])
            ->inRandomOrder()
            ->limit(self::USERS_TO_SEED)
            ->get();

        // Only use tag_ids that exist in taggable_tags AND have a context with a non-null category_id
        $validTagIds = DB::table('taggable_tags')
            ->whereIn('tag_id', DB::table('taggable_contexts')->whereNotNull('category_id')->pluck('id'))
            ->inRandomOrder()->limit(100)->pluck('tag_id')->toArray();

        if (empty($validTagIds)) {
            $this->command->warn('No valid tags found, skipping call seeding.');
            return;
        }

        // Fetch tag names for generating content
        $tagNames = DB::table('taggable_tags')
            ->whereIn('tag_id', $validTagIds)
            ->pluck('name', 'tag_id');

        $count = 0;
        $now = now();

        foreach ($users as $user) {
            $selected = collect($validTagIds)->shuffle()->take(self::CALLS_PER_USER);

            // Resolve location from user's primary location
            $locationId = $this->resolveUserLocation($user);

            foreach ($selected as $tagId) {
                // Random till date between 1 and 3 months from now
                $tillMonths = rand(1, 3);
                $till = $now->copy()->addMonths($tillMonths);

                $call = Call::create([
                    'callable_id'   => $user->id,
                    'callable_type' => 'App\Models\User',
                    'tag_id'        => $tagId,
                    'location_id'   => $locationId,
                    'from'          => $now->utc(),
                    'till'          => $till,
                    'is_public'     => true,
                    'is_suppressed' => false,
                    'is_paused'     => false,
                ]);

                $tagName = $tagNames[$tagId] ?? 'this skill';
                CallTranslation::create([
                    'call_id' => $call->id,
                    'locale'  => 'en',
                    'content' => "Looking to connect with others around {$tagName}. Available for the coming months.",
                ]);

                $count++;
            }
        }

        $this->command->info("Seeded {$count} calls for " . $users->count() . ' users.');
    }

    /**
     * Resolve or create a standalone Location record from the user's primary location.
     */
    private function resolveUserLocation(User $user): ?int
    {
        $userLocation = $user->locations->first();
        if (!$userLocation) {
            return null;
        }

        $attributes = array_filter([
            'country_id'  => $userLocation->country_id  ?: null,
            'division_id' => $userLocation->division_id ?: null,
            'city_id'     => $userLocation->city_id     ?: null,
            'district_id' => $userLocation->district_id ?: null,
        ]);

        if (empty($attributes)) {
            return null;
        }

        // Find existing standalone Location (no locatable_id/type) or create one
        $location = Location::whereNull('locatable_id')
            ->whereNull('locatable_type')
            ->where($attributes)
            ->first();

        if (!$location) {
            $location = new Location($attributes);
            $location->save();
        }

        return $location->id;
    }

    private function reindexAffectedUsers(): void
    {
        // Disable queue so indexing happens synchronously with full relationship loading
        config(['scout.queue' => false]);

        $cutoff = now()->subMinutes(10)->toDateTimeString();
        $userIds = DB::table('taggable_taggables')
            ->where('taggable_type', 'App\Models\User')
            ->where('created_at', '>=', $cutoff)
            ->pluck('taggable_id')
            ->merge(
                DB::table('calls')->where('created_at', '>=', $cutoff)->pluck('callable_id')
            )
            ->unique();

        $bar = $this->command->getOutput()->createProgressBar($userIds->count());
        User::whereIn('id', $userIds)->each(function (User $user) use ($bar) {
            $user->searchable();
            $bar->advance();
        });
        $bar->finish();
        $this->command->newLine();

        // Also re-index the calls themselves
        $this->command->info('Re-indexing calls in Elasticsearch...');
        config(['scout.queue' => false]);
        $callIds = DB::table('calls')->where('created_at', '>=', $cutoff)->pluck('id');
        $callBar = $this->command->getOutput()->createProgressBar($callIds->count());
        Call::whereIn('id', $callIds)->each(function (Call $call) use ($callBar) {
            $call->searchable();
            $callBar->advance();
        });
        $callBar->finish();
        $this->command->newLine();
    }

    private function rollback(): void
    {
        // Remove only the test-seeded data (created recently)
        $cutoff = now()->subHour()->toDateTimeString();

        // Delete call_translations first (FK constraint), then calls
        $callIds = DB::table('calls')
            ->where('created_at', '>=', $cutoff)
            ->pluck('id');

        $translationsDeleted = DB::table('call_translations')
            ->whereIn('call_id', $callIds)
            ->delete();
        $this->command->info("Removed {$translationsDeleted} test call translations.");

        $deleted = DB::table('calls')
            ->where('created_at', '>=', $cutoff)
            ->delete();
        $this->command->info("Removed {$deleted} test calls.");

        $deleted = DB::table('taggable_taggables')
            ->where('taggable_type', 'App\Models\User')
            ->where('created_at', '>=', $cutoff)
            ->delete();
        $this->command->info("Removed {$deleted} test tag assignments.");

        $this->command->info('Re-indexing affected users in Elasticsearch (synchronously)...');
        $this->reindexAffectedUsers();
        $this->command->info('Rollback complete.');
    }
}
