<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

/**
 * Der Nachtrag zur Umschaltung, für einen Bestand, auf dem sie schon lief.
 *
 * `2026_08_01_000004_switch_to_new_plan_ladder.php` erkannte einen bereits
 * umgeschalteten Bestand daran, dass die laufende Version dieselbe
 * `quota_gb` UND `disk_gb` trägt wie die Vorgabe — zwei von neun Feldern.
 * Bei Intern waren beide schon vorher richtig (5 GB / 20 GB, von Hand über
 * die Konsole angelegt), also hielt die Migration das Paket für erledigt und
 * ließ RAM, Kerne und Plätze auf ihren alten Werten stehen (1024 MB, 1 Kern,
 * 5 Plätze statt 4096 MB, 2 Kernen, 3 Plätzen). Die Prüfung selbst ist in
 * derselben Migration korrigiert — aber eine Migration läuft nur einmal, und
 * auf einem Bestand, der sie schon hinter sich hat, ändert die Korrektur
 * nichts mehr rückwirkend. Dieser Nachtrag schreibt für genau diesen Fall
 * vorwärts, was die erste Migration hätte schreiben sollen, statt die
 * gelaufene Migration im Nachhinein umzuschreiben.
 *
 * Enterprise zieht hier mit: die erste Migration setzt es auf
 * `sales_enabled = false` und macht es damit weder käuflich noch
 * verschenkbar — im Widerspruch zum Entwurf, nach dem Enterprise von Hand
 * vergeben wird, sobald eine Maschine dasteht. Richtig ist `internal = true`
 * bei eingeschaltetem Verkauf, genau wie beim Testpaket. Das steht hier und
 * nicht in der ersten Migration, damit eine Neuinstallation denselben Weg
 * nimmt wie ein bestehender Betrieb: beide durchlaufen diese Datei.
 *
 * Wiederholbar wie die erste: nichts zu tun, wenn Intern seine neun Werte
 * schon trägt. Auf einer Neuinstallation ist das sofort der Fall, weil die
 * korrigierte Prüfung in `000004` dort in einem Schritt landet — dieser
 * Nachtrag betrifft dann nur noch Enterprise.
 */
return new class extends Migration
{
    /** Dieselben neun Zahlen wie `LADDER['test']` in der ersten Migration. */
    private const TARGET = [
        'quota_gb' => 5, 'disk_gb' => 20, 'ram_mb' => 4096, 'cores' => 2, 'seats' => 3,
        'traffic_gb' => 1000, 'performance' => 'standard', 'price_cents' => 100, 'free_months' => 2,
        'features' => '["managed_updates","daily_backups","monitoring","subdomain","custom_domain"]',
    ];

    public function up(): void
    {
        $now = now();

        DB::transaction(function () use ($now) {
            $this->liftInternalOntoItsSizes($now);
            $this->makeEnterpriseGrantable($now);
        });
    }

    /**
     * Kein Rückweg, aus demselben Grund wie in `000004`: eine veröffentlichte
     * Version darf nicht gelöscht werden, solange Verträge auf sie verweisen
     * könnten.
     */
    public function down(): void {}

    private function liftInternalOntoItsSizes(Carbon $now): void
    {
        $family = DB::table('plan_families')->where('key', 'test')->first();

        if ($family === null) {
            // Keine Familie, die es zu heben gäbe — auf keiner Installation zu
            // erwarten, die `000004` schon durchlaufen hat, aber diese
            // Migration erfindet keine Familie, die eine andere ausgelassen
            // hat.
            return;
        }

        $current = DB::table('plan_versions')
            ->where('plan_family_id', $family->id)
            ->whereNotNull('published_at')
            ->where('available_from', '<=', $now)
            ->where(fn ($q) => $q->whereNull('available_until')->orWhere('available_until', '>', $now))
            ->orderByDesc('version')
            ->first();

        if ($current === null || $this->matchesTarget($current)) {
            return;
        }

        DB::table('plan_versions')->where('id', $current->id)
            ->update(['available_until' => $now, 'updated_at' => $now]);

        $next = (int) DB::table('plan_versions')->where('plan_family_id', $family->id)->max('version') + 1;

        $versionId = DB::table('plan_versions')->insertGetId([
            'uuid' => (string) Str::uuid(),
            'plan_family_id' => $family->id,
            'version' => $next,
            'quota_gb' => self::TARGET['quota_gb'],
            'traffic_gb' => self::TARGET['traffic_gb'],
            'seats' => self::TARGET['seats'],
            'free_months' => self::TARGET['free_months'],
            'ram_mb' => self::TARGET['ram_mb'],
            'cores' => self::TARGET['cores'],
            'disk_gb' => self::TARGET['disk_gb'],
            'performance' => self::TARGET['performance'],
            // Übernommen wie in `000004`: wovon geklont wird, ist keine
            // kaufmännische Entscheidung dieses Nachtrags.
            'template_vmid' => $current->template_vmid ?? 9000,
            'features' => self::TARGET['features'],
            'available_from' => $now,
            'available_until' => null,
            'published_at' => $now,
            'created_at' => $now,
            'updated_at' => $now,
        ]);

        $yearly = self::TARGET['price_cents'] * (12 - self::TARGET['free_months']);
        $currency = DB::table('plan_prices')->where('plan_version_id', $current->id)->value('currency')
            ?? strtoupper((string) config('provisioning.currency', 'EUR'));

        foreach ([['monthly', self::TARGET['price_cents']], ['yearly', $yearly]] as [$term, $amount]) {
            DB::table('plan_prices')->insert([
                'uuid' => (string) Str::uuid(),
                'plan_version_id' => $versionId,
                'term' => $term,
                'amount_cents' => $amount,
                'currency' => $currency,
                'stripe_price_id' => null,
                'created_at' => $now,
                'updated_at' => $now,
            ]);
        }
    }

    /** Trägt die laufende Version bereits alle neun Vorgaben? */
    private function matchesTarget(object $current): bool
    {
        if ((int) $current->quota_gb !== self::TARGET['quota_gb']
            || (int) $current->disk_gb !== self::TARGET['disk_gb']
            || (int) $current->ram_mb !== self::TARGET['ram_mb']
            || (int) $current->cores !== self::TARGET['cores']
            || (int) $current->seats !== self::TARGET['seats']
            || (int) $current->traffic_gb !== self::TARGET['traffic_gb']
            || (string) $current->performance !== self::TARGET['performance']
            || (int) $current->free_months !== self::TARGET['free_months']) {
            return false;
        }

        $monthly = DB::table('plan_prices')
            ->where('plan_version_id', $current->id)
            ->where('term', 'monthly')
            ->value('amount_cents');

        return (int) $monthly === self::TARGET['price_cents'];
    }

    /**
     * Enterprise bleibt im Katalog, wird aber wie das Testpaket von Hand
     * vergeben statt gekauft: `sellable()` lässt es weiterhin aus (Preisblatt,
     * Bestellung, Warenkorb), `grantable()` lässt es herein.
     */
    private function makeEnterpriseGrantable(Carbon $now): void
    {
        DB::table('plan_families')->where('key', 'enterprise')->update([
            'internal' => true,
            'sales_enabled' => true,
            'updated_at' => $now,
        ]);
    }
};
