<?php

namespace Modules\Payroll\Services;

use Illuminate\Support\Facades\DB;
use Modules\Core\Exceptions\ApiValidationException;
use Modules\Payroll\DTO\EmployeeAdvancesFilter;
use Modules\Payroll\Enums\EmployeeAdvanceEnum;
use Modules\Payroll\Repositories\EmployeeAdvanceRepository;
use Modules\Payroll\Models\EmployeeAdvance;
use Modules\Payroll\Models\Traits\GenerateAdvanceNoTrait;

class EmployeeAdvanceService
{
    use GenerateAdvanceNoTrait;

    public function __construct(protected EmployeeAdvanceRepository $employeeAdvanceRepo) {}

    public function list(EmployeeAdvancesFilter $filter)
    {
        $columns = [
            'id',
            'employee_id',
            'advance_amount',
            'remaining_amount',
            'installment_months',
            'monthly_deduction',
            'paid_installments',
            'advance_no',
            'issue_date',
            'start_deduction_date',
            'status'
        ];

        $relationships = [
            'employee:id,name,emp_number,phone',
        ];
        return $this->employeeAdvanceRepo->all($filter, $columns, $relationships);
    }

    public function show($id)
    {
        return $this->employeeAdvanceRepo->find($id);
    }

    public function store(array $data)
    {
        $data = $this->calculateAdvanceAmounts($data);

        $data['advance_no'] = $this->generateAdvanceNo();
        return DB::transaction(function () use ($data) {
            return $this->employeeAdvanceRepo->create($data);
        });
    }

    public function update(EmployeeAdvance $employeeAdvance, array $data)
    {
        if ($employeeAdvance->status !== EmployeeAdvanceEnum::PENDING) {
            ApiValidationException::errors([
                'status' => ['Only pending advances can be edited. This advance is already ' . $employeeAdvance->status . '.']
            ]);
        }
        $data = $this->calculateAdvanceAmounts($data);
        return DB::transaction(function () use ($employeeAdvance, $data) {
            return $this->employeeAdvanceRepo->update($employeeAdvance, $data);
        });
    }


    private function calculateAdvanceAmounts(array $data): array
    {
        $data['remaining_amount']  = $data['advance_amount'];
        $data['monthly_deduction'] = round($data['advance_amount'] / $data['installment_months'], 2);

        return $data;
    }

    public function approve(EmployeeAdvance $employeeAdvance)
    {
        if ($employeeAdvance->status !== EmployeeAdvanceEnum::PENDING) {
            ApiValidationException::errors([
                'status' => ['Only pending advances can be approved.']
            ]);
        }

        return DB::transaction(function () use ($employeeAdvance) {
            $employeeAdvance->update([
                'status'      => EmployeeAdvanceEnum::APPROVED,
                'approved_by' => auth()->id(),
                'approved_at' => now(),
            ]);

            return $employeeAdvance->fresh();
        });
    }

    public function cancel(EmployeeAdvance $employeeAdvance)
    {
        if (! in_array($employeeAdvance->status, [EmployeeAdvanceEnum::PENDING, EmployeeAdvanceEnum::APPROVED])) {
            ApiValidationException::errors([
                'status' => ['Only pending or approved advances can be cancelled.']
            ]);
        }

        return DB::transaction(function () use ($employeeAdvance) {
            $employeeAdvance->update([
                'status' => EmployeeAdvanceEnum::CANCELLED,
            ]);

            return $employeeAdvance->fresh();
        });
    }
}
