<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;

class Invoice extends Model
{
    use HasFactory;

    protected $fillable = [
        'travel_agent_id', 
        'customer_id', 
        'trip_code', // Add this
        'user_id',   // Add this         
        'type', 
        'package_name', 
        'duration_days', 
        'no_of_pax', 
        'tour_start_day', 
        'tax_percentage', 
        'margin_percentage',   
        'billed_amount',      
        'margin_amount',      
        'tax_amount',         
        'discount_amount',
        'final_price', 
        'status',
        // Cancellation fields
        'cancellation_reason',
        'cancelled_at',
        'refund_amount',
        'refund_status',
        'refund_notes',
        // Payment fields
        'total_paid_amount',
        'remaining_amount',
        'refundable_amount',
        'payment_status'
    ];

    protected $casts = [
        'tour_start_day'      => 'date:Y-m-d',
        'cancelled_at'        => 'datetime',
        'tax_percentage'      => 'float',
        'margin_percentage'   => 'float',
        'billed_amount'       => 'float',
        'margin_amount'       => 'float',
        'tax_amount'          => 'float',
        'discount_amount'     => 'float',
        'final_price'         => 'float',
        'refund_amount'       => 'float',
        'total_paid_amount'   => 'float',
        'remaining_amount'    => 'float',
        'refundable_amount'   => 'float',
    ];

    /* -------------------------
     | Relationships
     ------------------------- */
    public function travelAgent()
    {
        return $this->belongsTo(TravelAgent::class);
    }

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

    public function days()
    {
        return $this->hasMany(InvoiceDay::class)->with([
            'services.service', 
            'hotels.hotel', 
            'extraServices.extraService',
            'dayDescriptions'
        ]);
    }

    // NEW: Relationship specifically for seasonal data
    public function daysWithSeasonal()
    {
        return $this->hasMany(InvoiceDay::class)
            ->select('id', 'invoice_id', 'day_number', 'day_date', 
                    'seasonal_hike_percentage', 'seasonal_sessions')
            ->with([
                'services.service', 
                'hotels.hotel', 
                'extraServices.extraService',
                'dayDescriptions'
            ]);
    }

    public function tripPayments()
    {
        return $this->hasMany(TripPayment::class);
    }

    public function successfulTripPayments()
    {
        return $this->tripPayments()->successfulPayments();
    }

    public function refunds()
    {
        return $this->tripPayments()->refunds();
    }

    public function advancePayments()
    {
        return $this->tripPayments()->advances();
    }

    public function installmentPayments()
    {
        return $this->tripPayments()->installments();
    }

    /* -------------------------
     | Payment Methods
     ------------------------- */
    public function calculateTotalPaid(): float
    {
        return (float) $this->successfulTripPayments()->sum('amount');
    }

    public function calculateRemainingAmount(): float
    {
        return max(0, $this->final_price - $this->calculateTotalPaid());
    }

    private function getPaymentStatus($totalPaid)
    {
        if ($totalPaid <= 0) {
            return 'Unpaid'; // ✅ Should be Unpaid when no payments
        } elseif ($totalPaid < $this->final_price) {
            return 'Partially Paid';
        } else {
            return 'Fully Paid';
        }
    }

    public function updatePaymentSummary()
    {
        $totalPaid = $this->calculateTotalPaid();
        $remaining = $this->calculateRemainingAmount();
        
        // ✅ Ensure remaining amount is calculated correctly
        $remaining = max(0, $this->final_price - $totalPaid);
        
        $this->update([
            'total_paid_amount' => $totalPaid,
            'remaining_amount' => $remaining,
            'payment_status' => $this->getPaymentStatus($totalPaid),
        ]);
    }

    /* -------------------------
     | Refund Logic
     ------------------------- */
    public function calculateRefundAmount(): float
    {
        $totalPaid    = $this->calculateTotalPaid();
        if ($totalPaid <= 0) return 0;

        $daysUntilTour = now()->diffInDays($this->tour_start_day, false);

        return match (true) {
            $daysUntilTour < 0  => max(0, $totalPaid * 0.1),   // Tour started: 10%
            $daysUntilTour <= 2 => $totalPaid * 0.5,           // Within 48h: 50%
            $daysUntilTour <= 7 => $totalPaid * 0.75,          // Within 7d: 75%
            default             => $totalPaid * 0.9,           // >7d: 90%
        };
    }

    public function getRefundPolicyDescription(): string
    {
        $totalPaid = $this->calculateTotalPaid();
        $daysUntilTour = now()->diffInDays($this->tour_start_day, false);

        if ($totalPaid <= 0) {
            return "No payments made. No refund applicable.";
        }

        return match (true) {
            $daysUntilTour < 0  => "Tour already started. Max 10% refund possible.",
            $daysUntilTour <= 2 => "Cancelled within 48h of tour: 50% refund.",
            $daysUntilTour <= 7 => "Cancelled within 7 days: 75% refund.",
            default             => "Cancelled >7 days before: 90% refund.",
        };
    }

    /* -------------------------
     | NEW: Seasonal Pricing Methods
     ------------------------- */
    
    /**
     * Calculate total seasonal revenue impact for this invoice
     */
    public function calculateSeasonalRevenueImpact(): float
    {
        $baseAmount = 0;
        $seasonalAmount = 0;
        
        foreach ($this->days as $day) {
            $dayBaseAmount = 0;
            
            // Calculate base amount for the day (without seasonal)
            foreach ($day->services as $service) {
                $dayBaseAmount += ($service->unit_price ?? 0) * ($service->qty ?? 1);
            }
            foreach ($day->hotels as $hotel) {
                $dayBaseAmount += ($hotel->price ?? 0) * ($hotel->qty ?? 1);
            }
            foreach ($day->extraServices as $extra) {
                $dayBaseAmount += ($extra->price ?? 0) * ($extra->quantity ?? 1);
            }
            
            $baseAmount += $dayBaseAmount;
            $seasonalAmount += $dayBaseAmount * ($day->seasonal_hike_percentage / 100);
        }
        
        return round($seasonalAmount, 2);
    }

    /**
     * Get seasonal breakdown summary
     */
    public function getSeasonalBreakdown(): array
    {
        $daysWithSeasonal = $this->days->filter(function($day) {
            return $day->seasonal_hike_percentage > 0;
        });

        $sessionUsage = [];
        $totalSeasonalImpact = 0;

        foreach ($daysWithSeasonal as $day) {
            $totalSeasonalImpact += $day->seasonal_hike_percentage;
            
            foreach ($day->seasonal_sessions ?? [] as $session) {
                $sessionName = $session['name'] ?? 'Unknown';
                if (!isset($sessionUsage[$sessionName])) {
                    $sessionUsage[$sessionName] = [
                        'count' => 0,
                        'total_percentage' => 0,
                        'type' => $session['type'] ?? 'regular'
                    ];
                }
                $sessionUsage[$sessionName]['count']++;
                $sessionUsage[$sessionName]['total_percentage'] += $session['percentage'] ?? 0;
            }
        }

        return [
            'total_days' => $this->days->count(),
            'days_with_seasonal' => $daysWithSeasonal->count(),
            'total_seasonal_impact_percentage' => $totalSeasonalImpact,
            'seasonal_revenue_impact' => $this->calculateSeasonalRevenueImpact(),
            'session_breakdown' => $sessionUsage,
            'average_daily_hike' => $daysWithSeasonal->count() > 0 ? 
                $totalSeasonalImpact / $daysWithSeasonal->count() : 0
        ];
    }

    /**
     * Check if invoice has seasonal pricing applied
     */
    public function hasSeasonalPricing(): bool
    {
        return $this->days->contains(function($day) {
            return $day->seasonal_hike_percentage > 0;
        });
    }

    /* -------------------------
     | Summary Helpers
     ------------------------- */
    public function getPaymentSummary(): array
    {
        $totalPaid = $this->calculateTotalPaid();

        return [
            'final_price'        => (float) $this->final_price,
            'total_paid'         => $totalPaid,
            'remaining'          => $this->calculateRemainingAmount(),
            'payment_status'     => $this->getPaymentStatus($totalPaid),
            'can_cancel'         => $this->canBeCancelled(),
            'advance_payments'   => (float) $this->advancePayments()->sum('amount'),
            'installment_payments' => (float) $this->installmentPayments()->sum('amount'),
            // NEW: Seasonal data in payment summary
            'has_seasonal_pricing' => $this->hasSeasonalPricing(),
            'seasonal_breakdown' => $this->getSeasonalBreakdown(),
        ];
    }

    /* -------------------------
     | Cancellation
     ------------------------- */
    public function canBeCancelled(): bool
    {
        return !$this->isCancelled() && now()->lte($this->tour_start_day);
    }

    public function isCancelled(): bool
    {
        return $this->status === 'Cancelled';
    }

    public function hasPayments(): bool
    {
        return $this->successfulTripPayments()->exists();
    }

    /* -------------------------
     | History & Totals
     ------------------------- */
    public function getPaymentHistory()
    {
        return $this->tripPayments()
            ->orderBy('payment_date', 'desc')
            ->orderBy('created_at', 'desc')
            ->get();
    }

    public function getTotalAmountAttribute(): float
    {
        $total = 0;
        foreach ($this->days as $day) {
            foreach ($day->services as $service) {
                $total += ($service->unit_price ?? 0) * ($service->qty ?? 1);
            }
            foreach ($day->hotels as $hotel) {
                $total += ($hotel->price ?? 0) * ($hotel->rooms ?? 1);
            }
            foreach ($day->extraServices as $extra) {
                $total += ($extra->price ?? 0) * ($extra->quantity ?? 1);
            }
        }

        return round(
            $total + ($this->margin_amount ?? 0) 
                   + ($this->tax_amount ?? 0) 
                   - ($this->discount_amount ?? 0),
            2
        );
    }

    /* -------------------------
     | Scopes
     ------------------------- */
    public function scopePending($q)       { return $q->where('status', 'Pending'); }
    public function scopeFinalized($q)     { return $q->where('status', 'Finalized'); }
    public function scopeCancelled($q)     { return $q->where('status', 'Cancelled'); }
    public function scopeQuotations($q)    { return $q->where('type', 'Quotation'); }
    public function scopeBillings($q)      { return $q->where('type', 'Billing'); }
    public function scopeUnpaid($q)        { return $q->where('payment_status', 'Unpaid'); }
    public function scopePartiallyPaid($q) { return $q->where('payment_status', 'Partially Paid'); }
    public function scopeFullyPaid($q)     { return $q->where('payment_status', 'Fully Paid'); }

    // NEW: Seasonal pricing scopes
    public function scopeWithSeasonalPricing($query)
    {
        return $query->whereHas('days', function($q) {
            $q->where('seasonal_hike_percentage', '>', 0);
        });
    }

    public function scopeWithoutSeasonalPricing($query)
    {
        return $query->whereDoesntHave('days', function($q) {
            $q->where('seasonal_hike_percentage', '>', 0);
        });
    }

    // Dashboard Analytics Scopes
    public function scopeCancelledToday($query)
    {
        return $query->cancelled()->whereDate('cancelled_at', today());
    }

    public function scopeCancelledThisMonth($query)
    {
        return $query->cancelled()->whereMonth('cancelled_at', now()->month)
            ->whereYear('cancelled_at', now()->year);
    }

    public function scopeTourStartToday($query)
    {
        return $query->whereDate('tour_start_day', today());
    }

    public function scopeTourEndToday($query)
    {
        return $query->whereDate(DB::raw('DATE_ADD(tour_start_day, INTERVAL duration_days DAY)'), today());
    }

    public function scopeTourStartTomorrow($query)
    {
        return $query->whereDate('tour_start_day', today()->addDay());
    }

    public function scopeTourEndTomorrow($query)
    {
        return $query->whereDate(DB::raw('DATE_ADD(tour_start_day, INTERVAL duration_days DAY)'), today()->addDay());
    }

    /* -------------------------
     | Badge Colors (Bootstrap style)
     ------------------------- */
    public function getStatusBadgeColor(): string
    {
        return match($this->status) {
            'Pending'   => 'warning',
            'Finalized' => 'success',
            'Cancelled' => 'danger',
            default     => 'secondary',
        };
    }

    public function getPaymentStatusBadgeColor(): string
    {
        return match($this->payment_status) {
            'Unpaid'        => 'danger',
            'Partially Paid'=> 'warning',
            'Fully Paid'    => 'success',
            default         => 'secondary',
        };
    }

    public function getRefundStatusBadgeColor(): string
    {
        return match($this->refund_status) {
            'Pending'   => 'warning',
            'Processed' => 'success',
            'Rejected'  => 'danger',
            default     => 'secondary',
        };
    }

    // NEW: Seasonal pricing badge
    public function getSeasonalPricingBadgeColor(): string
    {
        return $this->hasSeasonalPricing() ? 'info' : 'secondary';
    }

    /* -------------------------
     | Utility
     ------------------------- */
    public function canConvertToBilling(): bool
    {
        return $this->type === 'Quotation' && $this->status !== 'Cancelled';
    }

    // Formatters
    public function getFormattedFinalPriceAttribute(): string
    {
        return '₹' . number_format($this->final_price, 2);
    }

    public function getFormattedTotalPaidAttribute(): string
    {
        return '₹' . number_format($this->total_paid_amount, 2);
    }

    public function getFormattedRemainingAmountAttribute(): string
    {
        return '₹' . number_format($this->remaining_amount, 2);
    }

    public function getFormattedRefundAmountAttribute(): string
    {
        return '₹' . number_format($this->refund_amount, 2);
    }

    // NEW: Seasonal pricing formatters
    public function getFormattedSeasonalRevenueImpactAttribute(): string
    {
        return '₹' . number_format($this->calculateSeasonalRevenueImpact(), 2);
    }

    // Dynamic helpers
    public function getCurrentRemainingAmount(): float
    {
        return max(0, $this->final_price - $this->calculateTotalPaid());
    }

    public function getCurrentRefundableAmount(): float
    {
        $alreadyRefunded = $this->refunds()->sum('amount');
        return max(0, $this->calculateTotalPaid() - $alreadyRefunded);
    }

    public function isFullyPaid(): bool
    {
        return $this->getCurrentRemainingAmount() <= 0;
    }

    // User relationship
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    // Trip code generation methods
    public static function generateTripCode($invoiceId, $createdAt, $travelAgentName = 'BTAT')
    {
        try {
            // Generate agent code from travel agent name
            $agentCode = self::generateAgentCode($travelAgentName);
            
            // Parse creation date
            $createdDate = \Carbon\Carbon::parse($createdAt);
            $yearMonth = $createdDate->format('ym');
            
            // Encode the invoice ID
            $encodedId = strtoupper(base_convert($invoiceId, 10, 36));
            $paddedId = str_pad($encodedId, 5, '0', STR_PAD_LEFT);
            
            return "{$agentCode}/{$yearMonth}/{$paddedId}/TC";
        } catch (\Exception $e) {
            // Fallback if anything fails
            $encodedId = strtoupper(base_convert($invoiceId, 10, 36));
            $paddedId = str_pad($encodedId, 5, '0', STR_PAD_LEFT);
            return "TC/0000/{$paddedId}/TC";
        }
    }

    // Add this new method to generate agent code from name
    public static function generateAgentCode($agentName)
    {
        if (empty($agentName)) {
            return 'TA'; // Default if no name
        }
        
        // Remove special characters and split into words
        $cleanedName = preg_replace('/[^a-zA-Z0-9\s]/', '', $agentName);
        $words = preg_split('/\s+/', $cleanedName);
        
        $code = '';
        
        foreach ($words as $word) {
            if (!empty(trim($word))) {
                $code .= strtoupper($word[0]);
            }
        }
        
        // If we got a code, return it (max 10 chars for safety)
        if (!empty($code)) {
            return substr($code, 0, 10);
        }
        
        return 'TA'; // Fallback
    }
}