<?php

namespace App\Models;

use App\Observers\TripObserver;
use App\Services\CouponService;
use App\Traits\HasFallbackData;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Trip extends Model
{
    use HasFactory, HasFallbackData;

    protected static function boot()
    {
        parent::boot();
        Trip::observe(TripObserver::class);
    }

    protected $guarded = ['id', 'created_at', 'updated_at', 'deleted_at'];

    protected $casts   = [
        'status_times' => 'json',
        'actual_path'  => 'json',
        'scooter_data' => 'json',
    ];

    public function getImageAttribute()
    {
        $media = $this->media()->where('option', 'image')->first();
        $image = $media ? asset('storage/images/trips/' . $media->media) : null;
        return $media ? ['id' => (int) $media->id, 'media' => $image] : null;
    }

    public function getImageShowAttribute()
    {
        $media = $this->media()->where('option', 'image')->first();
        $image = $media ? asset('storage/images/trips/' . $media->media) : null;
        return $image;
    }

    public function getStatusTimesDataAttribute()
    {
        return $this->attributes['status_times'] ? json_decode($this->attributes['status_times'], true) : [];
    }

    public function setStatusTimesAttribute($arr_value)
    {
        if (isset($this->attributes['status_times']) && $this->attributes['status_times']) {
            $statuses = json_decode($this->attributes['status_times']);
            foreach ($arr_value as $key => $value) {
                data_set($statuses, $key, $value);
            }
            $this->attributes['status_times'] = json_encode($statuses);
        } else {
            $this->attributes['status_times'] = json_encode($arr_value);
        }
    }

    public function getStartLocationAttribute()
    {
        return $this->first_lat && $this->first_lng ? [
            'lat'      => (float) $this->first_lat,
            'lng'      => (float) $this->first_lng,
            'location' => $this->first_location,
        ] : null;
    }

    public function getEndLocationAttribute()
    {
        return $this->end_lat && $this->end_lng ? [
            'lat'      => (float) $this->end_lat,
            'lng'      => (float) $this->end_lng,
            'location' => $this->attributes['end_location'],
        ] : null;
    }

    public function getStartTimeAttribute()
    {
        return isset($this->status_times['started']) ? Carbon::parse($this->status_times['started'])->timestamp : null;
    }

    public function getEndTimeAttribute()
    {
        return isset($this->status_times['ended']) ? Carbon::parse($this->status_times['ended'])->timestamp : null;
    }

    public function getTimeAttribute()
    {
        $start_time = $this->getParsedTime('started');
        $end_time   = $this->getParsedTime('ended');
        return $end_time->diffInMinutes($start_time);
    }

    private function getParsedTime($status)
    {
        return isset($this->status_times[$status]) ? Carbon::parse($this->status_times[$status]) : now();
    }

    public function getWaitingTimeToStartAttribute()
    {
        if ($this->status !== 'pending') {
            return 0;
        }
        $waitingTimeToStart = setting('waiting_time_to_start') ?? 10;
        return max(0, $waitingTimeToStart - now()->diffInMinutes($this->created_at));
    }

    public function getPriceAttribute($value)
    {
        switch ($this->attributes['status']) {
            case 'started':
                $amount = $this->calculateBaseAmount();
                return $this->applyCouponIfAvailable($amount);
                
            case 'pending':
                return $this->calculateMaxPossibleDuration();
                
            default:
                return (float) $value;
        }
    }

    private function calculateMaxPossibleDuration(): float
    {
        $balance        = $this->user?->wallet?->balance ?? 0;
        $baseFare       = $this->attributes['base_fare'] ?? 0;
        $pricePerMinute = $this->attributes['price_per_minute'] ?? 1;

        // Prevent division by zero
        if ($pricePerMinute <= 0) {
            return 0;
        }

        // If there's a coupon, calculate how it affects the base fare and price per minute
        if (isset($this->attributes['coupon_id']) && $this->coupon()->exists()) {
            // Apply coupon to base fare
            [$discountedBaseFare]  = CouponService::applyCoupon($this->attributes['coupon_id'], $baseFare);
            // Apply coupon to a sample minute to get discounted rate
            [$discountedOneMinute] = CouponService::applyCoupon($this->attributes['coupon_id'], $pricePerMinute);
            
            $baseFare = $discountedBaseFare;
            $pricePerMinute = $discountedOneMinute;
        }

        return max(0, ($balance - $baseFare) / $pricePerMinute);
    }

    private function calculateBaseAmount()
    {
        $time = $this->time;
        return $this->attributes['base_fare'] + $time * $this->attributes['price_per_minute'];
    }

    private function applyCouponIfAvailable($amount)
    {
        if (isset($this->attributes['coupon_id']) && $this->coupon()->exists()) {
            [$amount] = CouponService::applyCoupon($this->attributes['coupon_id'], $amount);
        }
        return $amount;
    }

    public function user()
    {
        return $this->belongsTo(User::class, 'user_id');
    }

    public function scooter()
    {
        return $this->belongsTo(Scooter::class, 'scooter_id');
    }

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

    public function startArea()
    {
        return $this->belongsTo(Area::class, 'start_area_id');
    }

    public function endArea()
    {
        return $this->belongsTo(Area::class, 'end_area_id');
    }

    public function media()
    {
        return $this->morphOne(AppMedia::class, 'app_mediaable');
    }
}
