<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class JournalEntry extends Model
{
    use HasFactory, SoftDeletes;

    /**
     * تسجيل الأحداث للنموذج
     */
    protected static function boot()
    {
        parent::boot();

        // التحقق من توازن القيد قبل الحفظ
        static::saving(function ($entry) {
            // تجاهل التحقق إذا كان القيد جديدًا (لم يتم حفظه بعد)
            if (!$entry->exists) {
                return true;
            }

            // التحقق من توازن القيد
            if (!$entry->isBalanced()) {
                // تسجيل الخطأ في ملف السجل
                \Illuminate\Support\Facades\Log::warning('محاولة حفظ قيد غير متوازن: ' . $entry->entry_number, [
                    'entry_id' => $entry->id,
                    'total_debit' => $entry->total_debit,
                    'total_credit' => $entry->total_credit,
                    'difference' => $entry->total_debit - $entry->total_credit
                ]);

                // إذا كان الفرق صغيرًا (أقل من 1)، نقوم بتصحيحه تلقائيًا
                $difference = $entry->total_debit - $entry->total_credit;
                if (abs($difference) < 1) {
                    // البحث عن حساب فروق ميزان المراجعة
                    $adjustmentAccount = \App\Models\ChartOfAccount::where('account_code', 'ADJ-DIFF')->first();

                    if ($adjustmentAccount) {
                        // إنشاء بند تصحيح
                        $item = new \App\Models\JournalEntryItem();
                        $item->journal_entry_id = $entry->id;
                        $item->account_id = $adjustmentAccount->id;
                        $item->description = 'تصحيح تلقائي لفرق ميزان المراجعة';

                        if ($difference > 0) {
                            // إذا كان المدين أكبر، نضيف بند دائن
                            $item->debit = 0;
                            $item->credit = $difference;
                        } else {
                            // إذا كان الدائن أكبر، نضيف بند مدين
                            $item->debit = abs($difference);
                            $item->credit = 0;
                        }

                        $item->save();

                        // تسجيل التصحيح في ملف السجل
                        \Illuminate\Support\Facades\Log::info('تم تصحيح قيد غير متوازن تلقائيًا: ' . $entry->entry_number, [
                            'entry_id' => $entry->id,
                            'difference' => $difference,
                            'adjustment_account' => $adjustmentAccount->account_code
                        ]);

                        return true;
                    }
                }

                // إذا كان الفرق كبيرًا، نمنع الحفظ
                if (abs($difference) >= 1) {
                    throw new \Exception('لا يمكن حفظ القيد المحاسبي لعدم توازن المدين والدائن. الفرق: ' . $difference);
                }
            }

            return true;
        });
    }

    /**
     * الحقول القابلة للتعبئة الجماعية
     *
     * @var array
     */
    protected $fillable = [
        'company_id',
        'fiscal_year_id',
        'accounting_period_id',
        'entry_number',
        'entry_date',
        'reference_type',
        'reference_id',
        'description',
        'is_posted',
        'is_approved',
        'created_by',
        'approved_by',
        'approved_at',
        'posted_by',
        'posted_at'
    ];

    /**
     * الحقول التي يجب تحويلها إلى أنواع محددة
     *
     * @var array
     */
    protected $casts = [
        'entry_date' => 'date',
        'is_posted' => 'boolean',
        'is_approved' => 'boolean',
        'approved_at' => 'datetime',
        'posted_at' => 'datetime'
    ];

    /**
     * علاقة مع الشركة
     */
    public function company()
    {
        return $this->belongsTo(Company::class);
    }

    /**
     * علاقة مع السنة المالية
     */
    public function fiscalYear()
    {
        return $this->belongsTo(FiscalYear::class);
    }

    /**
     * علاقة مع الفترة المحاسبية
     */
    public function accountingPeriod()
    {
        return $this->belongsTo(AccountingPeriod::class);
    }

    /**
     * علاقة مع بنود القيد المحاسبي
     */
    public function items()
    {
        return $this->hasMany(JournalEntryItem::class);
    }

    /**
     * علاقة مع المستخدم الذي أنشأ القيد
     */
    public function creator()
    {
        return $this->belongsTo(User::class, 'created_by');
    }

    /**
     * علاقة مع المستخدم الذي اعتمد القيد
     */
    public function approver()
    {
        return $this->belongsTo(User::class, 'approved_by');
    }

    /**
     * علاقة مع المستخدم الذي رحل القيد
     */
    public function poster()
    {
        return $this->belongsTo(User::class, 'posted_by');
    }

    /**
     * علاقة مع المرجع (مورفيك)
     */
    public function reference()
    {
        return $this->morphTo();
    }

    /**
     * الحصول على وصف مناسب للمرجع
     * 
     * @return string
     */
    public function getFormattedReferenceAttribute()
    {
        if (!$this->reference_type || !$this->reference_id) {
            return 'غير محدد';
        }

        $referenceType = $this->reference_type;
        $referenceId = $this->reference_id;

        // فاتورة مشتريات
        if ($referenceType === 'App\\Models\\PurchaseInvoice') {
            $invoice = \App\Models\PurchaseInvoice::find($referenceId);
            if ($invoice) {
                return 'فاتورة مشتريات رقم: ' . $invoice->invoice_number . ' - ' . $invoice->supplier->name;
            }
        }

        // فاتورة مبيعات
        if ($referenceType === 'App\\Models\\Invoice') {
            $invoice = \App\Models\Invoice::find($referenceId);
            if ($invoice) {
                return 'فاتورة مبيعات رقم: ' . $invoice->invoice_number . ' - ' . $invoice->customer->name;
            }
        }

        // سند قبض
        if ($referenceType === 'App\\Models\\Payment' && strpos($this->description, 'سند قبض') !== false) {
            $payment = \App\Models\Payment::find($referenceId);
            if ($payment) {
                return 'سند قبض رقم: ' . $payment->payment_number . ' - ' . $payment->customer->name;
            }
        }

        // سند صرف
        if ($referenceType === 'App\\Models\\Payment' && strpos($this->description, 'سند صرف') !== false) {
            $payment = \App\Models\Payment::find($referenceId);
            if ($payment) {
                return 'سند صرف رقم: ' . $payment->payment_number . ' - ' . $payment->supplier->name;
            }
        }

        // إذا لم يتم التعرف على نوع المرجع
        return class_basename($referenceType) . ' #' . $referenceId;
    }

    /**
     * علاقة مع معاملات الخزينة
     */
    public function cashTransactions()
    {
        return $this->belongsToMany(CashTransaction::class, 'cash_transaction_entries', 'journal_entry_id', 'cash_transaction_id');
    }

    /**
     * نطاق للقيود المحاسبية المرحلة
     */
    public function scopePosted($query)
    {
        return $query->where('is_posted', true);
    }

    /**
     * نطاق للقيود المحاسبية غير المرحلة
     */
    public function scopeUnposted($query)
    {
        return $query->where('is_posted', false);
    }

    /**
     * نطاق للقيود المحاسبية المعتمدة
     */
    public function scopeApproved($query)
    {
        return $query->where('is_approved', true);
    }

    /**
     * نطاق للقيود المحاسبية غير المعتمدة
     */
    public function scopeUnapproved($query)
    {
        return $query->where('is_approved', false);
    }

    /**
     * الحصول على إجمالي المدين
     */
    public function getTotalDebitAttribute()
    {
        return $this->items->sum('debit');
    }

    /**
     * الحصول على إجمالي الدائن
     */
    public function getTotalCreditAttribute()
    {
        return $this->items->sum('credit');
    }

    /**
     * الحصول على مبلغ القيد المحاسبي (إجمالي المدين أو الدائن)
     */
    public function getAmountAttribute()
    {
        return $this->total_debit;
    }

    /**
     * التحقق من توازن القيد المحاسبي
     */
    public function isBalanced()
    {
        return $this->total_debit == $this->total_credit;
    }

    /**
     * ترحيل القيد المحاسبي
     */
    public function post()
    {
        // التحقق من توازن القيد
        if (!$this->isBalanced()) {
            throw new \Exception('لا يمكن ترحيل القيد المحاسبي لعدم توازن المدين والدائن');
        }

        // التحقق من أن الفترة المحاسبية غير مغلقة
        if ($this->accountingPeriod->is_closed) {
            throw new \Exception('لا يمكن ترحيل القيد المحاسبي لأن الفترة المحاسبية مغلقة');
        }

        // تحديث أرصدة الحسابات
        foreach ($this->items as $item) {
            $item->account->updateBalance();
        }

        $this->is_posted = true;
        $this->save();

        // إنشاء كشف حساب للعميل من القيد المحاسبي
        \App\Models\CustomerStatement::createForJournalEntry($this);

        return true;
    }

    /**
     * إلغاء ترحيل القيد المحاسبي
     */
    public function unpost()
    {
        // التحقق من أن الفترة المحاسبية غير مغلقة
        if ($this->accountingPeriod->is_closed) {
            throw new \Exception('لا يمكن إلغاء ترحيل القيد المحاسبي لأن الفترة المحاسبية مغلقة');
        }

        // التحقق من أن القيد غير معتمد
        if ($this->is_approved) {
            throw new \Exception('لا يمكن إلغاء ترحيل القيد المحاسبي لأنه معتمد');
        }

        // حذف سجلات كشف حساب العميل المرتبطة بهذا القيد
        \App\Models\CustomerStatement::where('reference_type', 'App\\Models\\JournalEntry')
            ->where('reference_id', $this->id)
            ->delete();

        $this->is_posted = false;
        $this->save();

        // تحديث أرصدة الحسابات
        foreach ($this->items as $item) {
            $item->account->updateBalance();
        }

        return true;
    }

    /**
     * اعتماد القيد المحاسبي
     */
    public function approve($userId)
    {
        // التحقق من أن القيد مرحل
        if (!$this->is_posted) {
            throw new \Exception('لا يمكن اعتماد القيد المحاسبي لأنه غير مرحل');
        }

        $this->is_approved = true;
        $this->approved_by = $userId;
        $this->approved_at = now();
        $this->save();

        return true;
    }

    /**
     * إلغاء اعتماد القيد المحاسبي
     */
    public function unapprove()
    {
        // التحقق من أن الفترة المحاسبية غير مغلقة
        if ($this->accountingPeriod->is_closed) {
            throw new \Exception('لا يمكن إلغاء اعتماد القيد المحاسبي لأن الفترة المحاسبية مغلقة');
        }

        $this->is_approved = false;
        $this->approved_by = null;
        $this->approved_at = null;
        $this->save();

        return true;
    }

    /**
     * إنشاء قيد محاسبي من قالب
     */
    public static function createFromTemplate($templateId, $data)
    {
        $template = JournalTemplate::findOrFail($templateId);
        $company = Company::findOrFail($data['company_id']);

        // البحث عن السنة المالية والفترة المحاسبية النشطة
        $fiscalYear = FiscalYear::where('company_id', $company->id)
            ->where('is_active', true)
            ->where('is_closed', false)
            ->first();

        if (!$fiscalYear) {
            throw new \Exception('لا توجد سنة مالية نشطة');
        }

        $entryDate = $data['entry_date'] ?? now();

        $accountingPeriod = AccountingPeriod::where('fiscal_year_id', $fiscalYear->id)
            ->where('is_closed', false)
            ->where('start_date', '<=', $entryDate)
            ->where('end_date', '>=', $entryDate)
            ->first();

        if (!$accountingPeriod) {
            throw new \Exception('لا توجد فترة محاسبية نشطة للتاريخ المحدد');
        }

        // إنشاء القيد المحاسبي
        $entry = new JournalEntry();
        $entry->company_id = $company->id;
        $entry->fiscal_year_id = $fiscalYear->id;
        $entry->accounting_period_id = $accountingPeriod->id;
        $entry->entry_number = $data['entry_number'] ?? self::generateEntryNumber($company->id);
        $entry->entry_date = $entryDate;
        $entry->reference_type = $data['reference_type'] ?? null;
        $entry->reference_id = $data['reference_id'] ?? null;
        $entry->description = $data['description'] ?? $template->description;
        $entry->is_posted = false;
        $entry->is_approved = false;
        $entry->created_by = $data['created_by'];
        $entry->save();

        // إنشاء بنود القيد المحاسبي من بنود القالب
        $totalAmount = $data['amount'] ?? 0;

        foreach ($template->items as $templateItem) {
            $item = new JournalEntryItem();
            $item->journal_entry_id = $entry->id;
            $item->account_id = $templateItem->account_id;
            $item->description = $templateItem->description;

            // حساب المبلغ بناءً على النسبة أو المبلغ الثابت
            if ($templateItem->percentage) {
                $amount = $totalAmount * ($templateItem->percentage / 100);
            } else {
                $amount = $templateItem->fixed_amount ?? 0;
            }

            if ($templateItem->type == 'debit') {
                $item->debit = $amount;
                $item->credit = 0;
            } else {
                $item->debit = 0;
                $item->credit = $amount;
            }

            $item->save();
        }

        return $entry;
    }

    /**
     * توليد رقم قيد محاسبي جديد
     * 
     * يقوم بإنشاء رقم قيد محاسبي فريد على مستوى الشركة
     * يمكن أن يتكرر نفس الرقم في شركات مختلفة
     */
    public static function generateEntryNumber($companyId)
    {
        // تسجيل بداية عملية توليد رقم قيد جديد
        \Log::info('بدء توليد رقم قيد محاسبي جديد للشركة رقم: ' . $companyId);

        // البحث عن آخر قيد محاسبي للشركة
        $lastEntry = self::where('company_id', $companyId)
            ->orderBy('id', 'desc')
            ->first();

        if (!$lastEntry) {
            // إذا لم يكن هناك قيود سابقة، نبدأ من 1
            $entryNumber = 'JE-' . str_pad(1, 6, '0', STR_PAD_LEFT);
            \Log::info('لا توجد قيود سابقة للشركة ' . $companyId . '، بدء الترقيم من: ' . $entryNumber);
            return $entryNumber;
        }

        // استخراج الرقم من آخر قيد
        if (preg_match('/JE-(\d+)/', $lastEntry->entry_number, $matches)) {
            $nextNumber = (int)$matches[1] + 1;
            \Log::info('تم استخراج الرقم من آخر قيد للشركة ' . $companyId . ': ' . $lastEntry->entry_number . ' والرقم التالي هو: ' . $nextNumber);
        } else {
            // إذا كان تنسيق الرقم غير متوقع، نستخدم معرف القيد + 1
            $nextNumber = $lastEntry->id + 1;
            \Log::info('تنسيق الرقم غير متوقع للشركة ' . $companyId . '، استخدام معرف القيد + 1: ' . $nextNumber);
        }

        // تنسيق الرقم الجديد
        $entryNumber = 'JE-' . str_pad($nextNumber, 6, '0', STR_PAD_LEFT);

        // التحقق من عدم وجود قيد بنفس الرقم للشركة نفسها
        $existingEntry = self::where('company_id', $companyId)
            ->where('entry_number', $entryNumber)
            ->first();

        if ($existingEntry) {
            // إذا كان هناك قيد بنفس الرقم، نزيد الرقم بواحد ونحاول مرة أخرى
            \Log::warning('تم العثور على قيد بنفس الرقم للشركة ' . $companyId . ': ' . $entryNumber . '، محاولة توليد رقم جديد');

            // البحث عن أعلى رقم قيد للشركة
            $highestEntry = self::where('company_id', $companyId)
                ->where('entry_number', 'LIKE', 'JE-%')
                ->orderByRaw('CAST(SUBSTRING(entry_number, 4) AS UNSIGNED) DESC')
                ->first();

            if ($highestEntry && preg_match('/JE-(\d+)/', $highestEntry->entry_number, $matches)) {
                $nextNumber = (int)$matches[1] + 1;
                \Log::info('تم استخراج أعلى رقم قيد للشركة ' . $companyId . ': ' . $highestEntry->entry_number . ' والرقم التالي هو: ' . $nextNumber);
            } else {
                $nextNumber++;
                \Log::info('زيادة الرقم للشركة ' . $companyId . ' إلى: ' . $nextNumber);
            }

            $entryNumber = 'JE-' . str_pad($nextNumber, 6, '0', STR_PAD_LEFT);

            // التحقق مرة أخرى
            $existingEntry = self::where('company_id', $companyId)
                ->where('entry_number', $entryNumber)
                ->first();

            if ($existingEntry) {
                // إذا استمرت المشكلة، نستخدم الوقت الحالي لضمان رقم فريد
                $timestamp = now()->format('YmdHis');
                $entryNumber = 'JE-' . $timestamp;
                \Log::warning('استمرار وجود تكرار في الأرقام للشركة ' . $companyId . '، استخدام الطابع الزمني: ' . $entryNumber);
            }
        }

        \Log::info('تم توليد رقم قيد محاسبي جديد للشركة ' . $companyId . ': ' . $entryNumber);
        return $entryNumber;
    }
}
