<?php

namespace App\Services;

use App\Models\Bonus;
use App\Models\BonusGeneration;
use App\Models\BonusItem;
use App\Models\EmployeeSalaryAssignment;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\DB;

class BonusEligibilityService
{
    /**
     * Get all employees eligible for the given bonus.
     */
    public function getEligibleEmployees(Bonus $bonus): Collection
    {
        $rules = $bonus->eligibility_type ?? [];

        $query = User::where('type', 'employee')
            ->where('status', 'active')
            ->with(['employeeProfile.departmentRel']);

        if (! empty($rules['department_ids'])) {
            $query->whereHas('employeeProfile', function ($q) use ($rules) {
                $q->whereIn('department_id', $rules['department_ids']);
            });
        }

        if (! empty($rules['gender'])) {
            $query->whereHas('employeeProfile', function ($q) use ($rules) {
                $q->whereIn('gender', $rules['gender']);
            });
        }

        if (! empty($rules['min_service_months'])) {
            $cutoff = Carbon::now()->subMonths($rules['min_service_months'])->toDateString();
            $query->whereHas('employeeProfile', function ($q) use ($cutoff) {
                $q->where('joining_date', '<=', $cutoff);
            });
        }

        return $query->get();
    }

    /**
     * Generate bonus items for eligible employees and persist them.
     */
    /**
     * Generate bonus items for eligible (or selected) employees.
     *
     * @param  array<int>|null  $selectedEmployeeIds  null = all eligible
     */
    public function generateBonus(Bonus $bonus, string $month, User $actor, ?array $selectedEmployeeIds = null, ?int $generationId = null): BonusGeneration
    {
        return DB::transaction(function () use ($bonus, $month, $actor, $selectedEmployeeIds, $generationId) {
            $monthDate = Carbon::parse($month)->startOfMonth()->toDateString();

            $generation = null;
            if ($generationId) {
                $generation = BonusGeneration::findOrFail($generationId);
            }

            if ($generation) {
                if ($generation->status !== 'draft') {
                    throw new \Exception('Only draft bonus generations can be regenerated/edited.');
                }
                $generation->update(['generated_by' => $actor->id]);
                $generation->items()->delete();
            } else {
                $generation = BonusGeneration::create([
                    'bonus_id' => $bonus->id,
                    'month' => $monthDate,
                    'status' => 'draft',
                    'generated_by' => $actor->id,
                ]);
            }

            $employees = $this->getEligibleEmployees($bonus);

            if ($selectedEmployeeIds !== null) {
                $employees = $employees->filter(fn ($e) => in_array($e->id, $selectedEmployeeIds, true));
            }

            foreach ($employees as $employee) {
                $amount = $this->calculateBonusAmount($bonus, $employee);

                BonusItem::create([
                    'bonus_generation_id' => $generation->id,
                    'employee_id' => $employee->id,
                    'bonus_amount' => $amount,
                    'breakdown' => [
                        'bonus_name' => $bonus->name,
                        'amount_type' => $bonus->amount_type,
                        'amount' => (float) $bonus->amount,
                        'calculated_amount' => $amount,
                    ],
                ]);
            }

            return $generation;
        });
    }

    /**
     * Approve a bonus generation.
     */
    public function approve(BonusGeneration $generation, User $actor): void
    {
        $generation->update([
            'status' => 'approved',
            'approved_by' => $actor->id,
            'approved_at' => now(),
        ]);
    }

    private function calculateBonusAmount(Bonus $bonus, User $employee): float
    {
        if ($bonus->amount_type === 'fixed') {
            return (float) $bonus->amount;
        }

        // Percentage of current gross salary
        $assignment = EmployeeSalaryAssignment::where('employee_id', $employee->id)
            ->orderByDesc('effective_from')
            ->first();

        if (! $assignment) {
            return 0;
        }

        return round((float) $assignment->gross_salary * ((float) $bonus->amount / 100), 2);
    }
}
