<?php

namespace App\Models;

use App\Enums\BookingStatus;
use App\Models\Concerns\BelongsToCompany;
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\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;

class Booking extends Model
{
    use HasFactory, SoftDeletes, BelongsToCompany, HasUuid;

    protected $fillable = [
        'uuid', 'code',
        'company_id', 'branch_id',
        'customer_id', 'service_id', 'employee_id',
        'status', 'source',
        'booking_date', 'start_time', 'end_time', 'scheduled_at', 'duration_minutes',
        'service_price', 'discount_amount', 'total_amount', 'paid_amount', 'currency',
        'coupon_id', 'promotion_id',
        'customer_notes', 'internal_notes',
        'cancellation_reason', 'cancelled_at', 'cancelled_by',
        'reminder_sent_at', 'confirmed_at', 'completed_at',
        'rescheduled_from_id', 'rescheduled_to_id',
    ];

    protected function casts(): array
    {
        return [
            'status'            => BookingStatus::class,
            'booking_date'      => 'date',
            'scheduled_at'      => 'datetime',
            'service_price'     => 'decimal:2',
            'discount_amount'   => 'decimal:2',
            'total_amount'      => 'decimal:2',
            'paid_amount'       => 'decimal:2',
            'cancelled_at'      => 'datetime',
            'reminder_sent_at'  => 'datetime',
            'confirmed_at'      => 'datetime',
            'completed_at'      => 'datetime',
        ];
    }

    /* ------------------------------------------------------------------
     | Relaciones
     |------------------------------------------------------------------*/

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

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

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

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

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

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

    public function statusHistory(): HasMany
    {
        return $this->hasMany(BookingStatusHistory::class)->orderBy('created_at');
    }

    public function cancelledBy(): BelongsTo
    {
        return $this->belongsTo(User::class, 'cancelled_by');
    }

    public function rescheduledFrom(): BelongsTo
    {
        return $this->belongsTo(self::class, 'rescheduled_from_id');
    }

    public function rescheduledTo(): BelongsTo
    {
        return $this->belongsTo(self::class, 'rescheduled_to_id');
    }

    /* ------------------------------------------------------------------
     | Scopes
     |------------------------------------------------------------------*/

    public function scopePending(Builder $q): Builder
    {
        return $q->where('status', 'pending');
    }

    public function scopeConfirmed(Builder $q): Builder
    {
        return $q->where('status', 'confirmed');
    }

    public function scopeActive(Builder $q): Builder
    {
        return $q->whereIn('status', ['pending', 'confirmed']);
    }

    public function scopeForEmployee(Builder $q, int $id): Builder
    {
        return $q->where('employee_id', $id);
    }

    public function scopeForDate(Builder $q, $date): Builder
    {
        return $q->whereDate('booking_date', $date);
    }

    public function scopeBetween(Builder $q, $from, $to): Builder
    {
        return $q->whereBetween('scheduled_at', [$from, $to]);
    }

    public function scopeOverlapping(Builder $q, $start, $end, ?int $employeeId = null): Builder
    {
        $q->where(function ($q2) use ($start, $end) {
            $q2->where('scheduled_at', '<', $end)
               ->whereRaw('DATE_ADD(scheduled_at, INTERVAL duration_minutes MINUTE) > ?', [$start]);
        })->whereIn('status', ['pending', 'confirmed']);

        if ($employeeId) {
            $q->where('employee_id', $employeeId);
        }

        return $q;
    }

    /* ------------------------------------------------------------------
     | Helpers
     |------------------------------------------------------------------*/

    public function isCancellable(): bool
    {
        return ! $this->status->isFinal();
    }

    public function endsAt(): \Carbon\Carbon
    {
        return $this->scheduled_at->copy()->addMinutes($this->duration_minutes);
    }

    public function isPaid(): bool
    {
        return $this->paid_amount >= $this->total_amount && $this->total_amount > 0;
    }

    public function resolveRouteBinding($value, $field = null): ?self
    {
        return $this->where($field ?? 'uuid', $value)
            ->when(is_numeric($value) && blank($field), fn ($query) => $query->orWhere($this->getKeyName(), $value))
            ->first();
    }
}
