<?php

namespace App\Models;

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

class Employee extends Model
{
    use HasFactory;

    protected $fillable = [
        'company_id',
        'employee_number',
        'first_name',
        'last_name',
        'email',
        'phone',
        'date_of_birth',
        'hire_date',
        'employment_type',
        'gender',
        'national_id',
        'napsa_number',
        'nhima_number',
        'bank_name',
        'bank_account_number',
        'bank_account_name',
        'bank_branch',
        'payment_method',
        'is_active',
        'notes',
        'created_by',
        'updated_by',
        // NEW: Leave accrual fields
        'leave_balance_days',
        'leave_accrual_rate',
        'last_leave_accrual_date',
        'total_leave_accrued_days',
        'total_leave_taken_days',
        'total_leave_encashed_days'
    ];

    protected $casts = [
        'date_of_birth' => 'date',
        'hire_date' => 'date',
        'is_active' => 'boolean',
        // NEW: Leave accrual casts
        'leave_balance_days' => 'decimal:2',
        'leave_accrual_rate' => 'decimal:2',
        'total_leave_accrued_days' => 'decimal:2',
        'total_leave_taken_days' => 'decimal:2',
        'total_leave_encashed_days' => 'decimal:2',
        'last_leave_accrual_date' => 'date'
    ];

    // NEW: Default attribute values
    protected $attributes = [
        'leave_balance_days' => 0.00,
        'leave_accrual_rate' => 2.00, // 2 days per month by default
        'total_leave_accrued_days' => 0.00,
        'total_leave_taken_days' => 0.00,
        'total_leave_encashed_days' => 0.00,
    ];

    public function company()
    {
        return $this->belongsTo(Company::class, 'company_id');
    }

    public function payrollEntries()
    {
        return $this->hasMany(PayrollEntry::class, 'employee_id');
    }

    public function earnings()
    {
        return $this->hasMany(EmployeeEarning::class, 'employee_id');
    }

    public function activeEarnings()
    {
        return $this->hasMany(EmployeeEarning::class, 'employee_id')
            ->where('is_active', true)
            ->with('earningType');
    }

    // NEW: Leave accrual transactions relationship
    public function leaveAccrualTransactions()
    {
        return $this->hasMany(LeaveAccrualTransaction::class, 'employee_id');
    }

    public function getFullNameAttribute()
    {
        return $this->first_name . ' ' . $this->last_name;
    }

    public function getTotalAllowancesAttribute()
    {
        // This is now handled through earnings
        return $this->getEarningsByType('allowance');
    }

    public function getGrossSalaryAttribute()
    {
        return $this->calculateMonthlyEarnings();
    }

    public function getTaxableEarningsAttribute()
    {
        return $this->calculateTaxableEarnings();
    }

    public function getNonTaxableEarningsAttribute()
    {
        return $this->calculateNonTaxableEarnings();
    }

    // NEW: Get leave liability amount
    public function getLeaveLiabilityAmount()
    {
        $basicSalary = $this->getEarningsByType('basic');
        if ($basicSalary > 0 && $this->leave_balance_days > 0) {
            // Calculate daily rate based on basic salary (assuming 30 days in a month)
            $dailyRate = $basicSalary / 30;
            return $dailyRate * $this->leave_balance_days;
        }
        return 0;
    }

    public function calculateMonthlyEarnings()
    {
        $total = 0;
        foreach ($this->activeEarnings as $earning) {
            if ($earning->isCurrent()) {
                $total += $earning->getMonthlyAmount();
            }
        }
        return $total;
    }

    public function calculateTaxableEarnings()
    {
        $total = 0;
        foreach ($this->activeEarnings as $earning) {
            if ($earning->isCurrent() && $earning->earningType->is_taxable) {
                $total += $earning->getMonthlyAmount();
            }
        }
        return $total;
    }

    public function calculateNonTaxableEarnings()
    {
        $total = 0;
        foreach ($this->activeEarnings as $earning) {
            if ($earning->isCurrent() && !$earning->earningType->is_taxable) {
                $total += $earning->getMonthlyAmount();
            }
        }
        return $total;
    }

    public function getEarningsByType($type)
    {
        $total = 0;
        foreach ($this->activeEarnings as $earning) {
            if ($earning->isCurrent() && $earning->earningType->type === $type) {
                $total += $earning->getMonthlyAmount();
            }
        }
        return $total;
    }

    public function getEarningsBreakdown()
    {
        $breakdown = [];
        foreach ($this->activeEarnings as $earning) {
            if ($earning->isCurrent()) {
                $type = $earning->earningType->type;
                if (!isset($breakdown[$type])) {
                    $breakdown[$type] = 0;
                }
                $breakdown[$type] += $earning->getMonthlyAmount();
            }
        }
        return $breakdown;
    }

    // NEW: Leave accrual methods
    public function calculateLeaveAccrual($periodEndDate = null)
    {
        if (!$periodEndDate) {
            $periodEndDate = now();
        }

        $periodEndDate = \Carbon\Carbon::parse($periodEndDate);
        
        // Check if accrual is needed
        if ($this->last_leave_accrual_date) {
            $lastAccrualDate = \Carbon\Carbon::parse($this->last_leave_accrual_date);
            
            // Calculate months since last accrual
            $months = $lastAccrualDate->diffInMonths($periodEndDate);
            
            if ($months <= 0) {
                return 0; // No accrual needed
            }
            
            $accruedDays = $months * $this->leave_accrual_rate;
            
            return [
                'accrued_days' => $accruedDays,
                'months' => $months,
                'from_date' => $lastAccrualDate->format('Y-m-d'),
                'to_date' => $periodEndDate->format('Y-m-d')
            ];
        } else {
            // First accrual - calculate from hire date
            $hireDate = \Carbon\Carbon::parse($this->hire_date);
            $months = $hireDate->diffInMonths($periodEndDate);
            
            if ($months <= 0) {
                return 0; // Not hired yet or hired this month
            }
            
            $accruedDays = $months * $this->leave_accrual_rate;
            
            return [
                'accrued_days' => $accruedDays,
                'months' => $months,
                'from_date' => $hireDate->format('Y-m-d'),
                'to_date' => $periodEndDate->format('Y-m-d')
            ];
        }
    }

    public function applyLeaveAccrual($accruedDays, $periodDate = null, $createJournal = true)
    {
        if (!$periodDate) {
            $periodDate = now();
        }

        DB::beginTransaction();

        try {
            // Update employee leave balance
            $this->leave_balance_days += $accruedDays;
            $this->total_leave_accrued_days += $accruedDays;
            $this->last_leave_accrual_date = $periodDate;
            $this->save();

            // Create leave accrual transaction
            $transaction = LeaveAccrualTransaction::create([
                'employee_id' => $this->id,
                'period_date' => $periodDate,
                'accrued_days' => $accruedDays,
                'balance_days' => $this->leave_balance_days,
                'created_by' => auth()->id()
            ]);

            // Create journal entry if requested
            if ($createJournal) {
                $this->createLeaveAccrualJournalEntry($accruedDays, $periodDate, $transaction->id);
            }

            DB::commit();

            return $transaction;

        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    private function createLeaveAccrualJournalEntry($accruedDays, $periodDate, $transactionId)
    {
        $company = $this->company;
        $basicSalary = $this->getEarningsByType('basic');
        $dailyRate = $basicSalary > 0 ? $basicSalary / 30 : 0;
        $accrualAmount = $dailyRate * $accruedDays;

        if ($accrualAmount <= 0) {
            return null;
        }

        // Get business process mappings
        $leaveAccrualAccountId = BusinessProcessMapping::getPayrollAccountId('Leave Accrual');
        $leaveLiabilityAccountId = BusinessProcessMapping::getPayrollAccountId('Leave Liability');

        if (!$leaveAccrualAccountId || !$leaveLiabilityAccountId) {
            // Log error but don't fail the transaction
            \Log::error('Leave accrual accounts not configured in business process mappings');
            return null;
        }

        // Create journal entry
        $journal = JournalEntry::create([
            'entry_date' => $periodDate,
            'description' => 'Leave accrual for ' . $this->full_name . ' (' . $accruedDays . ' days)',
            'reference_number' => 'LA-' . $this->employee_number . '-' . date('Ym', strtotime($periodDate)),
            'status' => 'draft',
            'created_by' => auth()->id(),
            'company_id' => $company->id,
            'branch_id' => $company->default_branch_id ?? 1,
        ]);

        // Debit: Leave Accrual Expense
        $journal->items()->create([
            'account_id' => $leaveAccrualAccountId,
            'debit' => $accrualAmount,
            'credit' => 0,
            'description' => 'Leave accrual expense'
        ]);

        // Credit: Leave Liability
        $journal->items()->create([
            'account_id' => $leaveLiabilityAccountId,
            'debit' => 0,
            'credit' => $accrualAmount,
            'description' => 'Leave liability accrual'
        ]);

        // Post the journal entry
        $journal->post();

        // Update transaction with journal entry ID
        LeaveAccrualTransaction::where('id', $transactionId)->update([
            'journal_entry_id' => $journal->id
        ]);

        return $journal;
    }

    public function applyLeaveTaken($daysTaken, $startDate, $endDate, $notes = null)
    {
        if ($daysTaken > $this->leave_balance_days) {
            throw new \Exception('Insufficient leave balance. Available: ' . $this->leave_balance_days . ' days');
        }

        DB::beginTransaction();

        try {
            // Update employee leave balance
            $this->leave_balance_days -= $daysTaken;
            $this->total_leave_taken_days += $daysTaken;
            $this->save();

            // Update leave accrual transaction
            $transaction = LeaveAccrualTransaction::create([
                'employee_id' => $this->id,
                'period_date' => $endDate,
                'accrued_days' => 0,
                'taken_days' => $daysTaken,
                'balance_days' => $this->leave_balance_days,
                'notes' => $notes,
                'created_by' => auth()->id()
            ]);

            DB::commit();

            return $transaction;

        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    public function applyLeaveEncashment($daysEncashed, $encashmentDate, $notes = null)
    {
        if ($daysEncashed > $this->leave_balance_days) {
            throw new \Exception('Insufficient leave balance for encashment. Available: ' . $this->leave_balance_days . ' days');
        }

        DB::beginTransaction();

        try {
            // Calculate encashment amount
            $basicSalary = $this->getEarningsByType('basic');
            $dailyRate = $basicSalary > 0 ? $basicSalary / 30 : 0;
            $encashmentAmount = $dailyRate * $daysEncashed;

            // Update employee leave balance
            $this->leave_balance_days -= $daysEncashed;
            $this->total_leave_encashed_days += $daysEncashed;
            $this->save();

            // Create leave accrual transaction
            $transaction = LeaveAccrualTransaction::create([
                'employee_id' => $this->id,
                'period_date' => $encashmentDate,
                'accrued_days' => 0,
                'encashed_days' => $daysEncashed,
                'balance_days' => $this->leave_balance_days,
                'notes' => $notes,
                'created_by' => auth()->id()
            ]);

            // Create journal entry for leave encashment
            $this->createLeaveEncashmentJournalEntry($daysEncashed, $encashmentAmount, $encashmentDate, $transaction->id);

            DB::commit();

            return [
                'transaction' => $transaction,
                'encashment_amount' => $encashmentAmount
            ];

        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    private function createLeaveEncashmentJournalEntry($daysEncashed, $encashmentAmount, $encashmentDate, $transactionId)
    {
        $company = $this->company;

        // Get business process mappings
        $leaveLiabilityAccountId = BusinessProcessMapping::getPayrollAccountId('Leave Liability');
        $leaveEncashmentAccountId = BusinessProcessMapping::getPayrollAccountId('Leave Encashment');
        $netPayAccountId = BusinessProcessMapping::getPayrollAccountId('Payroll - Net Pay');

        if (!$leaveLiabilityAccountId || !$leaveEncashmentAccountId || !$netPayAccountId) {
            \Log::error('Leave encashment accounts not configured in business process mappings');
            return null;
        }

        // Create journal entry
        $journal = JournalEntry::create([
            'entry_date' => $encashmentDate,
            'description' => 'Leave encashment for ' . $this->full_name . ' (' . $daysEncashed . ' days)',
            'reference_number' => 'LE-' . $this->employee_number . '-' . date('Ymd', strtotime($encashmentDate)),
            'status' => 'draft',
            'created_by' => auth()->id(),
            'company_id' => $company->id,
            'branch_id' => $company->default_branch_id ?? 1,
        ]);

        // Debit: Leave Liability (reducing liability)
        $journal->items()->create([
            'account_id' => $leaveLiabilityAccountId,
            'debit' => $encashmentAmount,
            'credit' => 0,
            'description' => 'Leave liability reduction'
        ]);

        // Credit: Leave Encashment Expense
        $journal->items()->create([
            'account_id' => $leaveEncashmentAccountId,
            'debit' => 0,
            'credit' => $encashmentAmount,
            'description' => 'Leave encashment expense'
        ]);

        // Also need to pay the employee
        // Debit: Leave Encashment Expense
        $journal->items()->create([
            'account_id' => $leaveEncashmentAccountId,
            'debit' => $encashmentAmount,
            'credit' => 0,
            'description' => 'Leave encashment payment'
        ]);

        // Credit: Net Pay Payable
        $journal->items()->create([
            'account_id' => $netPayAccountId,
            'debit' => 0,
            'credit' => $encashmentAmount,
            'description' => 'Leave encashment payable'
        ]);

        // Post the journal entry
        $journal->post();

        // Update transaction with journal entry ID
        LeaveAccrualTransaction::where('id', $transactionId)->update([
            'journal_entry_id' => $journal->id
        ]);

        return $journal;
    }
}