<?php

namespace Modules\Payroll\Services;

use Illuminate\Support\Facades\DB;
use Modules\Core\Exceptions\ApiValidationException;
use Modules\Payroll\DTO\EmployeeLoansFilter;
use Modules\Payroll\Enums\EmployeeLoansEnum;
use Modules\Payroll\Repositories\EmployeeLoanRepository;
use Modules\Payroll\Models\EmployeeLoan;
use Modules\Payroll\Models\Traits\GenerateLoanNoTrait;

class EmployeeLoanService
{
    use GenerateLoanNoTrait;

    public function __construct(protected EmployeeLoanRepository $employeeLoanRepo) {}

    public function list(EmployeeLoansFilter $filter)
    {
        $columns = [
            'id',
            'employee_id',
            'loan_no',
            'loan_type',
            'principal_amount',
            'interest_rate',
            'installment_months',
            'interest_amount',
            'total_payable',
            'remaining_amount',
            'monthly_installment',
            'paid_installments',
            'issue_date',
            'start_deduction_date',
            'status'
        ];

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

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

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

        $data['loan_no'] = $this->generateLoanNo();
        return DB::transaction(function () use ($data) {
            return $this->employeeLoanRepo->create($data);
        });
    }

    public function update(EmployeeLoan $employeeLoan, array $data)
    {
        if ($employeeLoan->status !== EmployeeLoansEnum::PENDING) {
            ApiValidationException::errors([
                'status' => ['Only pending loans can be edited. This loan is already ' . $employeeLoan->status . '.']
            ]);
        }
        $data = $this->calculateLoanAmounts($data);
        return DB::transaction(function () use ($employeeLoan, $data) {
            return $this->employeeLoanRepo->update($employeeLoan, $data);
        });
    }


    private function calculateLoanAmounts(array $data): array
    {
        $interestAmount = $data['principal_amount'] * (($data['interest_rate'] ?? 0) / 100);
        $totalPayable = $data['principal_amount'] + $interestAmount;
        $data['interest_amount']     = $interestAmount;
        $data['total_payable']       = $totalPayable;
        $data['remaining_amount']    = $totalPayable;
        $data['monthly_installment'] = round($totalPayable / $data['installment_months'], 2);

        return $data;
    }

    public function approve(EmployeeLoan $employeeLoan)
    {
        if ($employeeLoan->status !== EmployeeLoansEnum::PENDING) {
            ApiValidationException::errors([
                'status' => ['Only pending loans can be approved.']
            ]);
        }

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

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

    public function cancel(EmployeeLoan $employeeLoan)
    {
        if (! in_array($employeeLoan->status, [EmployeeLoansEnum::PENDING, EmployeeLoansEnum::APPROVED])) {
            ApiValidationException::errors([
                'status' => ['Only pending or approved loans can be cancelled. Running loans cannot be cancelled.']
            ]);
        }

        return DB::transaction(function () use ($employeeLoan) {
            $employeeLoan->update([
                'status' => EmployeeLoansEnum::Cancelled,
            ]);

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