<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class EventPackage extends Model
{
    use HasFactory;

    protected $table = 'event_packages';

    protected $fillable = [
        'event_id',
        'package_id',
        'purchased_price',
        'purchased_at',
        'used_photos',
        'used_guests',
        'gallery_expires_at',
        'limits_snapshot',
        'extra_photos',
        'extra_guests',
        'extra_gallery_days',
    ];

    protected $casts = [
        'purchased_price' => 'decimal:2',
        'purchased_at' => 'datetime',
        'gallery_expires_at' => 'datetime',
        'gallery_warning_sent_at' => 'datetime',
        'gallery_expired_notified_at' => 'datetime',
        'used_photos' => 'integer',
        'used_guests' => 'integer',
        'extra_photos' => 'integer',
        'extra_guests' => 'integer',
        'extra_gallery_days' => 'integer',
        'limits_snapshot' => 'array',
    ];

    public function event(): BelongsTo
    {
        return $this->belongsTo(Event::class);
    }

    public function package(): BelongsTo
    {
        return $this->belongsTo(Package::class)->withTrashed();
    }

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

    public function isActive(): bool
    {
        return $this->gallery_expires_at && $this->gallery_expires_at->isFuture();
    }

    public function canUploadPhoto(): bool
    {
        if (! $this->isActive()) {
            return false;
        }

        $maxPhotos = $this->effectiveLimits()['max_photos'];

        if ($maxPhotos === null) {
            return true;
        }

        return $this->used_photos < $maxPhotos;
    }

    public function canAddGuest(): bool
    {
        if (! $this->isActive()) {
            return false;
        }

        $maxGuests = $this->effectiveLimits()['max_guests'];

        if ($maxGuests === null) {
            return true;
        }

        return $this->used_guests < $maxGuests;
    }

    public function getRemainingPhotosAttribute(): int
    {
        $limit = $this->effectiveLimits()['max_photos'] ?? 0;

        return max(0, (int) $limit - $this->used_photos);
    }

    public function getRemainingGuestsAttribute(): int
    {
        $limit = $this->effectiveLimits()['max_guests'] ?? 0;

        return max(0, (int) $limit - $this->used_guests);
    }

    /**
     * @return array{max_photos: ?int, max_guests: ?int, gallery_days: ?int, max_tasks: ?int, max_events_per_year: ?int}
     */
    public function effectiveLimits(): array
    {
        $snapshot = is_array($this->limits_snapshot) ? $this->limits_snapshot : [];

        $base = [
            'max_photos' => array_key_exists('max_photos', $snapshot)
                ? $snapshot['max_photos']
                : ($this->package->max_photos ?? null),
            'max_guests' => array_key_exists('max_guests', $snapshot)
                ? $snapshot['max_guests']
                : ($this->package->max_guests ?? null),
            'gallery_days' => array_key_exists('gallery_days', $snapshot)
                ? $snapshot['gallery_days']
                : ($this->package->gallery_days ?? null),
            'max_tasks' => array_key_exists('max_tasks', $snapshot)
                ? $snapshot['max_tasks']
                : ($this->package->max_tasks ?? null),
            'max_events_per_year' => array_key_exists('max_events_per_year', $snapshot)
                ? $snapshot['max_events_per_year']
                : ($this->package->max_events_per_year ?? null),
        ];

        $applyExtra = static function (?int $limit, int $extra): ?int {
            if ($limit === null) {
                return null;
            }

            $safeExtra = max(0, $extra);

            return max(0, $limit + $safeExtra);
        };

        $maxPhotos = $applyExtra($this->normalizeLimit($base['max_photos']), (int) ($this->extra_photos ?? 0));
        $maxGuests = $applyExtra($this->normalizeLimit($base['max_guests']), (int) ($this->extra_guests ?? 0));

        return [
            'max_photos' => $maxPhotos,
            'max_guests' => $maxGuests,
            'gallery_days' => $this->normalizeLimit($base['gallery_days']),
            'max_tasks' => $this->normalizeLimit($base['max_tasks']),
            'max_events_per_year' => $this->normalizeLimit($base['max_events_per_year']),
        ];
    }

    public function effectivePhotoLimit(): ?int
    {
        return $this->effectiveLimits()['max_photos'];
    }

    public function effectiveGuestLimit(): ?int
    {
        return $this->effectiveLimits()['max_guests'];
    }

    protected static function boot()
    {
        parent::boot();

        static::creating(function ($eventPackage) {
            if (! $eventPackage->purchased_at) {
                $eventPackage->purchased_at = now();
            }
            if (! $eventPackage->gallery_expires_at && $eventPackage->package) {
                $days = $eventPackage->package->gallery_days ?? 30;
                $eventPackage->gallery_expires_at = now()->addDays($days);
            }

            if (! $eventPackage->limits_snapshot) {
                $package = $eventPackage->relationLoaded('package')
                    ? $eventPackage->package
                    : Package::query()->find($eventPackage->package_id);

                if ($package) {
                    $eventPackage->limits_snapshot = array_filter([
                        'max_photos' => $package->max_photos,
                        'max_guests' => $package->max_guests,
                        'gallery_days' => $package->gallery_days,
                        'max_tasks' => $package->max_tasks,
                        'max_events_per_year' => $package->max_events_per_year,
                    ], static fn ($value) => $value !== null);
                }
            }
        });
    }

    private function normalizeLimit($value): ?int
    {
        if ($value === null) {
            return null;
        }

        return is_numeric($value) ? (int) $value : null;
    }
}
