<?php

namespace App\Models;

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

class Transaction extends Model
{
    use HasFactory;

    protected $fillable = [
        'date',
        'account_id',
        'party_id',
        'transactionable_type',
        'transactionable_id',
        'voucher_type',
        'voucher_no',
        'description',
        'debit',
        'credit',
        'balance',
        'created_by',
    ];

    protected $casts = [
        'date' => 'date',
        'debit' => 'decimal:2',
        'credit' => 'decimal:2',
        'balance' => 'decimal:2',
    ];

    /**
     * Voucher type labels
     */
    public const VOUCHER_TYPES = [
        'PUR' => 'Purchase',
        'SAL' => 'Sale',
        'CR' => 'Credit Voucher',
        'DR' => 'Debit Voucher',
        'JV' => 'Journal Voucher',
        'OB' => 'Opening Balance',
    ];

    /**
     * Account relationship
     */
    public function account(): BelongsTo
    {
        return $this->belongsTo(Account::class);
    }

    /**
     * Party relationship
     */
    public function party(): BelongsTo
    {
        return $this->belongsTo(Party::class);
    }

    /**
     * Created by user relationship
     */
    public function createdBy(): BelongsTo
    {
        return $this->belongsTo(User::class, 'created_by');
    }

    /**
     * Transactionable (polymorphic) relationship
     */
    public function transactionable(): MorphTo
    {
        return $this->morphTo();
    }

    /**
     * Get voucher type label
     */
    public function getVoucherTypeLabelAttribute(): string
    {
        return self::VOUCHER_TYPES[$this->voucher_type] ?? $this->voucher_type;
    }

    /**
     * Scope for date range
     */
    public function scopeDateRange($query, $from, $to)
    {
        return $query->whereBetween('date', [$from, $to]);
    }

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

    /**
     * Scope by party
     */
    public function scopeForParty($query, int $partyId)
    {
        return $query->where('party_id', $partyId);
    }

    /**
     * Scope by voucher type
     */
    public function scopeOfVoucherType($query, string $type)
    {
        return $query->where('voucher_type', $type);
    }

    /**
     * Get running balance for an account up to a date
     */
    public static function getBalanceUpTo(int $accountId, string $date): float
    {
        $account = Account::find($accountId);
        if (!$account) {
            return 0;
        }

        $debit = self::where('account_id', $accountId)
            ->where('date', '<', $date)
            ->sum('debit');

        $credit = self::where('account_id', $accountId)
            ->where('date', '<', $date)
            ->sum('credit');

        if ($account->normal_balance === 'debit') {
            return $account->opening_balance + $debit - $credit;
        }

        return $account->opening_balance + $credit - $debit;
    }

    /**
     * Get ledger entries for an account with running balance
     */
    public static function getLedger(int $accountId, string $fromDate, string $toDate): array
    {
        $account = Account::find($accountId);
        if (!$account) {
            return ['entries' => [], 'opening_balance' => 0, 'closing_balance' => 0];
        }

        $openingBalance = self::getBalanceUpTo($accountId, $fromDate);

        $entries = self::where('account_id', $accountId)
            ->whereBetween('date', [$fromDate, $toDate])
            ->orderBy('date')
            ->orderBy('id')
            ->get();

        $runningBalance = $openingBalance;
        $entriesWithBalance = [];

        foreach ($entries as $entry) {
            if ($account->normal_balance === 'debit') {
                $runningBalance += $entry->debit - $entry->credit;
            } else {
                $runningBalance += $entry->credit - $entry->debit;
            }

            $entry->balance = $runningBalance;
            $entriesWithBalance[] = $entry;
        }

        return [
            'entries' => $entriesWithBalance,
            'opening_balance' => $openingBalance,
            'closing_balance' => $runningBalance,
            'total_debit' => $entries->sum('debit'),
            'total_credit' => $entries->sum('credit'),
        ];
    }
}
