<?php

namespace App\Services;

use App\Models\AuditLog;
use App\Models\Employee;
use App\Models\EmployeeGratuity;
use App\Models\FinalSettlement;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class GratuityService
{
    /**
     * Produces a reviewable estimate under the traditional UAE private-sector
     * gratuity method. Alternative savings-scheme members must be handled by
     * their approved fund statement rather than this estimate.
     */
    public function estimate(Employee $employee, array $data, User $actor): EmployeeGratuity
    {
        $salary = $employee->currentSalary()->first();
        if (! $salary) {
            throw ValidationException::withMessages(['gratuity' => 'An effective salary structure is required before gratuity can be estimated.']);
        }
        if (! $employee->joining_date) {
            throw ValidationException::withMessages(['gratuity' => 'The employee joining date is required.']);
        }

        $lastDay = Carbon::parse($data['last_working_date'])->startOfDay();
        if ($lastDay->lessThan($employee->joining_date)) {
            throw ValidationException::withMessages(['last_working_date' => 'Last working date cannot be earlier than joining date.']);
        }

        $excludedDays = (int) ($data['excluded_days'] ?? 0);
        $serviceDays = max(0, $employee->joining_date->diffInDays($lastDay) + 1 - $excludedDays);
        $basic = (float) $salary->basic_salary;
        $workRatio = (float) ($data['work_ratio'] ?? 1);
        $eligible = $serviceDays >= 365;
        $firstFiveDays = min($serviceDays, 365 * 5);
        $laterDays = max(0, $serviceDays - (365 * 5));
        $dailyBasic = $basic / 30;
        $amount = $eligible
            ? (($firstFiveDays / 365) * 21 * $dailyBasic + ($laterDays / 365) * 30 * $dailyBasic) * $workRatio
            : 0;
        $cap = $basic * 24;
        $amount = round(min($amount, $cap), 2);

        $gratuity = EmployeeGratuity::create([
            'tenant_id' => $employee->tenant_id,
            'employee_id' => $employee->id,
            'joining_date' => $employee->joining_date,
            'last_working_date' => $lastDay,
            'basic_salary' => $basic,
            'eligible_service_days' => $serviceDays,
            'excluded_days' => $excludedDays,
            'termination_reason' => $data['termination_reason'] ?? null,
            'estimated_amount' => $amount,
            'calculation_snapshot' => [
                'method' => 'uae_private_sector_traditional_gratuity',
                'official_basis' => 'Federal Decree-Law No. 33 of 2021 and implementing rules',
                'first_five_years_days_per_year' => 21,
                'later_years_days_per_year' => 30,
                'minimum_service_days' => 365,
                'maximum_months_basic_salary' => 24,
                'work_ratio' => $workRatio,
                'daily_basic_salary' => round($dailyBasic, 4),
                'alternative_savings_scheme_excluded' => true,
            ],
            'status' => 'estimated',
            'calculated_by' => $actor->id,
        ]);
        AuditLog::record('gratuity', 'gratuity.estimated', $gratuity, null, $gratuity->toArray(), $actor);

        return $gratuity;
    }

    public function approve(EmployeeGratuity $gratuity, float $approvedAmount, User $actor): EmployeeGratuity
    {
        if ($gratuity->status !== 'estimated') {
            throw ValidationException::withMessages(['gratuity' => 'Only an estimate can be approved.']);
        }
        $gratuity->update(['approved_amount' => $approvedAmount, 'status' => 'approved', 'approved_by' => $actor->id, 'approved_at' => now()]);
        AuditLog::record('gratuity', 'gratuity.approved', $gratuity, null, ['approved_amount' => $approvedAmount], $actor);

        return $gratuity;
    }

    public function createFinalSettlement(EmployeeGratuity $gratuity, array $data, User $actor): FinalSettlement
    {
        if ($gratuity->status !== 'approved') {
            throw ValidationException::withMessages(['gratuity' => 'Approve the gratuity before creating a final settlement.']);
        }

        return DB::transaction(function () use ($gratuity, $data, $actor) {
            $gratuityAmount = (float) ($gratuity->approved_amount ?? $gratuity->estimated_amount);
            $salaryDue = (float) ($data['salary_due'] ?? 0);
            $leaveSalary = (float) ($data['leave_salary'] ?? 0);
            $otherEarnings = (float) ($data['other_earnings'] ?? 0);
            $loanBalance = (float) $gratuity->employee->loans()->where('status', 'active')->sum('remaining_amount');
            $otherDeductions = (float) ($data['other_deductions'] ?? 0);
            $net = round($salaryDue + $leaveSalary + $gratuityAmount + $otherEarnings - $loanBalance - $otherDeductions, 2);
            $settlement = FinalSettlement::create([
                'tenant_id' => $gratuity->tenant_id,
                'employee_id' => $gratuity->employee_id,
                'employee_gratuity_id' => $gratuity->id,
                'salary_due' => $salaryDue,
                'leave_salary' => $leaveSalary,
                'gratuity_amount' => $gratuityAmount,
                'other_earnings' => $otherEarnings,
                'loan_balance' => $loanBalance,
                'other_deductions' => $otherDeductions,
                'net_settlement' => $net,
                'created_by' => $actor->id,
            ]);
            AuditLog::record('gratuity', 'settlement.created', $settlement, null, $settlement->toArray(), $actor);

            return $settlement;
        });
    }
}
