<?php
declare(strict_types=1);

namespace App\Modules\HR\Services;

use App\Core\App;
use App\Core\EventBus;
use App\Core\Logger;
use App\Modules\HR\Models\HrEmployeeLoan;
use App\Modules\HR\Models\HrLoanInstallment;
use App\Modules\HR\Services\HrNumberGenerator;

/**
 * Loan & Salary Advance Service
 *
 * Handles loan creation, installment scheduling, approval workflow,
 * and payroll deduction integration.
 */
final class LoanService
{
    public static function create(array $data): array
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        $profileId = (int) ($data['employee_profile_id'] ?? 0);
        $loanAmount = $data['loan_amount'] ?? '0.00';
        $numInstallments = (int) ($data['number_of_installments'] ?? 1);

        if ($profileId <= 0) {
            return ['success' => false, 'error' => 'الموظف غير محدد'];
        }
        if (bccomp($loanAmount, '0.01', 2) < 0) {
            return ['success' => false, 'error' => 'مبلغ السلفة يجب أن يكون أكبر من صفر'];
        }
        if ($numInstallments < 1 || $numInstallments > 60) {
            return ['success' => false, 'error' => 'عدد الأقساط يجب أن يكون بين 1 و 60'];
        }

        // Calculate installment amount
        $installmentAmount = bcdiv($loanAmount, (string) $numInstallments, 2);

        // Adjust last installment for rounding
        $totalScheduled = bcmul($installmentAmount, (string) $numInstallments, 2);
        $lastInstallmentAdj = bcsub($loanAmount, bcmul($installmentAmount, (string) ($numInstallments - 1), 2), 2);

        $loanNumber = HrNumberGenerator::generateLoanReference();

        $db->beginTransaction();
        try {
            $loanId = $db->insert('hr_employee_loans', [
                'employee_profile_id'    => $profileId,
                'loan_number'            => $loanNumber,
                'loan_type'              => $data['loan_type'] ?? 'salary_advance',
                'loan_amount'            => $loanAmount,
                'installment_amount'     => $installmentAmount,
                'number_of_installments' => $numInstallments,
                'remaining_amount'       => $loanAmount,
                'paid_installments'      => 0,
                'request_date'           => $data['request_date'] ?? date('Y-m-d'),
                'start_deduction_date'   => $data['start_deduction_date'] ?? date('Y-m-01', strtotime('+1 month')),
                'reason'                 => $data['reason'] ?? null,
                'status'                 => 'pending',
                'notes'                  => $data['notes'] ?? null,
                'created_at'             => date('Y-m-d H:i:s'),
                'updated_at'             => date('Y-m-d H:i:s'),
                'created_by'             => $employee ? (int) $employee->id : null,
            ]);

            // Create installment schedule
            $startDate = $data['start_deduction_date'] ?? date('Y-m-01', strtotime('+1 month'));
            for ($i = 1; $i <= $numInstallments; $i++) {
                $dueDate = date('Y-m-d', strtotime($startDate . ' +' . ($i - 1) . ' months'));
                $amount = ($i === $numInstallments) ? $lastInstallmentAdj : $installmentAmount;

                $db->insert('hr_loan_installments', [
                    'loan_id'            => $loanId,
                    'installment_number' => $i,
                    'due_date'           => $dueDate,
                    'amount'             => $amount,
                    'status'             => 'pending',
                    'created_at'         => date('Y-m-d H:i:s'),
                    'updated_at'         => date('Y-m-d H:i:s'),
                ]);
            }

            $db->commit();

            Logger::info("Loan created", ['loan_id' => $loanId, 'profile_id' => $profileId, 'amount' => $loanAmount]);
            return ['success' => true, 'loan_id' => $loanId];

        } catch (\Throwable $e) {
            $db->rollBack();
            Logger::error("Loan creation failed: " . $e->getMessage());
            return ['success' => false, 'error' => 'فشل إنشاء السلفة: ' . $e->getMessage()];
        }
    }

    public static function approve(int $loanId): array
    {
        $db = App::getInstance()->db();
        $employee = App::getInstance()->currentEmployee();

        $loan = $db->selectOne("SELECT * FROM hr_employee_loans WHERE id = ? AND is_archived = 0", [$loanId]);
        if (!$loan || $loan['status'] !== 'pending') {
            return ['success' => false, 'error' => 'السلفة غير موجودة أو غير قابلة للاعتماد'];
        }

        $db->update('hr_employee_loans', [
            'status'      => 'approved',
            'approved_by' => $employee ? (int) $employee->id : null,
            'approved_at' => date('Y-m-d H:i:s'),
            'updated_at'  => date('Y-m-d H:i:s'),
        ], '`id` = ?', [$loanId]);

        EventBus::dispatch('hr.loan.approved', [
            'loan_id'     => $loanId,
            'employee_id' => (int) $loan['employee_profile_id'],
            'loan_amount' => $loan['loan_amount'],
        ]);

        return ['success' => true];
    }

    public static function disburse(int $loanId): array
    {
        $db = App::getInstance()->db();

        $loan = $db->selectOne("SELECT * FROM hr_employee_loans WHERE id = ? AND is_archived = 0", [$loanId]);
        if (!$loan || $loan['status'] !== 'approved') {
            return ['success' => false, 'error' => 'السلفة غير معتمدة'];
        }

        $db->update('hr_employee_loans', [
            'status'         => 'disbursed',
            'disbursed_date'     => date('Y-m-d'),
            'updated_at'     => date('Y-m-d H:i:s'),
        ], '`id` = ?', [$loanId]);

        return ['success' => true];
    }

    public static function processInstallmentDeduction(int $loanId, string $amount, ?int $payrollRunId = null): array
    {
        $db = App::getInstance()->db();

        $loan = $db->selectOne("SELECT * FROM hr_employee_loans WHERE id = ?", [$loanId]);
        if (!$loan || !in_array($loan['status'], ['disbursed', 'repaying'], true)) {
            return ['success' => false, 'error' => 'السلفة غير قابلة للخصم'];
        }

        $nextInstallment = HrLoanInstallment::getNextPending($loanId);
        if (!$nextInstallment) {
            return ['success' => false, 'error' => 'لا توجد أقساط معلقة'];
        }

        $db->beginTransaction();
        try {
            HrLoanInstallment::markDeducted((int) $nextInstallment['id'], $payrollRunId);

            $newRemaining = bcsub($loan['remaining_amount'], $amount, 2);
            $newPaid = (int) $loan['paid_installments'] + 1;

            $newStatus = 'repaying';
            if (bccomp($newRemaining, '0.01', 2) < 0 || $newPaid >= (int) $loan['number_of_installments']) {
                $newStatus = 'settled';
                $newRemaining = '0.00';
            }

            $db->update('hr_employee_loans', [
                'remaining_amount'  => $newRemaining,
                'paid_installments' => $newPaid,
                'status'            => $newStatus,
                'updated_at'        => date('Y-m-d H:i:s'),
            ], '`id` = ?', [$loanId]);

            $db->commit();
            return ['success' => true, 'remaining' => $newRemaining, 'status' => $newStatus];

        } catch (\Throwable $e) {
            $db->rollBack();
            return ['success' => false, 'error' => $e->getMessage()];
        }
    }
}
