<?php

namespace Database\Seeders;

use App\Models\Backup;
use App\Models\Customer;
use App\Models\Datacenter;
use App\Models\Host;
use App\Models\Instance;
use App\Models\InstanceMetric;
use App\Models\InstanceTraffic;
use App\Models\Order;
use App\Models\Seat;
use App\Models\Subscription;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;

/**
 * One complete customer, written as real rows.
 *
 * The panel used to be judged against fixtures baked into the views, and every
 * round of that hid a different gap: a card with no data source, a figure that
 * came from a translation string, a chart drawn from an array in a Blade file.
 * A real customer with real rows makes the panel tell the truth about itself —
 * whatever is missing shows up as missing.
 *
 * Everything hangs off one User, so removing the demo is one deletion:
 *
 *     php artisan db:seed --class=DemoCustomerSeeder --force
 *     php artisan tinker --execute="App\Models\User::where('email','demo@clupilot.com')->first()?->delete()"
 *
 * Safe to run twice: every row is matched on a natural key first.
 */
class DemoCustomerSeeder extends Seeder
{
    private const EMAIL = 'demo@clupilot.com';

    public function run(): void
    {
        // The demo account is a real login on a panel that is reachable from the
        // public internet, so it never gets a password written in a source file.
        // DEMO_PASSWORD if the operator set one, otherwise a fresh random one
        // printed once — a weak default would sit on production forever, and
        // "it is only the demo" is how the first account gets taken.
        $password = (string) (env('DEMO_PASSWORD') ?: Str::password(24));

        $user = User::firstOrCreate(
            ['email' => self::EMAIL],
            ['name' => 'Kanzlei Muster', 'password' => Hash::make($password)],
        );

        if ($user->wasRecentlyCreated) {
            $this->command?->warn('Demo password (shown once): '.$password);
        }

        $customer = Customer::firstOrCreate(
            ['user_id' => $user->id],
            ['name' => 'Kanzlei Muster', 'email' => self::EMAIL, 'contact_name' => 'Dr. M. Muster', 'status' => 'active'],
        );

        $datacenter = Datacenter::firstOrCreate(
            ['code' => 'fsn'],
            ['name' => 'Falkenstein', 'location' => 'DE', 'active' => true],
        );

        $host = Host::query()->where('datacenter', $datacenter->code)->first()
            ?? Host::factory()->create(['datacenter' => $datacenter->code, 'name' => 'fsn-01', 'status' => 'active']);

        $order = Order::firstOrCreate(
            ['customer_id' => $customer->id, 'type' => 'plan'],
            [
                'plan' => 'team',
                // 7900 = 79,00 €, der Team-Monatspreis auf der neuen Leiter.
                'amount_cents' => 7900,
                'currency' => 'EUR',
                'datacenter' => $datacenter->code,
                'status' => 'paid',
            ],
        );

        $instance = Instance::firstOrCreate(
            ['customer_id' => $customer->id],
            [
                'order_id' => $order->id,
                'host_id' => $host->id,
                'vmid' => 9001,
                'plan' => 'team',
                'quota_gb' => 500,
                'disk_gb' => 500,
                'ram_mb' => 8192,
                'cores' => 4,
                'traffic_addons' => 0,
                'guest_ip' => '10.20.0.31',
                'subdomain' => 'kanzlei-muster',
                'status' => 'active',
                'cert_ok' => true,
                'route_written' => true,
            ],
        );

        Subscription::firstOrCreate(
            ['customer_id' => $customer->id],
            [
                'order_id' => $order->id,
                'instance_id' => $instance->id,
                'plan' => 'team',
                'tier' => 2,
                'term' => 'monthly',
                // 7900 = 79,00 €, derselbe Team-Monatspreis wie oben.
                'price_cents' => 7900,
                'currency' => 'EUR',
                'quota_gb' => 500,
                'traffic_gb' => 3072,
                'seats' => 25,
                'ram_mb' => 8192,
                'cores' => 4,
                'disk_gb' => 500,
                'performance' => 'enhanced',
                'status' => 'active',
                'started_at' => now()->subMonths(4),
                'current_period_start' => now()->startOfMonth(),
                'current_period_end' => now()->addMonth()->startOfMonth(),
            ],
        );

        $this->seats($customer);
        $this->backups($instance);
        $this->traffic($instance);
        $this->metrics($instance);

        $this->command?->info('Demo customer ready: '.self::EMAIL);
    }

    private function seats(Customer $customer): void
    {
        $people = [
            ['Dr. M. Muster', 'demo@clupilot.com', 'owner', 'active'],
            ['S. Berger', 'berger@kanzlei-muster.test', 'admin', 'active'],
            ['A. Wimmer', 'wimmer@kanzlei-muster.test', 'member', 'active'],
            ['T. Klein', 'klein@kanzlei-muster.test', 'member', 'active'],
            ['P. Hofer', 'hofer@kanzlei-muster.test', 'member', 'invited'],
            ['Steuerberatung Ost', 'extern@partner.test', 'readonly', 'active'],
        ];

        foreach ($people as [$name, $email, $role, $status]) {
            Seat::firstOrCreate(
                ['customer_id' => $customer->id, 'email' => $email],
                ['name' => $name, 'role' => $role, 'status' => $status, 'invited_at' => now()->subDays(30)],
            );
        }
    }

    private function backups(Instance $instance): void
    {
        Backup::firstOrCreate(
            ['instance_id' => $instance->id, 'schedule' => 'täglich 03:00'],
            ['external_job_id' => 'demo-backup', 'status' => 'ok', 'last_ok_at' => now()->setTime(3, 12)],
        );
    }

    private function traffic(Instance $instance): void
    {
        InstanceTraffic::firstOrCreate(
            ['instance_id' => $instance->id, 'period' => InstanceTraffic::currentPeriod()],
            [
                'rx_bytes' => (int) (340 * 1024 ** 3),
                'tx_bytes' => (int) (72 * 1024 ** 3),
                'last_netin' => 0,
                'last_netout' => 0,
                'sampled_at' => now(),
            ],
        );
    }

    /**
     * Thirty days of samples: a disk that fills slowly, transfer that varies,
     * and one day where two checks out of 288 failed.
     *
     * Deliberately not a smooth curve — a straight line is the first thing that
     * gives a demo away, and a chart that never wobbles teaches nobody what a
     * real one looks like.
     */
    private function metrics(Instance $instance): void
    {
        $total = (int) (500 * 1024 ** 3);
        $used = (int) (198 * 1024 ** 3);
        $wobble = [1.0, 0.6, 1.4, 0.9, 1.7, 0.4, 0.2, 1.1, 1.5, 0.8];

        foreach (range(29, 0) as $offset) {
            $day = Carbon::today()->subDays($offset);
            $i = 29 - $offset;
            $used += (int) ($wobble[$i % 10] * 0.42 * 1024 ** 3);

            InstanceMetric::updateOrCreate(
                ['instance_id' => $instance->id, 'day' => $day->toDateString()],
                [
                    'disk_used_bytes' => $used,
                    'disk_total_bytes' => $total,
                    'rx_bytes' => (int) ((9 + $wobble[$i % 10] * 4) * 1024 ** 3),
                    'tx_bytes' => (int) ((2 + $wobble[($i + 3) % 10]) * 1024 ** 3),
                    // 288 checks a day is one every five minutes.
                    'checks_total' => 288,
                    'checks_ok' => $offset === 12 ? 286 : 288,
                ],
            );
        }
    }
}
