<?php

use App\Models\Host;
use App\Models\Instance;
use App\Models\Order;
use App\Models\ProvisioningRun;
use Illuminate\Database\QueryException;

it('resolves an Order as the polymorphic run subject', function () {
    $order = Order::factory()->create();
    $run = ProvisioningRun::factory()->create([
        'subject_type' => Order::class,
        'subject_id' => $order->id,
        'pipeline' => 'customer',
    ]);

    expect($run->fresh()->subject->is($order))->toBeTrue()
        ->and($order->getRouteKeyName())->toBe('uuid');
});

it('marks the order and releases its instance via the ProvisioningSubject hook', function () {
    $order = Order::factory()->create(['status' => 'provisioning']);
    $instance = Instance::factory()->create([
        'order_id' => $order->id, 'customer_id' => $order->customer_id, 'status' => 'provisioning',
    ]);

    $order->onProvisioningFailed();

    expect($order->fresh()->status)->toBe('failed')
        ->and($instance->fresh()->status)->toBe('failed'); // quota no longer counts against capacity
});

it('enforces a unique stripe_event_id (idempotency)', function () {
    Order::factory()->create(['stripe_event_id' => 'evt_123']);

    Order::factory()->create(['stripe_event_id' => 'evt_123']);
})->throws(QueryException::class);

it('enforces a unique subdomain', function () {
    Instance::factory()->create(['subdomain' => 'kanzlei-berger']);

    Instance::factory()->create(['subdomain' => 'kanzlei-berger']);
})->throws(QueryException::class);

it('encrypts nc_admin_ref at rest', function () {
    $instance = Instance::factory()->create(['nc_admin_ref' => 'admin']);

    $raw = DB::table('instances')->where('id', $instance->id)->value('nc_admin_ref');
    expect($raw)->not->toBe('admin')
        ->and($instance->fresh()->nc_admin_ref)->toBe('admin');
});

it('computes host capacity from committed instance quotas', function () {
    $host = Host::factory()->active()->create(['total_gb' => 1000, 'reserve_pct' => 15]);
    // freeGb = 850
    Instance::factory()->create(['host_id' => $host->id, 'disk_gb' => 120, 'status' => 'active']);
    Instance::factory()->create(['host_id' => $host->id, 'disk_gb' => 220, 'status' => 'provisioning']);
    Instance::factory()->create(['host_id' => $host->id, 'disk_gb' => 500, 'status' => 'failed', 'vmid' => null]); // released (no VM)
    Instance::factory()->create(['host_id' => $host->id, 'disk_gb' => 160, 'status' => 'failed', 'vmid' => 900]); // VM still exists → counts

    expect($host->committedGb())->toBe(500)      // 120 + 220 + 160 (disk allocations)
        ->and($host->availableGb())->toBe(350);  // 850 - 500
});

it('places an instance on an active host in the datacenter with room', function () {
    $full = Host::factory()->active()->create(['datacenter' => 'fsn', 'name' => 'pve-fsn-a', 'total_gb' => 100, 'reserve_pct' => 15]);
    Instance::factory()->create(['host_id' => $full->id, 'disk_gb' => 80, 'status' => 'active']); // free 85, committed 80 → 5 left
    $room = Host::factory()->active()->create(['datacenter' => 'fsn', 'name' => 'pve-fsn-b', 'total_gb' => 1000, 'reserve_pct' => 15]);
    Host::factory()->active()->create(['datacenter' => 'hel', 'name' => 'pve-hel-a', 'total_gb' => 1000]);

    $picked = Host::placeableIn('fsn', 100);

    expect($picked?->is($room))->toBeTrue()
        ->and(Host::placeableIn('nowhere', 100))->toBeNull();
});
