<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Models\Tag;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

final class SeedTagInteractionDemoCommand extends Command
{
    protected $signature = 'analytics:seed-tag-interaction-demo
        {--days=14 : Number of days to generate demo events for}
        {--per-day=90 : Approximate number of demo events to write per day}
        {--refresh : Remove existing seeded demo events first}
        {--force : Allow running outside local/testing environments}';

    protected $description = 'Generate demo tag interaction events for local analytics dashboards and ranking validation';

    public function handle(): int
    {
        if (! app()->environment(['local', 'testing']) && ! $this->option('force')) {
            $this->error('This command is restricted to local/testing unless --force is provided.');

            return self::FAILURE;
        }

        $days = max(1, min(60, (int) $this->option('days')));
        $perDay = max(10, min(500, (int) $this->option('per-day')));

        $tags = Tag::query()
            ->where('is_active', true)
            ->orderByDesc('usage_count')
            ->limit(20)
            ->get(['id', 'name', 'slug', 'usage_count']);

        if ($tags->count() < 2) {
            $this->error('At least two active tags are required to generate demo interaction data.');

            return self::FAILURE;
        }

        $transitions = $this->buildTransitionMap($tags);

        if ($this->option('refresh')) {
            DB::table('tag_interaction_events')
                ->where('meta->seeded_demo', true)
                ->delete();
        }

        $now = now();

        for ($offset = $days - 1; $offset >= 0; $offset--) {
            $date = Carbon::today()->subDays($offset);
            $rows = [];

            for ($index = 0; $index < $perDay; $index++) {
                $surface = $this->pickSurface();
                $sourceTag = $tags->random();
                $targetTag = $this->pickTargetTag($surface, $sourceTag->slug, $transitions, $tags);
                $query = in_array($surface, ['search_suggestion', 'rescue_suggestion', 'recent_search'], true)
                    ? $this->queryForTag($targetTag)
                    : null;

                $rows[] = [
                    'event_date' => $date->toDateString(),
                    'event_type' => 'click',
                    'surface' => $surface,
                    'user_id' => null,
                    'session_key' => hash('sha256', 'demo-' . $date->toDateString() . '-' . $index . '-' . $surface),
                    'tag_slug' => $targetTag->slug,
                    'source_tag_slug' => in_array($surface, ['related_chip', 'related_cluster', 'top_companion'], true)
                        ? $sourceTag->slug
                        : null,
                    'query' => $query,
                    'position' => random_int(1, 4),
                    'meta' => json_encode([
                        'seeded_demo' => true,
                        'seeded_at' => $now->toISOString(),
                    ], JSON_THROW_ON_ERROR),
                    'occurred_at' => $date->copy()->setTime(random_int(8, 23), random_int(0, 59), random_int(0, 59)),
                    'created_at' => $now,
                    'updated_at' => $now,
                ];
            }

            foreach (array_chunk($rows, 250) as $chunk) {
                DB::table('tag_interaction_events')->insert($chunk);
            }

            $this->call('analytics:aggregate-tag-interactions', ['--date' => $date->toDateString()]);
        }

        $this->info("Seeded demo tag interaction events for the last {$days} days.");

        return self::SUCCESS;
    }

    private function buildTransitionMap(Collection $tags): array
    {
        $pairs = DB::table('artwork_tag as source_pivot')
            ->join('tags as source_tag', 'source_tag.id', '=', 'source_pivot.tag_id')
            ->join('artwork_tag as target_pivot', 'target_pivot.artwork_id', '=', 'source_pivot.artwork_id')
            ->join('tags as target_tag', 'target_tag.id', '=', 'target_pivot.tag_id')
            ->whereIn('source_tag.id', $tags->pluck('id')->all())
            ->whereIn('target_tag.id', $tags->pluck('id')->all())
            ->whereColumn('source_tag.id', '!=', 'target_tag.id')
            ->groupBy('source_tag.slug', 'target_tag.slug')
            ->orderByRaw('COUNT(*) DESC')
            ->get([
                'source_tag.slug as source_slug',
                'target_tag.slug as target_slug',
                DB::raw('COUNT(*) as pair_count'),
            ]);

        $map = [];
        foreach ($pairs as $pair) {
            $map[$pair->source_slug][] = $pair->target_slug;
        }

        return $map;
    }

    private function pickSurface(): string
    {
        $roll = random_int(1, 100);

        return match (true) {
            $roll <= 32 => 'search_suggestion',
            $roll <= 46 => 'rescue_suggestion',
            $roll <= 58 => 'recent_search',
            $roll <= 80 => 'related_chip',
            $roll <= 94 => 'related_cluster',
            default => 'top_companion',
        };
    }

    private function pickTargetTag(string $surface, string $sourceSlug, array $transitions, Collection $tags): object
    {
        if (in_array($surface, ['related_chip', 'related_cluster', 'top_companion'], true)) {
            $candidateSlugs = $transitions[$sourceSlug] ?? [];
            if ($candidateSlugs !== []) {
                $slug = $candidateSlugs[array_rand($candidateSlugs)];
                return $tags->firstWhere('slug', $slug) ?? $tags->where('slug', '!=', $sourceSlug)->random();
            }

            return $tags->where('slug', '!=', $sourceSlug)->random();
        }

        return $tags->random();
    }

    private function queryForTag(object $tag): string
    {
        $name = trim((string) ($tag->name ?? $tag->slug));
        $options = array_values(array_filter([
            strtolower($name),
            strtolower((string) ($tag->slug ?? '')),
            strtolower(substr($name, 0, max(3, min(strlen($name), 7)))),
        ]));

        return $options[array_rand($options)];
    }
}
