<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Models\Conversation;
use App\Models\ConversationParticipant;
use App\Models\Message;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Throwable;

/**
 * Migrates legacy `chat` / `messages` tables into the modern conversation-based system.
 *
 * Strategy:
 *   1. Load all legacy rows from the `chat` table via the 'legacy' DB connection.
 *   2. Group by (sender_user_id, receiver_user_id) pair (canonical: min first).
 *   3. For each pair, find or create a `direct` conversation.
 *   4. Insert each message in chronological order.
 *   5. Set last_read_at based on the legacy read_date column (if present).
 *   6. Skip deleted / inactive rows.
 *   7. Convert smileys to emoji placeholders.
 *
 * Usage:
 *   php artisan skinbase:migrate-messages
 *   php artisan skinbase:migrate-messages --dry-run
 *   php artisan skinbase:migrate-messages --chunk=1000
 */
class MigrateMessagesCommand extends Command
{
    protected $signature = 'skinbase:migrate-messages
                            {--dry-run    : Preview only — no writes to DB}
                            {--chunk=500  : Rows to process per batch}';

    protected $description = 'Migrate legacy chat/messages into the modern conversation system';

    /** Columns we attempt to read; gracefully degrade if missing. */
    private array $skipped = [];

    public function handle(): int
    {
        $dryRun = (bool) $this->option('dry-run');
        $chunk  = max(1, (int) $this->option('chunk'));

        if ($dryRun) {
            $this->warn('[DRY-RUN] No data will be written.');
        }

        // ── Check legacy connection ───────────────────────────────────────────
        try {
            DB::connection('legacy')->getPdo();
        } catch (Throwable $e) {
            $this->error('Cannot connect to legacy database: ' . $e->getMessage());
            return self::FAILURE;
        }

        $legacySchema = DB::connection('legacy')->getSchemaBuilder();

        if (! $legacySchema->hasTable('chat')) {
            $this->error('Legacy table `chat` not found on the legacy connection.');
            return self::FAILURE;
        }

        $columns = $legacySchema->getColumnListing('chat');
        $this->info('Legacy chat columns: ' . implode(', ', $columns));

        // Map expected legacy columns (adapt if your legacy schema differs)
        $hasReadDate   = in_array('read_date', $columns, true);
        $hasSoftDelete = in_array('deleted', $columns, true);

        // ── Count total rows ──────────────────────────────────────────────────
        $query = DB::connection('legacy')->table('chat');

        if ($hasSoftDelete) {
            $query->where('deleted', 0);
        }

        $total = $query->count();
        $this->info("Total legacy rows to process: {$total}");

        if ($total === 0) {
            $this->info('Nothing to migrate.');
            return self::SUCCESS;
        }

        $bar      = $this->output->createProgressBar($total);
        $inserted = 0;
        $skipped  = 0;
        $offset   = 0;

        // ── Chunk processing ──────────────────────────────────────────────────
        while (true) {
            $rows = DB::connection('legacy')
                ->table('chat')
                ->when($hasSoftDelete, fn ($q) => $q->where('deleted', 0))
                ->orderBy('id')
                ->offset($offset)
                ->limit($chunk)
                ->get();

            if ($rows->isEmpty()) {
                break;
            }

            foreach ($rows as $row) {
                $senderId   = (int) ($row->sender_user_id  ?? $row->from_user_id ?? $row->user_id       ?? 0);
                $receiverId = (int) ($row->receiver_user_id ?? $row->to_user_id  ?? $row->recipient_id  ?? 0);
                $body       = trim((string) ($row->message ?? $row->body ?? $row->content ?? ''));
                $createdAt  = $row->created_at ?? $row->date ?? $row->timestamp ?? now();
                $readDate   = $hasReadDate ? $row->read_date : null;

                if ($senderId === 0 || $receiverId === 0 || $body === '') {
                    $skipped++;
                    $this->skipped[] = ['id' => $row->id ?? '?', 'reason' => 'missing sender/receiver/body'];
                    $bar->advance();
                    continue;
                }

                // Skip self-messages
                if ($senderId === $receiverId) {
                    $skipped++;
                    $this->skipped[] = ['id' => $row->id ?? '?', 'reason' => 'self-message'];
                    $bar->advance();
                    continue;
                }

                // Sanitize: strip HTML, convert smileys to emoji
                $body = $this->sanitize($body);

                if ($dryRun) {
                    $inserted++;
                    $bar->advance();
                    continue;
                }

                try {
                    DB::transaction(function () use ($senderId, $receiverId, $body, $createdAt, $readDate, &$inserted) {
                        // Find or create direct conversation
                        $conv = Conversation::findDirect($senderId, $receiverId);

                        if (! $conv) {
                            $conv = Conversation::create([
                                'type'            => 'direct',
                                'created_by'      => $senderId,
                                'last_message_at' => $createdAt,
                            ]);

                            ConversationParticipant::insert([
                                [
                                    'conversation_id' => $conv->id,
                                    'user_id'         => $senderId,
                                    'role'            => 'admin',
                                    'joined_at'       => $createdAt,
                                    'last_read_at'    => $readDate,
                                ],
                                [
                                    'conversation_id' => $conv->id,
                                    'user_id'         => $receiverId,
                                    'role'            => 'member',
                                    'joined_at'       => $createdAt,
                                    'last_read_at'    => $readDate,
                                ],
                            ]);
                        } else {
                            // Update last_read_at on existing participants when available
                            if ($readDate) {
                                ConversationParticipant::where('conversation_id', $conv->id)
                                    ->where('user_id', $receiverId)
                                    ->whereNull('last_read_at')
                                    ->update(['last_read_at' => $readDate]);
                            }
                        }

                        Message::create([
                            'conversation_id' => $conv->id,
                            'sender_id'       => $senderId,
                            'body'            => $body,
                            'created_at'      => $createdAt,
                            'updated_at'      => $createdAt,
                        ]);

                        // Keep last_message_at up to date
                        if ($conv->last_message_at < $createdAt) {
                            $conv->update(['last_message_at' => $createdAt]);
                        }

                        $inserted++;
                    });
                } catch (Throwable $e) {
                    $skipped++;
                    $this->skipped[] = ['id' => $row->id ?? '?', 'reason' => $e->getMessage()];
                    Log::warning('MigrateMessages: skipped row', [
                        'id'     => $row->id ?? '?',
                        'reason' => $e->getMessage(),
                    ]);
                }

                $bar->advance();
            }

            $offset += $chunk;
        }

        $bar->finish();
        $this->newLine();

        $this->info("Done. Inserted: {$inserted} | Skipped: {$skipped}");

        if ($skipped > 0 && $this->option('verbose')) {
            $this->table(['ID', 'Reason'], $this->skipped);
        }

        return self::SUCCESS;
    }

    /**
     * Strip HTML tags and convert common legacy smileys to emoji.
     */
    private function sanitize(string $body): string
    {
        // Strip raw HTML
        $body = strip_tags($body);

        // Decode HTML entities
        $body = html_entity_decode($body, ENT_QUOTES | ENT_HTML5, 'UTF-8');

        // Common smiley → emoji mapping
        $smileys = [
            ':)'  => '🙂', ':-)' => '🙂',
            ':('  => '🙁', ':-(' => '🙁',
            ':D'  => '😀', ':-D' => '😀',
            ':P'  => '😛', ':-P' => '😛',
            ';)'  => '😉', ';-)' => '😉',
            ':o'  => '😮', ':O'  => '😮',
            ':|'  => '😐', ':-|' => '😐',
            ':/'  => '😕', ':-/' => '😕',
            '<3'  => '❤️',
            'xD'  => '😂', 'XD'  => '😂',
        ];

        return str_replace(array_keys($smileys), array_values($smileys), $body);
    }
}
