<?php

namespace App\Models;

use App\Models\Concerns\HasUuid;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Collection;

class MaintenanceWindow extends Model
{
    /** @use HasFactory<\Database\Factories\MaintenanceWindowFactory> */
    use HasFactory, HasUuid;

    /** How far before the start the banner appears (Codex: 72 h). */
    public const DISPLAY_HOURS = 72;

    protected $fillable = [
        'title', 'public_description', 'internal_notes', 'starts_at', 'ends_at',
        'state', 'created_by', 'published_at', 'cancelled_at', 'cancellation_reason',
    ];

    protected function casts(): array
    {
        return [
            'starts_at' => 'datetime',
            'ends_at' => 'datetime',
            'published_at' => 'datetime',
            'cancelled_at' => 'datetime',
        ];
    }

    public function hosts(): BelongsToMany
    {
        return $this->belongsToMany(Host::class);
    }

    public function deliveries(): HasMany
    {
        return $this->hasMany(MaintenanceNotification::class);
    }

    /** Derived lifecycle state — never stored (Codex). */
    public function derivedState(): string
    {
        if ($this->state === 'cancelled') {
            return 'cancelled';
        }
        if ($this->state === 'draft') {
            return 'draft';
        }
        if (now()->lt($this->starts_at)) {
            return 'upcoming';
        }
        if (now()->lte($this->ends_at)) {
            return 'active';
        }

        return 'completed';
    }

    public function isPublished(): bool
    {
        return $this->state === 'scheduled';
    }

    /** Service-bearing customers on the assigned hosts (recomputed live). */
    public function affectedCustomers(): Collection
    {
        $hostIds = $this->hosts()->pluck('hosts.id');
        if ($hostIds->isEmpty()) {
            return collect();
        }

        return Customer::query()
            ->whereHas('instances', fn (Builder $q) => $q
                ->whereIn('host_id', $hostIds)
                ->whereIn('status', ['active', 'provisioning', 'cancellation_scheduled']))
            ->get();
    }

    /**
     * Scheduled windows within the display horizon that affect ONE instance's
     * host — for a per-instance badge, so a customer with several instances
     * never sees another instance's maintenance on this card.
     */
    public static function forInstance(?Instance $instance): Collection
    {
        if ($instance === null || $instance->host_id === null) {
            return collect();
        }

        return self::query()
            ->where('state', 'scheduled')
            ->where('ends_at', '>=', now())
            ->where('starts_at', '<=', now()->addHours(self::DISPLAY_HOURS))
            ->whereHas('hosts', fn (Builder $q) => $q->whereKey($instance->host_id))
            ->orderBy('starts_at')
            ->get();
    }

    /**
     * Scheduled windows to show a given customer right now: within the display
     * horizon (72 h before start until the end) and touching one of the
     * customer's service instances' hosts.
     */
    public static function bannerFor(Customer $customer): Collection
    {
        $hostIds = Instance::query()
            ->where('customer_id', $customer->id)
            ->whereIn('status', ['active', 'provisioning', 'cancellation_scheduled'])
            ->whereNotNull('host_id')
            ->pluck('host_id')->unique();

        if ($hostIds->isEmpty()) {
            return collect();
        }

        return self::query()
            ->where('state', 'scheduled')
            ->where('ends_at', '>=', now())
            ->where('starts_at', '<=', now()->addHours(self::DISPLAY_HOURS))
            ->whereHas('hosts', fn (Builder $q) => $q->whereIn('hosts.id', $hostIds))
            ->orderBy('starts_at')
            ->get();
    }
}
