<?php

declare(strict_types=1);

namespace Database\Seeders;

use App\Models\Invoice;
use App\Models\Plan;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class DemoDataSeeder extends Seeder
{
    /**
     * Marker email domain used for idempotency checks.
     */
    private const DEMO_DOMAIN = 'demo.ezscale.test';

    public function run(): void
    {
        // Idempotency: skip if demo data already exists
        if (User::query()->where('email', 'like', '%@'.self::DEMO_DOMAIN)->exists()) {
            $this->command->info('Demo data already exists. Skipping DemoDataSeeder.');

            return;
        }

        $this->command->info('Seeding demo data...');

        // Load real plans from PlanSeeder
        $plans = Plan::query()->where('status', 'active')->get();

        if ($plans->isEmpty()) {
            $this->command->error('No plans found. Run PlanSeeder first.');

            return;
        }

        // Grab admin user for audit logs / staff replies
        $adminUser = User::role('admin')->first();
        $adminId = $adminUser?->id ?? 1;

        // ─── 1. Create 1000 Customers ─────────────────────────────────
        $this->command->info('Creating customers...');
        $customers = $this->createCustomers();

        // ─── 2. Create ~1500 Subscriptions ────────────────────────────
        $this->command->info('Creating subscriptions...');
        $subscriptionMap = $this->createSubscriptions($customers, $plans);

        // ─── 3. Create ~1200 Services (70% VPS) ───────────────────────
        $this->command->info('Creating services...');
        $this->createServices($customers, $plans, $subscriptionMap);

        // ─── 4. Create ~2000 Invoices with Items ──────────────────────
        $this->command->info('Creating invoices...');
        $invoiceIds = $this->createInvoices($customers, $plans, $subscriptionMap);

        // ─── 5. Create ~1500 Payment Transactions ─────────────────────
        $this->command->info('Creating payment transactions...');
        $this->createPaymentTransactions($customers, $invoiceIds);

        // ─── 6. Create ~400 Orders ────────────────────────────────────
        $this->command->info('Creating orders...');
        $this->createOrders($customers, $plans);

        // ─── 7. Create ~500 Support Tickets with Replies ──────────────
        $this->command->info('Creating support tickets...');
        $this->createSupportTickets($customers, $adminId);

        // ─── 8. Create ~100 Coupons ───────────────────────────────────
        $this->command->info('Creating coupons...');
        $this->createCoupons();

        // ─── 9. Create ~300 Audit Logs ────────────────────────────────
        $this->command->info('Creating audit logs...');
        $this->createAuditLogs($customers, $adminId);

        $this->command->info('Demo data seeded successfully!');
    }

    /**
     * Create 1000 customers with profiles.
     *
     * @return \Illuminate\Support\Collection<int, User>
     */
    private function createCustomers(): \Illuminate\Support\Collection
    {
        $customers = collect();
        $statuses = array_merge(
            array_fill(0, 900, 'active'),
            array_fill(0, 50, 'suspended'),
            array_fill(0, 30, 'banned'),
            array_fill(0, 20, 'pending'),
        );
        shuffle($statuses);

        $faker = fake();
        $batchSize = 100;
        $totalCustomers = 1000;

        for ($i = 0; $i < $totalCustomers; $i += $batchSize) {
            $batchCount = min($batchSize, $totalCustomers - $i);

            for ($j = 0; $j < $batchCount; $j++) {
                $index = $i + $j;
                $createdAt = now()->subDays(rand(1, 365));

                $user = User::factory()->create([
                    'name' => $faker->name(),
                    'email' => "demo_{$index}@".self::DEMO_DOMAIN,
                    'status' => $statuses[$index] ?? 'active',
                    'phone' => $faker->optional(0.7)->phoneNumber(),
                    'company' => $faker->optional(0.4)->company(),
                    'created_at' => $createdAt,
                    'updated_at' => $createdAt,
                ]);

                $user->assignRole('customer');

                $user->profile()->create([
                    'billing_address_line1' => $faker->streetAddress(),
                    'billing_city' => $faker->city(),
                    'billing_state' => $faker->stateAbbr(),
                    'billing_zip' => $faker->postcode(),
                    'billing_country' => $faker->randomElement(['US', 'CA', 'GB', 'DE', 'FR', 'AU', 'NL', 'SE']),
                    'tax_exempt' => $faker->boolean(5),
                    'company_name' => $user->company,
                ]);

                $customers->push($user);
            }
        }

        return $customers;
    }

    /**
     * Create ~1500 subscriptions linked to real plans (70% VPS).
     *
     * Returns a map of subscription_id => [user_id, plan_id] for use by other seeders.
     *
     * @param  \Illuminate\Support\Collection<int, User>  $customers
     * @param  \Illuminate\Support\Collection<int, Plan>  $plans
     * @return array<int, array{user_id: int, plan_id: int}>
     */
    private function createSubscriptions(\Illuminate\Support\Collection $customers, \Illuminate\Support\Collection $plans): array
    {
        $subscriptionMap = [];
        $statuses = ['active', 'active', 'active', 'active', 'active', 'active', 'active', 'canceled', 'past_due', 'trialing'];
        $total = 1500;

        // Separate plans by type for weighted distribution
        $vpsPlans = $plans->where('service_type', 'vps');
        $otherPlans = $plans->whereNotIn('service_type', ['vps']);
        $rows = [];

        for ($i = 0; $i < $total; $i++) {
            $customer = $customers->random();
            // 70% VPS, 30% other services
            $plan = (rand(1, 100) <= 70 && $vpsPlans->isNotEmpty())
                ? $vpsPlans->random()
                : ($otherPlans->isNotEmpty() ? $otherPlans->random() : $plans->random());
            $status = $statuses[array_rand($statuses)];
            $createdAt = $customer->created_at->copy()->addDays(rand(0, 60));

            if ($createdAt->isFuture()) {
                $createdAt = now()->subDays(rand(1, 30));
            }

            $periodStart = $createdAt->copy();
            $periodEnd = match ($plan->billing_cycle) {
                'quarterly' => $periodStart->copy()->addMonths(3),
                'annual' => $periodStart->copy()->addYear(),
                default => $periodStart->copy()->addMonth(),
            };

            $endsAt = null;
            $cancelledAt = null;
            if ($status === 'canceled') {
                $cancelledAt = $createdAt->copy()->addDays(rand(30, 180));
                $endsAt = $cancelledAt->copy()->addDays(rand(0, 30));
            }

            $trialEndsAt = null;
            if ($status === 'trialing') {
                $trialEndsAt = now()->addDays(rand(1, 14));
            }

            $rows[] = [
                'user_id' => $customer->id,
                'type' => 'default',
                'stripe_id' => 'sub_demo_'.Str::random(14).'_'.$i,
                'stripe_status' => $status,
                'stripe_price' => 'price_demo_'.Str::random(10),
                'quantity' => 1,
                'trial_ends_at' => $trialEndsAt,
                'ends_at' => $endsAt,
                'plan_id' => $plan->id,
                'gateway' => fake()->randomElement(['stripe', 'stripe', 'stripe', 'paypal']),
                'gateway_subscription_id' => 'sub_demo_'.Str::random(14),
                'gateway_customer_id' => 'cus_demo_'.Str::random(14),
                'gateway_price_id' => 'price_demo_'.Str::random(14),
                'current_period_start' => $periodStart,
                'current_period_end' => $periodEnd,
                'cancelled_at' => $cancelledAt,
                'created_at' => $createdAt,
                'updated_at' => $createdAt,
            ];
        }

        // Bulk insert in chunks
        foreach (array_chunk($rows, 100) as $chunk) {
            DB::table('subscriptions')->insert($chunk);
        }

        // Build the subscription map (query back IDs)
        $allSubs = DB::table('subscriptions')
            ->where('stripe_id', 'like', 'sub_demo_%')
            ->select('id', 'user_id', 'plan_id')
            ->get();

        foreach ($allSubs as $sub) {
            $subscriptionMap[$sub->id] = [
                'user_id' => $sub->user_id,
                'plan_id' => $sub->plan_id,
            ];
        }

        return $subscriptionMap;
    }

    /**
     * Create ~400 services.
     *
     * @param  \Illuminate\Support\Collection<int, User>  $customers
     * @param  \Illuminate\Support\Collection<int, Plan>  $plans
     * @param  array<int, array{user_id: int, plan_id: int}>  $subscriptionMap
     */
    private function createServices(\Illuminate\Support\Collection $customers, \Illuminate\Support\Collection $plans, array $subscriptionMap): void
    {
        $platformMap = [
            'vps' => 'virtfusion',
            'dedicated' => 'synergycp',
            'hosting' => 'enhance',
            'game_server' => 'pterodactyl',
            'mysql' => 'enhance',
        ];

        $serviceStatuses = array_merge(
            array_fill(0, 1000, 'active'),
            array_fill(0, 100, 'suspended'),
            array_fill(0, 50, 'pending'),
            array_fill(0, 50, 'terminated'),
        );
        shuffle($serviceStatuses);

        $subIds = array_keys($subscriptionMap);
        $rows = [];
        $faker = fake();
        $total = 1200;

        // Separate plans by type for weighted distribution
        $vpsPlans = $plans->where('service_type', 'vps');
        $otherPlans = $plans->whereNotIn('service_type', ['vps']);

        for ($i = 0; $i < $total; $i++) {
            $customer = $customers->random();
            // 70% VPS, 30% other services
            $plan = (rand(1, 100) <= 70 && $vpsPlans->isNotEmpty())
                ? $vpsPlans->random()
                : ($otherPlans->isNotEmpty() ? $otherPlans->random() : $plans->random());
            $status = $serviceStatuses[$i] ?? 'active';
            $serviceType = $plan->service_type;
            $platform = $platformMap[$serviceType] ?? 'virtfusion';

            // Try to link to a subscription for this user
            $subId = null;
            foreach ($subIds as $sid) {
                if ($subscriptionMap[$sid]['user_id'] === $customer->id) {
                    $subId = $sid;
                    break;
                }
            }

            $createdAt = $customer->created_at->copy()->addDays(rand(1, 90));
            if ($createdAt->isFuture()) {
                $createdAt = now()->subDays(rand(1, 30));
            }

            $provisionedAt = $status !== 'pending' ? $createdAt->copy()->addHours(rand(1, 48)) : null;
            $suspendedAt = $status === 'suspended' ? $createdAt->copy()->addDays(rand(30, 120)) : null;
            $terminatedAt = $status === 'terminated' ? $createdAt->copy()->addDays(rand(60, 180)) : null;

            $hostParts = [$faker->domainWord(), $faker->randomElement(['us-east', 'us-west', 'eu-west', 'ap-south'])];

            $rows[] = [
                'user_id' => $customer->id,
                'subscription_id' => $subId,
                'plan_id' => $plan->id,
                'service_type' => $serviceType,
                'platform' => $platform,
                'platform_service_id' => (string) $faker->numberBetween(10000, 99999),
                'status' => $status,
                'ipv4_address' => $faker->ipv4(),
                'ipv6_address' => $faker->optional(0.3)->ipv6(),
                'hostname' => implode('-', $hostParts).'.ezscale.cloud',
                'domain' => $faker->optional(0.5)->domainName(),
                'credentials' => null,
                'provisioned_at' => $provisionedAt,
                'suspended_at' => $suspendedAt,
                'terminated_at' => $terminatedAt,
                'auto_renew' => $faker->boolean(85),
                'created_at' => $createdAt,
                'updated_at' => $createdAt,
            ];
        }

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

    /**
     * Create ~2000 invoices with line items.
     *
     * @param  \Illuminate\Support\Collection<int, User>  $customers
     * @param  \Illuminate\Support\Collection<int, Plan>  $plans
     * @param  array<int, array{user_id: int, plan_id: int}>  $subscriptionMap
     * @return array<int, array{id: int, user_id: int, total: float, status: string}>
     */
    private function createInvoices(\Illuminate\Support\Collection $customers, \Illuminate\Support\Collection $plans, array $subscriptionMap): array
    {
        $invoiceStatuses = array_merge(
            array_fill(0, 1400, 'paid'),
            array_fill(0, 300, 'pending'),
            array_fill(0, 200, 'overdue'),
            array_fill(0, 100, 'void'),
        );
        shuffle($invoiceStatuses);

        $subIds = array_keys($subscriptionMap);
        $invoiceRows = [];
        $invoiceTracker = [];
        $faker = fake();
        $total = 2000;

        for ($i = 0; $i < $total; $i++) {
            $customer = $customers->random();
            $plan = $plans->random();
            $status = $invoiceStatuses[$i] ?? 'paid';

            // Link to a subscription if possible
            $subId = null;
            foreach ($subIds as $sid) {
                if ($subscriptionMap[$sid]['user_id'] === $customer->id) {
                    $subId = $sid;
                    break;
                }
            }

            $price = (float) $plan->price;
            $tax = round($price * fake()->randomFloat(2, 0.05, 0.12), 2);
            $invoiceTotal = round($price + $tax, 2);

            $createdAt = now()->subDays(rand(1, 365));
            $dueDate = $createdAt->copy()->addDays(rand(14, 30));
            $paidAt = null;

            if ($status === 'paid') {
                $paidAt = $createdAt->copy()->addDays(rand(0, 14));
            }

            if ($status === 'overdue') {
                $dueDate = now()->subDays(rand(1, 60));
            }

            $gateway = $faker->randomElement(['stripe', 'stripe', 'stripe', 'paypal']);

            $invoiceRows[] = [
                'user_id' => $customer->id,
                'subscription_id' => $subId,
                'gateway' => $gateway,
                'gateway_invoice_id' => 'in_demo_'.Str::random(14).'_'.$i,
                'number' => 'INV-'.str_pad((string) (100000 + $i), 6, '0', STR_PAD_LEFT),
                'total' => $invoiceTotal,
                'tax' => $tax,
                'currency' => 'USD',
                'status' => $status,
                'invoice_pdf' => null,
                'due_date' => $dueDate,
                'paid_at' => $paidAt,
                'created_at' => $createdAt,
                'updated_at' => $createdAt,
            ];

            $invoiceTracker[] = [
                'user_id' => $customer->id,
                'total' => $invoiceTotal,
                'status' => $status,
                'plan_name' => $plan->name,
                'plan_price' => $price,
            ];
        }

        foreach (array_chunk($invoiceRows, 100) as $chunk) {
            DB::table('invoices')->insert($chunk);
        }

        // Get the IDs back for invoice items and payment transactions
        $invoiceIds = DB::table('invoices')
            ->where('gateway_invoice_id', 'like', 'in_demo_%')
            ->orderBy('id')
            ->pluck('id')
            ->toArray();

        // Create invoice items for each invoice
        $itemRows = [];
        foreach ($invoiceIds as $idx => $invoiceId) {
            $tracker = $invoiceTracker[$idx] ?? null;
            if (! $tracker) {
                continue;
            }

            $itemRows[] = [
                'invoice_id' => $invoiceId,
                'description' => ($tracker['plan_name'] ?? 'Service').' - Monthly Subscription',
                'amount' => $tracker['plan_price'] ?? 10.00,
                'quantity' => 1,
                'created_at' => now(),
                'updated_at' => now(),
            ];
        }

        foreach (array_chunk($itemRows, 200) as $chunk) {
            DB::table('invoice_items')->insert($chunk);
        }

        // Build return data
        $result = [];
        foreach ($invoiceIds as $idx => $invoiceId) {
            $tracker = $invoiceTracker[$idx] ?? null;
            $result[] = [
                'id' => $invoiceId,
                'user_id' => $tracker['user_id'] ?? 0,
                'total' => $tracker['total'] ?? 0,
                'status' => $tracker['status'] ?? 'paid',
            ];
        }

        return $result;
    }

    /**
     * Create ~600 payment transactions.
     *
     * @param  \Illuminate\Support\Collection<int, User>  $customers
     * @param  array<int, array{id: int, user_id: int, total: float, status: string}>  $invoiceData
     */
    private function createPaymentTransactions(\Illuminate\Support\Collection $customers, array $invoiceData): void
    {
        $rows = [];
        $faker = fake();
        $total = 1500;

        // Use paid/pending invoices for payment transactions
        $paidInvoices = array_filter($invoiceData, fn ($inv) => $inv['status'] === 'paid');
        $paidInvoices = array_values($paidInvoices);

        $transactionStatuses = array_merge(
            array_fill(0, 1200, 'succeeded'),
            array_fill(0, 150, 'failed'),
            array_fill(0, 100, 'refunded'),
            array_fill(0, 50, 'pending'),
        );
        shuffle($transactionStatuses);

        for ($i = 0; $i < $total; $i++) {
            $status = $transactionStatuses[$i] ?? 'succeeded';

            // Link to an invoice if available
            $invoiceId = null;
            $userId = $customers->random()->id;
            $amount = round($faker->randomFloat(2, 4, 200), 2);

            if (! empty($paidInvoices) && $i < count($paidInvoices)) {
                $inv = $paidInvoices[$i];
                $invoiceId = $inv['id'];
                $userId = $inv['user_id'];
                $amount = (float) $inv['total'];
            }

            $gateway = $faker->randomElement(['stripe', 'stripe', 'stripe', 'paypal']);
            $paymentMethod = $gateway === 'stripe'
                ? $faker->randomElement(['card', 'card', 'card', 'bank_transfer'])
                : 'paypal';

            $createdAt = now()->subDays(rand(1, 365));

            $rows[] = [
                'user_id' => $userId,
                'subscription_id' => null,
                'invoice_id' => $invoiceId,
                'gateway' => $gateway,
                'gateway_transaction_id' => ($gateway === 'stripe' ? 'pi_demo_' : 'PAY-DEMO-').Str::random(14).'_'.$i,
                'amount' => $amount,
                'currency' => 'USD',
                'status' => $status,
                'payment_method' => $paymentMethod,
                'description' => match ($status) {
                    'succeeded' => 'Payment for hosting service',
                    'failed' => 'Payment declined - '.fake()->randomElement(['insufficient funds', 'card expired', 'bank declined']),
                    'refunded' => 'Refund processed',
                    'pending' => 'Payment processing',
                    default => 'Payment',
                },
                'metadata' => json_encode(['demo' => true]),
                'created_at' => $createdAt,
                'updated_at' => $createdAt,
            ];
        }

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

    /**
     * Create ~150 orders.
     *
     * @param  \Illuminate\Support\Collection<int, User>  $customers
     * @param  \Illuminate\Support\Collection<int, Plan>  $plans
     */
    private function createOrders(\Illuminate\Support\Collection $customers, \Illuminate\Support\Collection $plans): void
    {
        $orderStatuses = array_merge(
            array_fill(0, 250, 'completed'),
            array_fill(0, 70, 'pending'),
            array_fill(0, 50, 'processing'),
            array_fill(0, 30, 'cancelled'),
        );
        shuffle($orderStatuses);

        $rows = [];
        $faker = fake();
        $total = 400;

        for ($i = 0; $i < $total; $i++) {
            $customer = $customers->random();
            $plan = $plans->random();
            $status = $orderStatuses[$i] ?? 'completed';
            $createdAt = now()->subDays(rand(1, 365));

            $completedAt = null;
            $cancelledAt = null;

            if ($status === 'completed') {
                $completedAt = $createdAt->copy()->addDays(rand(0, 3));
            }

            if ($status === 'cancelled') {
                $cancelledAt = $createdAt->copy()->addDays(rand(0, 7));
            }

            $rows[] = [
                'user_id' => $customer->id,
                'plan_id' => $plan->id,
                'invoice_id' => null,
                'service_id' => null,
                'order_number' => 'ORD-'.strtoupper(Str::random(8)).$i,
                'status' => $status,
                'total' => (float) $plan->price,
                'currency' => 'USD',
                'payment_gateway' => $faker->randomElement(['stripe', 'paypal']),
                'configuration' => json_encode([
                    'hostname' => $faker->domainWord().'.ezscale.cloud',
                    'location' => $faker->randomElement(['us-east-1', 'us-west-1', 'eu-west-1', 'ap-south-1']),
                    'os' => $faker->randomElement(['Ubuntu 24.04', 'Debian 12', 'Rocky Linux 9', 'AlmaLinux 9', 'Windows Server 2022']),
                ]),
                'admin_notes' => $faker->optional(0.2)->sentence(),
                'completed_at' => $completedAt,
                'cancelled_at' => $cancelledAt,
                'created_at' => $createdAt,
                'updated_at' => $createdAt,
            ];
        }

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

    /**
     * Create ~200 support tickets with 2-5 replies each.
     *
     * @param  \Illuminate\Support\Collection<int, User>  $customers
     */
    private function createSupportTickets(\Illuminate\Support\Collection $customers, int $adminId): void
    {
        $faker = fake();
        $ticketSubjects = [
            'billing' => [
                'Invoice not received', 'Payment failed', 'Request refund', 'Billing cycle question',
                'Upgrade pricing inquiry', 'Coupon not working', 'Double charged', 'Payment method update help',
                'Tax exemption request', 'Downgrade subscription',
            ],
            'technical' => [
                'Server not responding', 'High CPU usage', 'Cannot SSH into server', 'DNS not resolving',
                'SSL certificate issue', 'Database connection error', 'Slow website performance',
                'Firewall blocking port', 'Disk space full', 'Container restart loop',
            ],
            'sales' => [
                'Custom server configuration', 'Volume discount inquiry', 'Enterprise plan availability',
                'Reseller program info', 'Data center locations', 'SLA details', 'Migration assistance',
                'Bulk order pricing', 'Partner program', 'Referral program',
            ],
            'general' => [
                'Account access issue', 'Two-factor auth locked out', 'Email not verified',
                'Profile update help', 'API documentation', 'Feature request', 'Service status inquiry',
                'Password reset not working', 'Terms of service question', 'Account closure request',
            ],
        ];

        $ticketStatuses = array_merge(
            array_fill(0, 150, 'open'),
            array_fill(0, 125, 'in_progress'),
            array_fill(0, 100, 'waiting'),
            array_fill(0, 125, 'closed'),
        );
        shuffle($ticketStatuses);

        $priorities = ['low', 'low', 'medium', 'medium', 'medium', 'high', 'high', 'urgent'];
        $total = 500;

        $ticketRows = [];
        $ticketMeta = [];

        for ($i = 0; $i < $total; $i++) {
            $customer = $customers->random();
            $status = $ticketStatuses[$i] ?? 'open';
            $department = $faker->randomElement(['billing', 'technical', 'sales', 'general']);
            $priority = $priorities[array_rand($priorities)];
            $subjects = $ticketSubjects[$department];
            $subject = $subjects[array_rand($subjects)];

            $createdAt = now()->subDays(rand(1, 180));
            $lastReplyAt = $status !== 'open' ? $createdAt->copy()->addDays(rand(0, 14)) : null;

            $ticketRows[] = [
                'user_id' => $customer->id,
                'subject' => $subject,
                'status' => $status,
                'priority' => $priority,
                'department' => $department,
                'last_reply_at' => $lastReplyAt,
                'created_at' => $createdAt,
                'updated_at' => $createdAt,
            ];

            $ticketMeta[] = [
                'user_id' => $customer->id,
                'department' => $department,
                'created_at' => $createdAt,
            ];
        }

        foreach (array_chunk($ticketRows, 100) as $chunk) {
            DB::table('support_tickets')->insert($chunk);
        }

        // Get ticket IDs back
        $ticketIds = DB::table('support_tickets')
            ->whereIn('user_id', $customers->pluck('id')->toArray())
            ->orderBy('id')
            ->pluck('id')
            ->toArray();

        // Limit to the tickets we just created (in case there were pre-existing ones)
        $ticketIds = array_slice($ticketIds, -$total);

        // Create 2-5 replies per ticket
        $replyTemplates = [
            'customer' => [
                'I need help with this issue urgently. Please assist.',
                'This is causing problems for my business. When can I expect a resolution?',
                'Thank you for looking into this. Any update on the status?',
                'I have tried the steps you suggested but the issue persists.',
                'Could you please provide more details on how to resolve this?',
                'The issue seems to have gotten worse since my last update.',
                'I appreciate your help. Let me know if you need more information.',
                'This has been ongoing for several days now. Please escalate.',
            ],
            'staff' => [
                'Thank you for reaching out. I am looking into this for you now.',
                'I have identified the issue and am working on a fix. I will update you shortly.',
                'Could you please provide your server IP and any error messages you are seeing?',
                'I have escalated this to our senior team. You should hear back within 24 hours.',
                'The issue has been resolved. Please confirm that everything is working correctly.',
                'I have applied a credit to your account for the inconvenience. Is there anything else?',
                'I have updated the firewall rules. Please try connecting again.',
                'This appears to be resolved on our end. Please let us know if you experience further issues.',
            ],
        ];

        $replyRows = [];

        foreach ($ticketIds as $idx => $ticketId) {
            $meta = $ticketMeta[$idx] ?? null;
            if (! $meta) {
                continue;
            }

            $replyCount = rand(2, 5);
            $replyDate = $meta['created_at']->copy();

            for ($r = 0; $r < $replyCount; $r++) {
                $isStaff = $r % 2 !== 0; // alternate customer/staff
                $replyDate = $replyDate->copy()->addHours(rand(1, 48));

                if ($replyDate->isFuture()) {
                    $replyDate = now()->subMinutes(rand(1, 60));
                }

                $templates = $isStaff ? $replyTemplates['staff'] : $replyTemplates['customer'];

                $replyRows[] = [
                    'ticket_id' => $ticketId,
                    'user_id' => $isStaff ? $adminId : $meta['user_id'],
                    'body' => $templates[array_rand($templates)],
                    'is_staff_reply' => $isStaff,
                    'created_at' => $replyDate,
                    'updated_at' => $replyDate,
                ];
            }
        }

        foreach (array_chunk($replyRows, 200) as $chunk) {
            DB::table('ticket_replies')->insert($chunk);
        }
    }

    /**
     * Create ~50 coupons.
     */
    private function createCoupons(): void
    {
        $rows = [];
        $faker = fake();

        $couponNames = [
            'WELCOME', 'SUMMER', 'WINTER', 'SPRING', 'FALL', 'HOLIDAY', 'NEWYEAR',
            'BLACKFRI', 'CYBERMON', 'LAUNCH', 'EARLYBIRD', 'LOYALTY', 'REFER',
            'VPS10', 'VPS20', 'VPS30', 'HOST15', 'HOST25', 'DEDI10', 'DEDI20',
            'SAVE10', 'SAVE15', 'SAVE20', 'SAVE25', 'SAVE30', 'SAVE40', 'SAVE50',
            'FIRST50', 'ANNUAL20', 'MIGRATE', 'TRANSFER', 'STARTUP', 'STUDENT',
            'NONPROFIT', 'DEVOPS', 'CLOUD10', 'CLOUD20', 'CLOUD30', 'BF2025',
            'CM2025', 'NY2026', 'SPECIAL', 'FLASH', 'LIMITED', 'EXCLUSIVE',
            'PARTNER', 'AGENCY', 'RESELLER', 'BULK', 'ENTERPRISE',
        ];

        $total = 100;

        for ($i = 0; $i < $total; $i++) {
            $type = $faker->randomElement(['percentage', 'percentage', 'fixed_amount']);
            $value = $type === 'percentage'
                ? $faker->randomElement([5, 10, 15, 20, 25, 30, 40, 50])
                : $faker->randomFloat(2, 2, 25);

            $isActive = $faker->boolean(70);
            $expiresAt = $faker->optional(0.6)->dateTimeBetween('-3 months', '+6 months');
            $maxUses = $faker->optional(0.5)->numberBetween(10, 500);
            $timesUsed = $maxUses ? $faker->numberBetween(0, (int) min($maxUses, 100)) : $faker->numberBetween(0, 50);

            $createdAt = now()->subDays(rand(1, 300));

            $rows[] = [
                'code' => $couponNames[$i] ?? strtoupper(Str::random(6)),
                'type' => $type,
                'value' => $value,
                'currency' => $type === 'fixed_amount' ? 'USD' : null,
                'applies_to' => $faker->optional(0.3)->randomElement([
                    json_encode(['service_type' => 'vps']),
                    json_encode(['service_type' => 'dedicated']),
                    json_encode(['service_type' => 'hosting']),
                ]),
                'max_uses' => $maxUses,
                'times_used' => $timesUsed,
                'active' => $isActive,
                'expires_at' => $expiresAt,
                'created_at' => $createdAt,
                'updated_at' => $createdAt,
            ];
        }

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

    /**
     * Create ~100 audit log entries.
     *
     * @param  \Illuminate\Support\Collection<int, User>  $customers
     */
    private function createAuditLogs(\Illuminate\Support\Collection $customers, int $adminId): void
    {
        $actions = [
            ['action' => 'user.created', 'resource_type' => 'User'],
            ['action' => 'user.updated', 'resource_type' => 'User'],
            ['action' => 'user.suspended', 'resource_type' => 'User'],
            ['action' => 'user.banned', 'resource_type' => 'User'],
            ['action' => 'user.reactivated', 'resource_type' => 'User'],
            ['action' => 'service.provisioned', 'resource_type' => 'Service'],
            ['action' => 'service.suspended', 'resource_type' => 'Service'],
            ['action' => 'service.terminated', 'resource_type' => 'Service'],
            ['action' => 'invoice.created', 'resource_type' => 'Invoice'],
            ['action' => 'invoice.paid', 'resource_type' => 'Invoice'],
            ['action' => 'invoice.voided', 'resource_type' => 'Invoice'],
            ['action' => 'subscription.created', 'resource_type' => 'Subscription'],
            ['action' => 'subscription.cancelled', 'resource_type' => 'Subscription'],
            ['action' => 'coupon.created', 'resource_type' => 'Coupon'],
            ['action' => 'coupon.deactivated', 'resource_type' => 'Coupon'],
            ['action' => 'plan.updated', 'resource_type' => 'Plan'],
            ['action' => 'order.completed', 'resource_type' => 'Order'],
            ['action' => 'order.cancelled', 'resource_type' => 'Order'],
            ['action' => 'impersonation.started', 'resource_type' => 'User'],
            ['action' => 'impersonation.stopped', 'resource_type' => 'User'],
            ['action' => 'settings.updated', 'resource_type' => 'Setting'],
            ['action' => 'login', 'resource_type' => null],
            ['action' => 'logout', 'resource_type' => null],
        ];

        $faker = fake();
        $rows = [];
        $total = 300;

        $userAgents = [
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/120.0.0.0',
            'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36',
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Safari/605.1.15',
        ];

        for ($i = 0; $i < $total; $i++) {
            $actionDef = $actions[array_rand($actions)];
            $customer = $customers->random();
            $createdAt = now()->subDays(rand(1, 180));

            $changes = null;
            $action = $actionDef['action'];

            // Generate realistic changes based on action type
            if ($action === 'customer.updated') {
                // Per-field old/new format (most common for updates)
                $changes = json_encode([
                    'name' => ['old' => $faker->name(), 'new' => $faker->name()],
                    'email' => ['old' => $faker->safeEmail(), 'new' => $faker->safeEmail()],
                ]);
            } elseif ($action === 'customer.suspended' || $action === 'customer.banned') {
                // Per-field old/new for status changes
                $newStatus = str_contains($action, 'suspended') ? 'suspended' : 'banned';
                $changes = json_encode([
                    'status' => ['old' => 'active', 'new' => $newStatus],
                ]);
            } elseif ($action === 'service.updated') {
                // Per-field old/new with plan change
                $plans = ['Starter VPS', 'Basic VPS', 'Pro VPS', 'Enterprise VPS', 'Basic Dedicated', 'Pro Dedicated'];
                $oldPlan = $faker->randomElement($plans);
                $newPlan = $faker->randomElement(array_diff($plans, [$oldPlan]));
                $changes = json_encode([
                    'plan' => ['old' => $oldPlan, 'new' => $newPlan],
                    'status' => ['old' => 'active', 'new' => 'active'],
                ]);
            } elseif ($action === 'plan.updated') {
                // Per-field old/new with pricing
                $changes = json_encode([
                    'price' => ['old' => number_format($faker->randomFloat(2, 5, 50), 2), 'new' => number_format($faker->randomFloat(2, 5, 50), 2)],
                    'name' => ['old' => $faker->word().' Plan', 'new' => $faker->word().' Plan'],
                    'disk_space' => ['old' => $faker->randomElement(['20GB', '50GB', '100GB']), 'new' => $faker->randomElement(['50GB', '100GB', '200GB'])],
                ]);
            } elseif ($action === 'settings.updated') {
                // Per-field old/new for settings
                $changes = json_encode([
                    'site_name' => ['old' => 'EZSCALE Cloud', 'new' => 'EZSCALE Cloud Hosting'],
                    'maintenance_mode' => ['old' => false, 'new' => true],
                ]);
            } elseif (str_contains($action, 'created') || str_contains($action, 'completed')) {
                // Top-level old/new (new only = create)
                $changes = json_encode([
                    'old' => null,
                    'new' => [
                        'status' => $faker->randomElement(['active', 'pending', 'completed']),
                        'amount' => number_format($faker->randomFloat(2, 10, 500), 2),
                    ],
                ]);
            } elseif (str_contains($action, 'cancelled') || str_contains($action, 'voided')) {
                // Top-level old/new (both = update)
                $changes = json_encode([
                    'old' => ['status' => 'active'],
                    'new' => ['status' => $faker->randomElement(['cancelled', 'voided'])],
                ]);
            } elseif ($action === 'impersonation.started') {
                // Flat key-value data
                $changes = json_encode([
                    'target_user' => $faker->name(),
                    'target_email' => $faker->safeEmail(),
                    'reason' => 'Support request',
                ]);
            }

            $rows[] = [
                'user_id' => $customer->id,
                'admin_id' => $adminId,
                'action' => $actionDef['action'],
                'resource_type' => $actionDef['resource_type'],
                'resource_id' => $actionDef['resource_type'] ? $faker->numberBetween(1, 500) : null,
                'ip_address' => $faker->ipv4(),
                'user_agent' => $userAgents[array_rand($userAgents)],
                'changes' => $changes,
                'created_at' => $createdAt,
                'updated_at' => $createdAt,
            ];
        }

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