<?php

declare(strict_types=1);

namespace Maestro\Core\Modules\Offers\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Maestro\Core\Modules\Customers\Models\Contact;
use Maestro\Core\Modules\Customers\Models\Customer;
use Maestro\Core\Modules\HourRegistration\Models\HourProject;
use Maestro\Core\Modules\Offers\Enums\OfferDiscountType;
use Maestro\Core\Modules\Offers\Enums\OfferStatus;
use Maestro\Core\Support\AuthUserResolver;

class Offer extends Model
{
    /**
     * @var list<string>
     */
    protected $fillable = [
        'offer_number',
        'customer_id',
        'contact_id',
        'subject',
        'intro_html',
        'document_background_url',
        'valid_until',
        'discount_type',
        'discount_value',
        'vat_total',
        'status',
        'public_uuid',
        'sent_at',
        'viewed_at',
        'signed_at',
        'declined_at',
        'decline_reason',
        'signer_name',
        'signature_path',
        'is_template',
        'template_name',
        'hour_project_id',
        'created_by',
        'updated_by',
    ];

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'valid_until' => 'date',
            'discount_type' => OfferDiscountType::class,
            'status' => OfferStatus::class,
            'sent_at' => 'datetime',
            'viewed_at' => 'datetime',
            'signed_at' => 'datetime',
            'declined_at' => 'datetime',
            'is_template' => 'boolean',
        ];
    }

    /**
     * @return BelongsTo<Customer, Offer>
     */
    public function customer(): BelongsTo
    {
        return $this->belongsTo(Customer::class);
    }

    /**
     * @return BelongsTo<Contact, Offer>
     */
    public function contactPerson(): BelongsTo
    {
        return $this->belongsTo(Contact::class, 'contact_id');
    }

    /**
     * All sections (roots and nested pricing categories).
     *
     * @return HasMany<OfferSection, Offer>
     */
    public function sections(): HasMany
    {
        return $this->hasMany(OfferSection::class)->orderBy('sort_order');
    }

    /**
     * Top-level document sections only (chapters + pricing root).
     *
     * @return HasMany<OfferSection, Offer>
     */
    public function rootSections(): HasMany
    {
        return $this->hasMany(OfferSection::class)
            ->whereNull('parent_section_id')
            ->orderBy('sort_order');
    }

    /**
     * @return BelongsTo<HourProject, Offer>
     */
    public function hourProject(): BelongsTo
    {
        return $this->belongsTo(HourProject::class, 'hour_project_id');
    }

    /** @return BelongsTo<Model, Offer> */
    public function createdBy(): BelongsTo
    {
        return $this->belongsTo(AuthUserResolver::eloquentModelClass(), 'created_by');
    }

    /** @return BelongsTo<Model, Offer> */
    public function updatedBy(): BelongsTo
    {
        return $this->belongsTo(AuthUserResolver::eloquentModelClass(), 'updated_by');
    }

    /** Idempotent expiry when validity date passed (sent/viewed only). */
    public function syncExpiryIfNeeded(): void
    {
        if (! in_array($this->status, [OfferStatus::Sent, OfferStatus::Viewed], true)) {
            return;
        }

        if ($this->valid_until === null) {
            return;
        }

        $today = now()->startOfDay();
        if ($today->gt($this->valid_until->startOfDay())) {
            $this->status = OfferStatus::Expired;
            $this->saveQuietly();
        }
    }

    public function markPublicViewed(): void
    {
        if ($this->status === OfferStatus::Sent) {
            $this->status = OfferStatus::Viewed;
            $this->viewed_at ??= now();
            $this->saveQuietly();
        }
    }
}
