<?php

namespace Modules\HR\Services;

use Illuminate\Support\Facades\DB;
use Modules\Core\Enums\StatusEnum;
use Modules\Core\Exceptions\ApiValidationException;
use Modules\Employee\Models\Employee;
use Modules\HR\DTO\HrSettingFilter;
use Modules\HR\Enums\AdjustmentDirectionEnum;
use Modules\HR\Enums\RevisionTypeEnum;
use Modules\HR\Enums\SalaryRevisionStatusEnum;
use Modules\HR\Models\SalaryRevision;
use Modules\HR\Models\SalaryStructure;
use Modules\HR\Repositories\SalaryRevisionRepository;
use Modules\HR\Repositories\SalaryStructureRepository;
use Carbon\Carbon;
use Modules\Employee\Repositories\EmployeeRepository;

class SalaryRevisionService
{
    public function __construct(
        protected SalaryRevisionRepository $salaryRevisionRepo,
        protected SalaryStructureRepository $salaryStructureRepo,
        protected EmployeeRepository $employeeRepo,
    ) {}

    public function list(HrSettingFilter $filter)
    {
        $columns = [
            'id',
            'employee_id',
            'revision_type',
            'effective_date',
            'reason',
            'revision_amount',
            'adjustment_direction',
            'new_basic_salary',
            'old_salary_structure_id',
            'salary_revision_status',

        ];

        $relations = [
            'employee:id,name',
            'oldSalaryStructure:id,name',
            'oldSalaryStructure.basicItem:id,salary_structure_id,calculation_value,is_basic'
        ];

        return $this->salaryRevisionRepo->all($filter, $columns, $relations);
    }

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

    public function store(array $data)
    {
        return DB::transaction(function () use ($data) {
            $employee = Employee::with([
                'currentSalaryStructure',
                'designation',
            ])->findOrFail($data['employee_id']);

            $currentStructure = $employee->currentSalaryStructure;
            $currentSalary    = $currentStructure?->basicSalaryAmount() ?? 0;


            $newBasicSalary = $this->calculateNewSalary(
                type: $data['revision_type'],
                current: $currentSalary,
                data: $data,
            );

            if ($employee->designation && $employee->designation->salaryGrade) {
                $grade = $employee->designation->salaryGrade;
                if (
                    $newBasicSalary < (float) $grade->min_salary ||
                    $newBasicSalary > (float) $grade->max_salary
                ) {
                    ApiValidationException::message(
                        "New basic salary must be between {$grade->min_salary} and {$grade->max_salary} " .
                            "as per '{$grade->name}' salary grade."
                    );
                }
            }

            $payload = [
                'employee_id'             => $data['employee_id'],
                'revision_type'           => $data['revision_type'],
                'effective_date'          => $data['effective_date'],
                'reason'                  => $data['reason'] ?? null,

                // Snapshot (apply করার আগের values)
                'old_basic_salary'        => $currentSalary,
                'old_salary_structure_id' => $currentStructure?->id,
                'old_designation_id'      => $employee->designation_id,

                // Calculated new values
                'new_basic_salary'        => $newBasicSalary,
                'new_designation_id'      => $data['new_designation_id'] ?? null,

                // Type-specific
                'revision_amount' => !empty($data['revision_amount'])
                    ? $data['revision_amount']
                    : $data['new_basic_salary'],

                'adjustment_direction'    => $data['adjustment_direction'] ?? null,
                'clone_salary_structure'  => $data['clone_salary_structure'] ?? false,

                // Status সবসময় pending দিয়ে শুরু
                'salary_revision_status'  => SalaryRevisionStatusEnum::PENDING,

            ];

            return $this->salaryRevisionRepo->create($payload);
        });
    }


    private function calculateNewSalary(
        string $type,
        float  $current,
        array  $data
    ): float {

        return match ($type) {
            // Increment: বর্তমান + পরিমাণ
            RevisionTypeEnum::INCREMENT =>
            $current + $data['revision_amount'],

            // Adjustment: বাড়বে বা কমবে
            RevisionTypeEnum::ADJUSTMENT =>
            $data['adjustment_direction'] === AdjustmentDirectionEnum::INCREASE
                ? $current + $data['revision_amount']
                : $current - $data['revision_amount'],

            // Correction: সরাসরি নতুন value বসবে
            RevisionTypeEnum::CORRECTION =>
            $data['new_basic_salary'],

            // Promotion: নতুন salary সরাসরি দেওয়া হয়
            RevisionTypeEnum::PROMOTION =>
            $data['new_basic_salary'],

            default => $current,
        };
    }


    public function approve(SalaryRevision $revision, array $data)
    {
        if ($revision->salary_revision_status !== SalaryRevisionStatusEnum::PENDING) {
            ApiValidationException::message("Only pending revisions can be approved.");
        }
        return $this->salaryRevisionRepo->approve($revision, $data);
    }

    public function reject(SalaryRevision $revision, array $data)
    {
        if ($revision->salary_revision_status !== SalaryRevisionStatusEnum::PENDING && $revision->salary_revision_status !== SalaryRevisionStatusEnum::APPROVED) {
            ApiValidationException::message("Only pending revisions can be rejected.");
        }
        return $this->salaryRevisionRepo->reject($revision, $data);
    }



    // ── Apply ─────────────────────────────────────────────────────────

    public function apply(SalaryRevision $revision)
    {
        if ($revision->salary_revision_status !== SalaryRevisionStatusEnum::APPROVED) {
            ApiValidationException::message("Only approved revisions can be applied.");
        }

        DB::transaction(function () use ($revision) {

            $isPromotion = $revision->revision_type === RevisionTypeEnum::PROMOTION;

            if ($isPromotion) {

                if ($revision->clone_salary_structure) {
                    // clone=true → পুরনো lock + নতুন structure + সব items copy
                    if ($revision->old_salary_structure_id) {
                        SalaryStructure::where('id', $revision->old_salary_structure_id)
                            ->update([
                                'effective_to' => Carbon::parse($revision->effective_date)->subDay(),
                                'is_locked'    => true,
                                'status'       => StatusEnum::INACTIVE,
                            ]);
                    }

                    $newStructure = SalaryStructure::create([
                        'employee_id'    => $revision->employee_id,
                        'name'           => 'Revision #' . $revision->id,
                        'effective_from' => $revision->effective_date,
                        'effective_to'   => null,
                        'is_locked'      => false,
                        'status'         => StatusEnum::ACTIVE,
                    ]);

                    $this->salaryStructureRepo->copyItems(
                        $revision->old_salary_structure_id,
                        $newStructure->id,
                        $revision->new_basic_salary,
                    );
                } else {

                    // clone=false → lock নেই, নতুন structure নেই
                    // শুধু পুরনো structure-এর basic item update

                    $newStructure = null;

                    $this->salaryStructureRepo->updateBasicSalary(
                        $revision->old_salary_structure_id,
                        $revision->new_basic_salary,
                    );
                }

                $this->employeeRepo->updateCurrentSalary(
                    $revision->employee_id,
                    $revision->new_basic_salary,
                    $revision->new_designation_id
                );
                // ⑤ Revision applied mark করো
                $this->salaryRevisionRepo->apply($revision, $newStructure);
            } else {

                // ── INCREMENT / ADJUSTMENT / CORRECTION ───────────────

                 $employee = Employee::with('designation.salaryGrade')
                    ->findOrFail($revision->employee_id);



                // নতুন structure তৈরি হবে না
                // existing structure-এর basic item-এর value update হবে


                $this->salaryStructureRepo->updateBasicSalary(
                    $revision->old_salary_structure_id,
                    $revision->new_basic_salary,
                );


                $this->employeeRepo->updateCurrentSalary(
                    $revision->employee_id,
                    $revision->new_basic_salary,
                    null
                );

                // Revision applied mark করো (newStructure নেই, তাই null)
                $this->salaryRevisionRepo->apply($revision, null);
            }
        });
    }
}
