<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class JournalEntryItem extends Model
{
    use HasFactory, \App\Traits\HotelScopeTrait;

    protected $table = 'journal_entry_items';

    protected $fillable = [
        'hotel_id',
        'journal_entry_id',
        'account_id',
        'debit',
        'credit',
        'description'
    ];

    protected $casts = [
        'debit' => 'decimal:2',
        'credit' => 'decimal:2',
        'created_at' => 'datetime',
        'updated_at' => 'datetime'
    ];

    protected $appends = [
        'amount',
        'type',
        'formatted_debit',
        'formatted_credit',
        'formatted_amount',
        'is_global'
    ];

    // ==================== Relationships ====================

    /**
     * Get the hotel that owns this item
     */
    public function hotel(): BelongsTo
    {
        return $this->belongsTo(Hotel::class);
    }

    /**
     * Get the journal entry that owns this item
     */
    public function journalEntry(): BelongsTo
    {
        return $this->belongsTo(JournalEntry::class, 'journal_entry_id');
    }

    /**
     * Get the account for this item
     */
    public function account(): BelongsTo
    {
        return $this->belongsTo(Account::class, 'account_id');
    }

    // ==================== Scopes ====================

    /**
     * Scope to filter by hotel including global items
     */
    public function scopeForHotel($query, $hotelId)
    {
        return $query->where(function($q) use ($hotelId) {
            $q->where('hotel_id', $hotelId)
              ->orWhereNull('hotel_id');
        });
    }

    /**
     * Scope to get only global items
     */
    public function scopeGlobal($query)
    {
        return $query->whereNull('hotel_id');
    }

    /**
     * Scope to get only hotel-specific items
     */
    public function scopeHotelSpecific($query, $hotelId)
    {
        return $query->where('hotel_id', $hotelId);
    }

    /**
     * Scope to get only debit items
     */
    public function scopeDebitItems($query)
    {
        return $query->where('debit', '>', 0);
    }

    /**
     * Scope to get only credit items
     */
    public function scopeCreditItems($query)
    {
        return $query->where('credit', '>', 0);
    }

    /**
     * Scope to filter by account
     */
    public function scopeForAccount($query, $accountId)
    {
        return $query->where('account_id', $accountId);
    }

    // ==================== Accessors ====================

    /**
     * Check if item is global
     */
    public function getIsGlobalAttribute(): bool
    {
        return is_null($this->hotel_id);
    }

    /**
     * Get the amount (non-zero value)
     */
    public function getAmountAttribute(): float
    {
        return (float) ($this->debit > 0 ? $this->debit : $this->credit);
    }

    /**
     * Get the type (debit or credit)
     */
    public function getTypeAttribute(): string
    {
        return $this->debit > 0 ? 'debit' : 'credit';
    }

    /**
     * Get formatted debit amount
     */
    public function getFormattedDebitAttribute(): string
    {
        return $this->debit > 0 ? number_format($this->debit, 2) : '-';
    }

    /**
     * Get formatted credit amount
     */
    public function getFormattedCreditAttribute(): string
    {
        return $this->credit > 0 ? number_format($this->credit, 2) : '-';
    }

    /**
     * Get formatted amount
     */
    public function getFormattedAmountAttribute(): string
    {
        $currency = $this->journalEntry->currency_code ?? 'UGX';
        $symbol = $currency === 'USD' ? '$' : 'UGX ';
        
        return $symbol . number_format($this->amount, 2);
    }

    /**
     * Get the account name with code
     */
    public function getAccountNameAttribute(): string
    {
        return $this->account ? $this->account->code . ' - ' . $this->account->name : 'N/A';
    }

    // ==================== Methods ====================

    /**
     * Check if this is a debit item
     */
    public function isDebit(): bool
    {
        return $this->debit > 0;
    }

    /**
     * Check if this is a credit item
     */
    public function isCredit(): bool
    {
        return $this->credit > 0;
    }

    /**
     * Validate the item
     */
    public function validate(): array
    {
        $errors = [];

        if ($this->debit > 0 && $this->credit > 0) {
            $errors[] = 'Item cannot have both debit and credit amounts.';
        }

        if ($this->debit == 0 && $this->credit == 0) {
            $errors[] = 'Item must have either debit or credit amount.';
        }

        if ($this->debit < 0 || $this->credit < 0) {
            $errors[] = 'Amounts cannot be negative.';
        }

        if (!$this->account) {
            $errors[] = 'Invalid account selected.';
        }

        if ($this->account && $this->account->is_header) {
            $errors[] = "Cannot post to header account: {$this->account->full_path}";
        }

        return $errors;
    }

    /**
     * Boot method for model events
     */
    protected static function boot()
    {
        parent::boot();

        static::creating(function ($item) {
            // Ensure hotel_id is set from journal entry if not provided
            if (empty($item->hotel_id) && $item->journalEntry) {
                $item->hotel_id = $item->journalEntry->hotel_id;
            }
        });

        static::created(function ($item) {
            // Immediately apply balance if parent journal entry is already posted
            if ($item->journalEntry && $item->journalEntry->status === JournalEntry::STATUS_POSTED) {
                if ($item->account) {
                    $item->account->updateBalanceFromItem($item);
                }
            }
        });

        static::saving(function ($item) {
            // Prevent modification if parent journal entry is posted
            if ($item->exists && $item->journalEntry && $item->journalEntry->status === JournalEntry::STATUS_POSTED) {
                throw new \Exception('Cannot modify items of a posted journal entry.');
            }

            // Validate the item
            $errors = $item->validate();
            if (!empty($errors)) {
                throw new \Exception(implode(' ', $errors));
            }

            // Ensure only one of debit or credit is set
            if ($item->debit > 0) {
                $item->credit = 0;
            } elseif ($item->credit > 0) {
                $item->debit = 0;
            }
        });

        static::deleting(function ($item) {
            // Prevent deletion if parent journal entry is posted
            if ($item->journalEntry && $item->journalEntry->status === JournalEntry::STATUS_POSTED) {
                throw new \Exception('Cannot delete items from a posted journal entry.');
            }
        });
    }
}