<?php

namespace App\Models;

use App\Enums\PayrollAdjustmentType;
use App\Enums\PayrollStatus;
use App\Models\Concerns\InteractsWithActivityLog;
use Illuminate\Database\Eloquent\Attributes\Appends;
use Illuminate\Database\Eloquent\Attributes\Guarded;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

#[Guarded(['id'])]
#[Appends([
    'base_salary_formatted',
    'bonus_amount_formatted',
    'can_adjust',
    'deduction_amount_formatted',
    'employee_name',
    'paid_at_formatted',
    'status_label',
    'total_amount_formatted',
])]
class Payroll extends Model
{
    // 1. Use Trait
    use HasFactory, InteractsWithActivityLog;

    // 2. Casting
    protected function casts(): array
    {
        return [
            'base_salary' => 'integer',
            'bonus_amount' => 'integer',
            'deduction_amount' => 'integer',
            'paid_at' => 'datetime',
            'status' => PayrollStatus::class,
            'total_amount' => 'integer',
        ];
    }

    // 3. Scope (grouped by column, then alphabetical)
    // Column Group: status
    #[Scope]
    protected function paid(Builder $query): void
    {
        $query->where('status', PayrollStatus::PAID);
    }

    #[Scope]
    protected function unpaid(Builder $query): void
    {
        $query->where('status', PayrollStatus::UNPAID);
    }

    // 4. Attribute
    public function baseSalaryFormatted(): Attribute
    {
        return Attribute::make(
            get: fn () => $this->base_salary !== null ? 'Rp '.number_format($this->base_salary, 0, ',', '.') : '-',
        );
    }

    public function bonusAmountFormatted(): Attribute
    {
        return Attribute::make(
            get: fn () => 'Rp '.number_format($this->bonus_amount, 0, ',', '.'),
        );
    }

    public function canAdjust(): Attribute
    {
        return Attribute::make(
            get: fn () => $this->status === PayrollStatus::UNPAID
                && $this->relationLoaded('payrollPeriod')
                && $this->payrollPeriod?->isOpen(),
        );
    }

    public function deductionAmountFormatted(): Attribute
    {
        return Attribute::make(
            get: fn () => 'Rp '.number_format($this->deduction_amount, 0, ',', '.'),
        );
    }

    public function employeeName(): Attribute
    {
        return Attribute::make(
            get: fn () => $this->employee?->user?->profile?->full_name
                ?? $this->employee?->user?->username,
        );
    }

    public function paidAtFormatted(): Attribute
    {
        return Attribute::make(
            get: fn () => $this->paid_at?->translatedFormat('l, d F Y H:i'),
        );
    }

    public function statusLabel(): Attribute
    {
        return Attribute::make(
            get: fn () => $this->status?->label(),
        );
    }

    public function totalAmountFormatted(): Attribute
    {
        return Attribute::make(
            get: fn () => 'Rp '.number_format($this->total_amount, 0, ',', '.'),
        );
    }

    // 5. Other Methods
    public function calculateAdvanceDeduction(): int
    {
        $outstanding = (int) EmployeeAdvance::query()
            ->where('employee_id', $this->employee_id)
            ->whereIn('status', [EmployeeAdvanceStatus::APPROVED, EmployeeAdvanceStatus::PARTIALLY_PAID])
            ->selectRaw('SUM(amount - paid_amount) as remaining')
            ->value('remaining');

        return min($outstanding, (int) $this->base_salary + (int) $this->adjustments()
            ->where('type', PayrollAdjustmentType::BONUS)
            ->sum('amount'));
    }

    public function recalculateAmounts(): void
    {
        $bonusAmount = (int) $this->adjustments()
            ->where('type', PayrollAdjustmentType::BONUS)
            ->sum('amount');

        $manualDeduction = (int) $this->adjustments()
            ->where('type', PayrollAdjustmentType::DEDUCTION)
            ->sum('amount');

        $advanceDeduction = $this->calculateAdvanceDeduction();

        $this->bonus_amount = $bonusAmount;
        $this->deduction_amount = $advanceDeduction + $manualDeduction;
        $this->total_amount = max(0, (int) $this->base_salary + $bonusAmount - $this->deduction_amount);
    }

    // 6. Relation
    public function adjustments(): HasMany
    {
        return $this->hasMany(PayrollAdjustment::class);
    }

    public function cashTransaction(): BelongsTo
    {
        return $this->belongsTo(CashTransaction::class);
    }

    public function employee(): BelongsTo
    {
        return $this->belongsTo(Employee::class);
    }

    public function paidBy(): BelongsTo
    {
        return $this->belongsTo(User::class, 'paid_by_id');
    }

    public function payrollPeriod(): BelongsTo
    {
        return $this->belongsTo(PayrollPeriod::class);
    }
}
