<?php

namespace App\Models\Sale;

use App\Models\Agency\Agency;
use App\Models\Customer\Customer;
use App\Models\Product\Product;
use App\Models\Product\ProductType;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Sale extends Model
{
     use HasFactory, SoftDeletes;

     protected $table = 'sales';

     protected $fillable = [
          'agency_id',
          'customer_id',
          'product_type_id',
          'product_id',
          'reservable_type',
          'reservable_id',
          'tour_date_type',
          'tour_date_id',
          'created_by',
          'updated_by',
          'assigned_to',
          'confirmed_by',
          'cancelled_by',
          'sale_code',
          'title',
          'start_date',
          'end_date',
          'pickup_time',
          'status',
          'payment_status',
          'adult_count',
          'child_count',
          'infant_count',
          'unit_price',
          'child_price',
          'infant_price',
          'subtotal',
          'discount_amount',
          'discount_rate',
          'discount_reason',
          'tax_amount',
          'total_amount',
          'paid_amount',
          'remaining_amount',
          'currency',
          'pickup_location',
          'dropoff_location',
          'flight_number',
          'flight_info',
          'extra_data',
          'participants',
          'customer_note',
          'internal_note',
          'cancellation_reason',
          'cancelled_at',
          'confirmed_at',
          'source',
          'reference_number',
          'rating',
     ];

     protected $casts = [
          'start_date' => 'date',
          'end_date' => 'date',
          'pickup_time' => 'datetime',
          'cancelled_at' => 'datetime',
          'confirmed_at' => 'datetime',
          'extra_data' => 'array',
          'participants' => 'array',
          'unit_price' => 'decimal:2',
          'child_price' => 'decimal:2',
          'infant_price' => 'decimal:2',
          'subtotal' => 'decimal:2',
          'discount_amount' => 'decimal:2',
          'discount_rate' => 'decimal:2',
          'tax_amount' => 'decimal:2',
          'total_amount' => 'decimal:2',
          'paid_amount' => 'decimal:2',
          'remaining_amount' => 'decimal:2',
     ];

     // ─── İLİŞKİLER ────────────────────────────────────────────────────────────

     public function agency()
     {
          return $this->belongsTo(Agency::class);
     }

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

     public function productType()
     {
          return $this->belongsTo(ProductType::class);
     }

     public function product()
     {
          return $this->belongsTo(Product::class);
     }

     /** Polymorphic: hangi ürün (Tour, DayTour, vb.) */
     public function reservable()
     {
          return $this->morphTo();
     }

     /** Polymorphic: hangi tur tarihi (kalkış) — envanter/operasyon bağı */
     public function tourDate()
     {
          return $this->morphTo(__FUNCTION__, 'tour_date_type', 'tour_date_id');
     }

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

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

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

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

     /** Bu satışa katılan kayıtlı yolcular */
     public function passengers()
     {
          return $this->belongsToMany(\App\Models\CustomerPassenger::class, 'sale_passengers', 'sale_id', 'passenger_id')
               ->withPivot(['room_number', 'seat_number', 'special_request'])
               ->withTimestamps();
     }

     public function items()
     {
          return $this->hasMany(SaleItem::class, 'sale_id');
     }

     public function activities()
     {
          return $this->hasMany(SaleActivity::class, 'sale_id');
     }

     public function customFields()
     {
          return $this->hasMany(SaleFieldValue::class, 'sale_id');
     }

     public function documents()
     {
          return $this->hasMany(SaleDocument::class, 'sale_id')->orderBy('created_at', 'desc');
     }

     // ─── SCOPE'LAR ────────────────────────────────────────────────────────────

     public function scopeByAgency($query, int $agencyId)
     {
          return $query->where('agency_id', $agencyId);
     }

     public function scopeByCustomer($query, int $customerId)
     {
          return $query->where('customer_id', $customerId);
     }

     public function scopeByStatus($query, string $status)
     {
          return $query->where('status', $status);
     }

     public function scopeByPaymentStatus($query, string $status)
     {
          return $query->where('payment_status', $status);
     }

     public function scopeByProductType($query, int $productTypeId)
     {
          return $query->where('product_type_id', $productTypeId);
     }

     public function scopeUpcoming($query)
     {
          return $query->where('start_date', '>=', today())
               ->whereNotIn('status', ['cancelled', 'refunded']);
     }

     public function scopePast($query)
     {
          return $query->where('start_date', '<', today());
     }

     public function scopeActive($query)
     {
          return $query->whereNotIn('status', ['cancelled', 'refunded']);
     }

     public function scopeByStage($query, string $stage)
     {
          return match($stage) {
               'potential' => $query->whereIn('status', ['potential', 'offer_sent', 'revised']),
               'won' => $query->whereIn('status', ['confirmed', 'in_progress', 'completed']),
               'lost' => $query->whereIn('status', ['cancelled', 'refunded']),
               default => $query->where('status', $stage)
          };
     }

     // ─── DURUM YARDIMCILARI ───────────────────────────────────────────────────

     public function isPotential(): bool
     {
          return in_array($this->status, ['potential', 'offer_sent', 'revised']);
     }

     public function isPending(): bool
     {
          return $this->isPotential();
     }
     public function isWon(): bool
     {
          return in_array($this->status, ['confirmed', 'in_progress', 'completed']);
     }
     public function isConfirmed(): bool
     {
          return $this->status === 'confirmed';
     }
     public function isCompleted(): bool
     {
          return $this->status === 'completed';
     }
     public function isCancelled(): bool
     {
          return $this->status === 'cancelled';
     }
     public function isPaid(): bool
     {
          return $this->payment_status === 'paid';
     }

     public function confirm(): void
     {
          $this->update([
               'status' => 'confirmed',
               'confirmed_at' => now(),
               'confirmed_by' => auth()->id(),
          ]);
     }

     public function cancel(string $reason = ''): void
     {
          $this->update([
               'status' => 'cancelled',
               'cancellation_reason' => $reason,
               'cancelled_at' => now(),
               'cancelled_by' => auth()->id(),
          ]);
     }

     public function convertAmount(float $amount, string $fromCurrency, string $toCurrency): float
     {
          if ($fromCurrency === $toCurrency) {
               return $amount;
          }

          $agency = $this->agency;
          if (!$agency) {
               return $amount;
          }

          $currencies = $agency->getActiveCurrencies()->keyBy('code');

          $fromRate = isset($currencies[$fromCurrency]) ? (float)$currencies[$fromCurrency]->exchange_rate : 1.0;
          $toRate = isset($currencies[$toCurrency]) ? (float)$currencies[$toCurrency]->exchange_rate : 1.0;

          $amountInBase = $amount * $fromRate;
          if ($toRate > 0) {
               return $amountInBase / $toRate;
          }

          return $amountInBase;
     }

     // ─── FİYAT HESAPLAMA ──────────────────────────────────────────────────────

      public function calculateTotals(): void
      {
           if ($this->items()->exists()) {
                $subtotal = 0.0;
                $taxAmount = 0.0;
                $total = 0.0;
                $saleCurrency = $this->currency ?: 'TRY';

                foreach ($this->items as $item) {
                    $itemSubtotal = (float)$item->unit_price * (float)$item->quantity;
                    $itemTax = (float)$item->tax_amount;
                    $itemTotal = (float)$item->total_price;
                    $itemCurrency = $item->currency ?: 'TRY';

                    $subtotal += $this->convertAmount($itemSubtotal, $itemCurrency, $saleCurrency);
                    $taxAmount += $this->convertAmount($itemTax, $itemCurrency, $saleCurrency);
                    $total += $this->convertAmount($itemTotal, $itemCurrency, $saleCurrency);
                }

                $discountAmount = $this->discount_rate > 0
                     ? $subtotal * ($this->discount_rate / 100)
                     : (float) ($this->discount_amount ?? 0);

                $total = $total - $discountAmount;
           } else {
                $unitPrice = (float) ($this->unit_price ?? 0);
                $childPrice = (float) ($this->child_price ?? $unitPrice * 0.5);
                $infantPr = (float) ($this->infant_price ?? 0);

                $subtotal = ($unitPrice * $this->adult_count)
                     + ($childPrice * $this->child_count)
                     + ($infantPr * $this->infant_count);

                $discountAmount = $this->discount_rate > 0
                     ? $subtotal * ($this->discount_rate / 100)
                     : (float) ($this->discount_amount ?? 0);

                $taxAmount = (float) ($this->tax_amount ?? 0);
                $total = $subtotal - $discountAmount + $taxAmount;
           }

           $this->update([
                'subtotal' => number_format($subtotal, 2, '.', ''),
                'discount_amount' => number_format($discountAmount, 2, '.', ''),
                'tax_amount' => number_format($taxAmount, 2, '.', ''),
                'total_amount' => number_format(max(0.0, $total), 2, '.', ''),
                'remaining_amount' => number_format(max(0.0, $total - (float) ($this->paid_amount ?? 0)), 2, '.', ''),
           ]);
      }

     // ─── DISPLAY ATTRIBUTE'LAR ────────────────────────────────────────────────

     public function getStatusLabelAttribute(): string
     {
         return self::getStatusLabel($this->status);
     }

     public static function getStatusLabel(string $status): string
     {
         return [
             'potential' => 'Potansiyel',
             'offer_sent' => 'Teklif Verildi',
             'revised' => 'Revize Edildi',
             'confirmed' => 'Satış Tamamlandı',
             'in_progress' => 'Devam Ediyor',
             'completed' => 'Kapanan',
             'cancelled' => 'İptal Edildi',
             'refunded' => 'İade Edildi',
         ][$status] ?? ucfirst($status);
     }

     public function getStatusColorAttribute(): string
     {
          return [
               'potential' => '#64748b',
               'offer_sent' => '#3b82f6',
               'revised' => '#8b5cf6',
               'confirmed' => '#10b981',
               'in_progress' => '#0ea5e9',
               'completed' => '#22c55e',
               'cancelled' => '#ef4444',
               'refunded' => '#f97316',
          ][$this->status] ?? '#94a3b8';
     }

     public function getPaymentStatusLabelAttribute(): string
     {
          return [
               'unpaid' => 'Ödenmedi',
               'partial' => 'Kısmi Ödendi',
               'paid' => 'Ödendi',
               'refunded' => 'İade Edildi',
          ][$this->payment_status] ?? ucfirst($this->payment_status);
     }

     public function getTotalParticipantsAttribute(): int
     {
          return $this->adult_count + $this->child_count + $this->infant_count;
     }

     /**
      * Liste tablolarındaki "Hizmet" hücresi için bağlı tur/kalkış özeti.
      *
      * Satış bir tura (reservable) veya kayıtlı bir kalkışa (tourDate) bağlıysa
      * tur adı + kalkış bilgisini normalize edip döndürür. Hiçbir hizmete bağlı
      * değilse null döner; bu durumda çağıran taraf yalnızca satış başlığını
      * (title) gösterir.
      *
      * N+1'i önlemek için çağıran sorgularda `reservable` ve `tourDate` eager
      * yüklenmelidir.
      *
      * @return array{name: ?string, has_departure: bool, dep_code: ?string, remaining: ?int}|null
      */
     public function getServiceSummaryAttribute(): ?array
     {
          if (!$this->reservable_id && !$this->tour_date_id) {
               return null;
          }

          $departure = $this->tourDate; // kayıtlı kalkış (varsa)

          return [
               // Tur/ürün adı reservable üzerinden; boşsa başlığa düş
               'name' => $this->reservable?->name ?: $this->title,
               // Belirli bir kalkışa (tur tarihine) bağlı mı?
               'has_departure' => $this->tour_date_id !== null,
               // Kalkış kodu (kültür turu tarihlerinde vardır; diğerlerinde null)
               'dep_code' => $departure->code ?? null,
               // Kalan kapasite — model tipine göre farklı kolon adı
               'remaining' => $departure->remaining_capacity ?? $departure->available_seats ?? null,
          ];
     }

     /**
      * Ürün tipine göre form alanlarını döndür (dinamik form sistemi için)
      */
     public static function extraFieldsFor(string $productTypeCode): array
     {
          return match ($productTypeCode) {
               'kultur-turu' => [
                    ['key' => 'room_type', 'label' => 'Oda Tipi', 'type' => 'select', 'options' => ['single' => 'Tek Kişilik', 'double' => 'Çift Kişilik', 'triple' => '3 Kişilik']],
                    ['key' => 'board_type', 'label' => 'Pansiyon', 'type' => 'select', 'options' => ['bb' => 'Oda & Kahvaltı', 'hb' => 'Yarım Pansiyon', 'fb' => 'Tam Pansiyon', 'ai' => 'Her Şey Dahil']],
                    ['key' => 'departure_point', 'label' => 'Kalkış Noktası', 'type' => 'text'],
               ],
               'gunubirlik-tur' => [
                    ['key' => 'departure_point', 'label' => 'Kalkış Noktası', 'type' => 'text'],
                    ['key' => 'return_point', 'label' => 'Dönüş Noktası', 'type' => 'text'],
                    ['key' => 'meal_included', 'label' => 'Yemek Dahil', 'type' => 'boolean'],
               ],
               'transfer' => [
                    ['key' => 'service_type', 'label' => 'Hizmet Tipi', 'type' => 'select', 'options' => ['airport' => 'Havalimanı', 'hotel' => 'Otel', 'port' => 'Liman', 'private' => 'Özel']],
                    ['key' => 'vehicle_type', 'label' => 'Araç Tipi', 'type' => 'select', 'options' => ['sedan' => 'Sedan', 'van' => 'Van', 'minibus' => 'Minibüs', 'bus' => 'Otobüs']],
                    ['key' => 'flight_number', 'label' => 'Uçuş No', 'type' => 'text'],
               ],
               'arac-kiralama', 'rent-a-car' => [
                    ['key' => 'car_class', 'label' => 'Araç Sınıfı', 'type' => 'select', 'options' => ['economy' => 'Ekonomi', 'compact' => 'Compact', 'suv' => 'SUV', 'premium' => 'Premium']],
                    ['key' => 'driver_age', 'label' => 'Sürücü Yaşı', 'type' => 'number'],
                    ['key' => 'extra_driver', 'label' => 'Ek Sürücü', 'type' => 'boolean'],
                    ['key' => 'gps', 'label' => 'GPS', 'type' => 'boolean'],
                    ['key' => 'child_seat', 'label' => 'Çocuk Koltuğu', 'type' => 'boolean'],
                    ['key' => 'full_insurance', 'label' => 'Tam Kasko', 'type' => 'boolean'],
               ],
               'tekne-turu' => [
                    ['key' => 'boat_type', 'label' => 'Tekne Tipi', 'type' => 'select', 'options' => ['yacht' => 'Yat', 'catamaran' => 'Katamaran', 'speedboat' => 'Hız Teknesi', 'gulet' => 'Gulet']],
                    ['key' => 'cabin_count', 'label' => 'Kabin Sayısı', 'type' => 'number'],
                    ['key' => 'meal_plan', 'label' => 'Yemek Planı', 'type' => 'text'],
               ],
               default => [],
          };
     }

     // ─── BOOT ──────────────────────────────────────────────────────────────────

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

          // Kalan tutar = max(0, toplam − ödenen). "saving" hook'u DB'ye yazmadan ÖNCE
          // çalıştığı için hangi yoldan kaydedilirse kaydedilsin (quick-update, calculateTotals,
          // store/update, ödeme kaydı…) kalan tutar toplamla birlikte anında güncellenir.
          static::saving(function (Sale $sale) {
               $sale->remaining_amount = max(0, (float) $sale->total_amount - (float) $sale->paid_amount);
          });

          static::creating(function (Sale $sale) {
               if (empty($sale->sale_code)) {
                    $sale->sale_code = 'SAL-' . strtoupper(substr(uniqid(), -6)) . '-' . date('ymd');
               }
               if (auth()->check()) {
                    $sale->created_by = auth()->id();
                    $sale->agency_id = $sale->agency_id ?? auth()->user()->agency_id;
               }
          });

          static::updated(function (Sale $sale) {
               if (auth()->check()) {
                    $sale->updated_by = auth()->id();
               }
               // remaining_amount artık "saving" hook'unda (kayıttan önce) hesaplanıyor.
          });

          static::created(function (Sale $sale) {
               $sale->updateCustomerStats();

               // Otomatik görev: yeni rezervasyon (anlık taslaklar hariç — onlar
               // gerçek statüye geçtiğinde updated hook'unda yakalanır)
               if ($sale->status !== 'potential') {
                    app(\App\Services\Task\AutoTaskService::class)
                         ->fireSafe('sale_created', $sale, $sale->autoTaskContext(), $sale->autoTaskRelations());
               }
          });

          static::updated(function (Sale $sale) {
               if ($sale->isDirty(['status', 'total_amount', 'customer_id'])) {
                    $sale->updateCustomerStats();

                    if ($sale->isDirty('customer_id')) {
                         $oldCustomerId = $sale->getOriginal('customer_id');
                         if ($oldCustomerId) {
                              $oldCustomer = Customer::find($oldCustomerId);
                              if ($oldCustomer) {
                                   $sale->recalculateCustomerStats($oldCustomer);
                              }
                         }
                    }
               }
          });

          // Otomatik görevler: statü geçişleri (kural açıksa)
          static::updated(function (Sale $sale) {
               if (!$sale->isDirty('status')) {
                    return;
               }

               $autoTasks = app(\App\Services\Task\AutoTaskService::class);
               $old = $sale->getOriginal('status');
               $new = $sale->status;

               // Taslak gerçek bir rezervasyona dönüştü → "yeni rezervasyon"
               // (doğrudan onaya/iptale geçişte sale_confirmed/cancelled yeterli)
               if ($old === 'potential' && !in_array($new, ['confirmed', 'cancelled', 'refunded'], true)) {
                    $autoTasks->fireSafe('sale_created', $sale, $sale->autoTaskContext(), $sale->autoTaskRelations());
               }
               if ($new === 'confirmed') {
                    $autoTasks->fireSafe('sale_confirmed', $sale, $sale->autoTaskContext(), $sale->autoTaskRelations());
               }
               if ($new === 'cancelled') {
                    $autoTasks->fireSafe('sale_cancelled', $sale, $sale->autoTaskContext(), $sale->autoTaskRelations());
               }
          });

          static::deleted(function (Sale $sale) {
               $sale->updateCustomerStats();
          });
     }

     /** Otomatik görev başlık şablonları için satış bağlamı */
     public function autoTaskContext(): array
     {
          return [
               'agency_id' => $this->agency_id,
               'sale_code' => $this->sale_code,
               'customer' => $this->customer?->full_name ?? 'Müşteri',
          ];
     }

     /** Otomatik görevin ek ilişkileri: müşteri + bağlı kalkış (kural açıkken çözülür) */
     public function autoTaskRelations(): \Closure
     {
          return fn() => array_filter([
               $this->customer,
               app(\App\Services\Operation\DepartureInventoryService::class)
                    ->resolve($this->tour_date_type, $this->tour_date_id),
          ]);
     }

     public function updateCustomerStats(): void
     {
          $customer = $this->customer;
          if (!$customer)
               return;

          $stats = self::where('customer_id', $customer->id)
               ->whereNotIn('status', ['cancelled', 'refunded'])
               ->selectRaw('COUNT(*) as total_count, SUM(total_amount) as total_spent, MIN(start_date) as first_date, MAX(start_date) as last_date')
               ->first();

          $totalBookings = (int) ($stats->total_count ?? 0);
          $totalSpent = (float) ($stats->total_spent ?? 0);
          $avgValue = $totalBookings > 0 ? $totalSpent / $totalBookings : 0;

          $loyaltyPoints = (int) floor($totalSpent / 100) * 10;

          $customer->update([
               'total_bookings' => $totalBookings,
               'total_spent' => $totalSpent,
               'average_booking_value' => $avgValue,
               'lifetime_value' => $totalSpent,
               'first_booking_date' => $stats->first_date,
               'last_booking_date' => $stats->last_date,
               'loyalty_points' => $loyaltyPoints,
          ]);
     }

     protected function recalculateCustomerStats(Customer $customer): void
     {
          $stats = self::where('customer_id', $customer->id)
               ->whereNotIn('status', ['cancelled', 'refunded'])
               ->selectRaw('COUNT(*) as total_count, SUM(total_amount) as total_spent, MIN(start_date) as first_date, MAX(start_date) as last_date')
               ->first();

          $totalBookings = (int) ($stats->total_count ?? 0);
          $totalSpent = (float) ($stats->total_spent ?? 0);

          $customer->update([
               'total_bookings' => $totalBookings,
               'total_spent' => $totalSpent,
               'average_booking_value' => $totalBookings > 0 ? $totalSpent / $totalBookings : 0,
               'lifetime_value' => $totalSpent,
               'first_booking_date' => $stats->first_date,
               'last_booking_date' => $stats->last_date,
          ]);
     }
}
