<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use App\Models\Department;
use App\Models\Designation;
use App\Models\Document;
use App\Models\Location;
use App\Models\SalaryRevision;
use App\Models\SalaryGroup;
use App\Models\Role;
use App\Models\Shift;
use App\Models\User;
use App\Services\Payroll\SalaryStructureResolver;
use App\Services\Payroll\V2\EmployeePayrollCalculator;
use App\Services\Payroll\V2\PayrollRunService;
use App\Services\PayrollService;
use App\Services\SalaryRevisionService;
use App\Services\TenantRoleService;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rule;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Throwable;

class EmployeeController extends Controller
{
    public function __construct(
        protected PayrollRunService $payrollRunService,
        protected PayrollService $payrollService,
        protected SalaryRevisionService $salaryRevisionService,
        protected EmployeePayrollCalculator $employeePayrollCalculator,
    ) {}

    public function index(Request $request)
    {
        $companyId = Auth::user()?->company_id;
        $query = User::with(['department', 'designation', 'reportingManager', 'documents'])
            ->forTenantDirectory();
        if ($companyId) {
            $query->where('company_id', $companyId);
        }

        if ($request->filled('search')) {
            $query->where(function ($q) use ($request) {
                $q->where('name', 'like', '%' . $request->search . '%')
                    ->orWhere('employee_number', 'like', '%' . $request->search . '%')
                    ->orWhere('email', 'like', '%' . $request->search . '%');
            });
        }
        if ($request->filled('department_id')) {
            $query->where('department_id', $request->department_id);
        }
        if ($request->filled('designation_id')) {
            $query->where('designation_id', $request->designation_id);
        }
        $statusFilter = $this->applyEmployeeStatusFilter($query, $request);

        $employees = $query->orderBy('name')->paginate(16)->withQueryString();
        $documentComplianceMap = [];
        foreach ($employees->items() as $emp) {
            $documentComplianceMap[$emp->id] = $this->buildDocumentCompliance($emp->documents);
        }
        $departments = Department::orderBy('name')->get();
        $designations = Designation::orderBy('name')->get();

        return view('employees.index', compact('employees', 'departments', 'designations', 'documentComplianceMap', 'statusFilter'));
    }

    public function export(Request $request): StreamedResponse
    {
        $companyId = Auth::user()?->company_id;
        $query = User::with(['department', 'designation', 'reportingManager'])
            ->forTenantDirectory();

        if ($companyId) {
            $query->where('company_id', $companyId);
        }

        if ($request->filled('search')) {
            $query->where(function ($q) use ($request) {
                $q->where('name', 'like', '%' . $request->search . '%')
                    ->orWhere('employee_number', 'like', '%' . $request->search . '%')
                    ->orWhere('email', 'like', '%' . $request->search . '%');
            });
        }
        if ($request->filled('department_id')) {
            $query->where('department_id', $request->department_id);
        }
        if ($request->filled('designation_id')) {
            $query->where('designation_id', $request->designation_id);
        }
        $this->applyEmployeeStatusFilter($query, $request);

        $employees = $query->orderBy('name')->get();
        $fileName = 'employees-' . now()->format('Ymd-His') . '.csv';

        return response()->streamDownload(function () use ($employees) {
            $handle = fopen('php://output', 'w');
            fputcsv($handle, ['Employee ID', 'Name', 'Email', 'Department', 'Designation', 'Manager', 'Status']);

            foreach ($employees as $emp) {
                fputcsv($handle, [
                    $emp->employee_number ?? '',
                    $emp->name ?? '',
                    $emp->email ?? '',
                    $emp->department?->name ?? '',
                    $emp->designation?->name ?? '',
                    $emp->reportingManager?->name ?? '',
                    $emp->status ?? '',
                ]);
            }

            fclose($handle);
        }, $fileName, ['Content-Type' => 'text/csv']);
    }

    public function create()
    {
        $companyId = Auth::user()?->company_id;
        $departments = Department::orderBy('name')->get();
        $designations = Designation::orderBy('name')->get();
        $locations = Location::orderBy('name')->get();
        $shifts = Shift::orderBy('name')->get();
        $salaryGroupsQuery = SalaryGroup::query();
        if ($companyId) {
            $salaryGroupsQuery->where('company_id', $companyId);
        }
        $salaryGroups = $salaryGroupsQuery->orderBy('name')->get();
        $managersQuery = User::forActiveDirectory();
        if ($companyId) {
            $managersQuery->where('company_id', $companyId);
        }
        $managers = $managersQuery->orderBy('name')->get();
        $roles = Role::query()->orderBy('name')->get();
        return view('employees.create', compact('departments', 'designations', 'locations', 'shifts', 'salaryGroups', 'managers', 'roles'));
    }

    public function store(Request $request)
    {
        $companyId = Auth::user()?->company_id;
        $validated = $request->validate([
            'name' => 'required|string|max:191',
            'email' => 'required|email|unique:users,email',
            'password' => 'required|string|min:8',
            'employee_number' => [
                'nullable',
                'string',
                'max:50',
                Rule::unique('users', 'employee_number')
                    ->where(fn ($query) => $companyId ? $query->where('company_id', $companyId) : $query),
            ],
            'phone' => 'nullable|string|max:50',
            'gender' => 'nullable|in:male,female,other',
            'dob' => 'nullable|date',
            'father_name' => 'nullable|string|max:191',
            'joining_date' => 'nullable|date',
            'registration_date' => 'nullable|date',
            'confirmation_date' => 'nullable|date',
            'probation_end_date' => 'nullable|date',
            'last_working_date' => 'nullable|date',
            'department_id' => 'nullable|exists:departments,id',
            'designation_id' => 'nullable|exists:designations,id',
            'location_id' => 'nullable|exists:locations,id',
            'shift_id' => 'nullable|exists:shifts,id',
            'salary_group_id' => 'nullable|exists:salary_groups,id',
            'report_to' => 'nullable|exists:users,id',
            'annual_ctc' => 'nullable|numeric|min:0',
            'account_holder_name' => 'nullable|string|max:191',
            'bank_name' => 'nullable|string|max:191',
            'account_number' => 'nullable|string|max:50',
            'branch_name' => 'nullable|string|max:191',
            'city' => 'nullable|string|max:100',
            'ifsc_code' => 'nullable|string|max:20',
            'pan_number' => 'nullable|string|max:20',
            'uan_number' => 'nullable|string|max:30',
            'pf_join_date' => 'nullable|date',
            'work_state' => 'nullable|string|max:100',
            'pt_location' => 'nullable|string|max:191',
            'role_id' => array_merge(['nullable', 'integer'], $companyId ? [Rule::exists('roles', 'id')->where('company_id', (int) $companyId)] : []),
        ]);
        $validated['company_id'] = $companyId;
        $validated['user_type'] = User::USER_TYPE_STAFF;
        $validated['status'] = 'active';
        $validated['password'] = Hash::make($validated['password']);
        $manualEmployeeNumber = trim((string) ($validated['employee_number'] ?? ''));
        $validated['employee_number'] = $manualEmployeeNumber !== '' ? $manualEmployeeNumber : null;
        if (! Auth::user()->hasPermission('users_edit')) {
            unset($validated['role_id']);
        }
        if (Auth::user()->hasPermission('users_edit') && ! empty($validated['role_id'])) {
            // use requested role
        } elseif ($companyId) {
            $validated['role_id'] = TenantRoleService::ensureEmployeeRoleForCompany((int) $companyId)->id;
        }
        $initialAnnualCtc = (float) ($validated['annual_ctc'] ?? 0);
        unset($validated['annual_ctc']);
        $employee = DB::transaction(function () use ($companyId, $validated) {
            $payload = $validated;

            if (empty($payload['employee_number'])) {
                $payload['employee_number'] = $this->nextEmployeeNumber($companyId);
            }

            return User::create($payload);
        });
        if ($initialAnnualCtc > 0 && !empty($employee->id) && !empty($employee->salary_group_id)) {
            $this->salaryRevisionService->createSalaryRevision((int)$companyId, $employee->id, $employee->salary_group_id, $initialAnnualCtc);
        }
        return redirect()->route('employees.show', $employee->id)->with('success', 'Employee created successfully.');
    }

    public function show(int $employee)
    {
        $companyId = Auth::user()?->company_id;
        $query = User::with(['department', 'designation', 'location', 'shift', 'salaryGroup.salaryGroupComponents.salaryComponent', 'reportingManager'])
            ->forTenantDirectory();
        if ($companyId) {
            $query->where('company_id', $companyId);
        }
        $employee = $query->findOrFail($employee);
        $attendances = $employee->attendances()->orderByDesc('date')->limit(30)->get();
        $leaves = $employee->leaves()->with('leaveType')->orderByDesc('start_date')->limit(20)->get();
        $payrolls = \App\Models\Payroll::where('user_id', $employee->id)->orderByDesc('year')->orderByDesc('month')->limit(24)->get();
        $documents = $employee->documents()->orderByDesc('created_at')->get();
        $uploadCategories = $this->getUploadCategories();
        $documentCompliance = $this->buildDocumentCompliance($documents);
        $assets = \App\Models\Asset::where('assigned_to', $employee->id)->with('assetType')->get();
        $latestRevision = SalaryRevision::withoutGlobalScopes()
            ->where('user_id', $employee->id)
            ->when($companyId, fn ($q) => $q->where('company_id', $companyId))
            ->orderByDesc('effective_date')
            ->orderByDesc('id')
            ->first();
        $salaryStructure = $this->buildSalaryStructureForEmployee($employee, $latestRevision);
        $salaryBreakup = null;
        if ($companyId) {
            $settings = $this->payrollRunService->getStatutorySettings((int) $companyId);
            $ptByState = $this->payrollRunService->getPtSlabsByState((int) $companyId, now());
            $ptSlabs = $ptByState[$employee->work_state ?? ''] ?? [];
            $salaryBreakup = $this->employeePayrollCalculator->indicativeBreakdown(
                $employee,
                (int) $companyId,
                now(),
                $settings,
                $ptSlabs
            );
        }
        $salaryGroups = SalaryGroup::query()
            ->when($companyId, fn ($q) => $q->where('company_id', $companyId))
            ->with('salaryGroupComponents.salaryComponent')
            ->orderBy('name')
            ->get();

        return view('employees.show', compact('employee', 'attendances', 'leaves', 'payrolls', 'documents', 'assets', 'uploadCategories', 'documentCompliance', 'salaryStructure', 'salaryBreakup', 'latestRevision', 'salaryGroups'));
    }

    public function salaryBreakup($salaryRevisionId){
        $companyId = auth()->user()->company_id;
        $employee = auth()->user();

        $settings = $this->payrollRunService->getStatutorySettings((int) $companyId);
        $ptByState = $this->payrollRunService->getPtSlabsByState((int) $companyId, now());
        $ptSlabs = $ptByState[$employee->work_state ?? ''] ?? [];
        $salaryBreakup = $this->employeePayrollCalculator->indicativeBreakdown(
            $employee,
            (int) $companyId,
            now(),
            $settings,
            $ptSlabs,
            $salaryRevisionId
        );

        return view('employees.salary-breakup', compact('employee', 'salaryBreakup'));
    }

    public function storeSalaryRevision(Request $request, int $employee)
    {
        $companyId = Auth::user()?->company_id;

        $validated = $request->validate([
            'effective_date' => 'required|date',
            'ctc_annual' => 'required|numeric|min:0',
            'salary_group' => [
                'required',
                Rule::exists('salary_groups', 'id')->when($companyId, fn ($rule) => $rule->where('company_id', $companyId)),
            ],
        ]);

        $employeeRow = User::query()
            ->with(['salaryGroup.salaryGroupComponents.salaryComponent'])
            ->forTenantDirectory()
            ->where('id', $employee)
            ->when($companyId, fn ($q) => $q->where('company_id', $companyId))
            ->firstOrFail();

        $initialAnnualCtc = (float) $validated['ctc_annual'];
        $salary_group_id = $request->input('salary_group');
        $effective_date = $validated['effective_date'];

        $employeeRow->update([
            'salary_group_id' => $salary_group_id,
        ]);

        $this->salaryRevisionService->createSalaryRevision((int)$companyId, $employeeRow->id, $employeeRow->salary_group_id, $initialAnnualCtc, $effective_date);

        return redirect()->route('employees.show', $employeeRow->id)->with('success', 'Salary revision saved successfully.');
    }

    public function edit(int $employee)
    {
        $companyId = Auth::user()?->company_id;
        $query = User::forTenantDirectory();
        if ($companyId) {
            $query->where('company_id', $companyId);
        }
        $employee = $query->findOrFail($employee);
        $departments = Department::orderBy('name')->get();
        $designations = Designation::orderBy('name')->get();
        $locations = Location::orderBy('name')->get();
        $shifts = Shift::orderBy('name')->get();
        $salaryGroupsQuery = SalaryGroup::query();
        if ($companyId) {
            $salaryGroupsQuery->where('company_id', $companyId);
        }
        $salaryGroups = $salaryGroupsQuery->orderBy('name')->get();
        $managersQuery = User::forActiveDirectory()->where('id', '!=', $employee->id);
        if ($companyId) {
            $managersQuery->where('company_id', $companyId);
        }
        $managers = $managersQuery->orderBy('name')->get();
        $roles = Role::query()->orderBy('name')->get();
        $canFullyManageEmployee = $this->canFullyManageEmployee(Auth::user());

        return view('employees.edit', compact('employee', 'departments', 'designations', 'locations', 'shifts', 'salaryGroups', 'managers', 'roles', 'canFullyManageEmployee'));
    }

    public function update(Request $request, int $employee)
    {
        $companyId = Auth::user()?->company_id;
        $query = User::forTenantDirectory();
        if ($companyId) {
            $query->where('company_id', $companyId);
        }
        $employee = $query->findOrFail($employee);
        $oldSalaryGroupId = $employee->salary_group_id;
        $validated = $request->validate([
            'name' => 'required|string|max:191',
            'email' => 'required|email|unique:users,email,' . $employee->id,
            'password' => 'nullable|string|min:8',
            'employee_number' => [
                'nullable',
                'string',
                'max:50',
                Rule::unique('users', 'employee_number')
                    ->ignore($employee->id)
                    ->where(fn ($query) => $companyId ? $query->where('company_id', $companyId) : $query),
            ],
            'phone' => 'nullable|string|max:50',
            'gender' => 'nullable|in:male,female,other',
            'dob' => 'nullable|date',
            'father_name' => 'nullable|string|max:191',
            'joining_date' => 'nullable|date',
            'registration_date' => 'nullable|date',
            'confirmation_date' => 'nullable|date',
            'probation_end_date' => 'nullable|date',
            'last_working_date' => 'nullable|date',
            'department_id' => 'nullable|exists:departments,id',
            'designation_id' => 'nullable|exists:designations,id',
            'location_id' => 'nullable|exists:locations,id',
            'shift_id' => 'nullable|exists:shifts,id',
            'annual_ctc' => 'nullable|numeric|min:0',
            'salary_group_id' => 'nullable|exists:salary_groups,id',
            'report_to' => 'nullable|exists:users,id',
            'status' => 'nullable|in:active,inactive',
            'account_holder_name' => 'nullable|string|max:191',
            'bank_name' => 'nullable|string|max:191',
            'account_number' => 'nullable|string|max:50',
            'branch_name' => 'nullable|string|max:191',
            'city' => 'nullable|string|max:100',
            'ifsc_code' => 'nullable|string|max:20',
            'pan_number' => 'nullable|string|max:20',
            'uan_number' => 'nullable|string|max:30',
            'pf_join_date' => 'nullable|date',
            'work_state' => 'nullable|string|max:100',
            'pt_location' => 'nullable|string|max:191',
            'role_id' => array_merge(['nullable', 'integer'], $companyId ? [Rule::exists('roles', 'id')->where('company_id', (int) $companyId)] : []),
            'login_enabled' => 'nullable|boolean',
            'allow_login' => 'nullable|boolean',
        ]);
        if (! Auth::user()->hasPermission('users_edit')) {
            unset($validated['role_id']);
        }
        if (! $this->canFullyManageEmployee(Auth::user())) {
            unset($validated['login_enabled'], $validated['allow_login'], $validated['employee_number']);
        } else {
            $validated['login_enabled'] = $request->boolean('login_enabled');
            $validated['allow_login'] = $request->boolean('allow_login');

            $manualEmployeeNumber = trim((string) ($validated['employee_number'] ?? ''));
            if ($manualEmployeeNumber === '') {
                $manualEmployeeNumber = $this->nextEmployeeNumber($companyId);
            }
            $validated['employee_number'] = $manualEmployeeNumber ?: null;
        }

        if (! empty($validated['password'])) {
            $validated['password'] = Hash::make($validated['password']);
        } else {
            unset($validated['password']);
        }
        $initialAnnualCtc = (float) ($validated['annual_ctc'] ?? 0);
        $employee->update($validated);
        unset($validated['annual_ctc']);
        
        $salaryGroupChanged = isset($validated['salary_group_id']) && $validated['salary_group_id'] != $oldSalaryGroupId;
        
        if (!empty($employee->salary_group_id)) {
            if ($initialAnnualCtc > 0) {
                $ctcToUse = $initialAnnualCtc;
            } elseif ($salaryGroupChanged && $employee->latestEffectiveSalaryRevision()) {
                $ctcToUse = $employee->latestEffectiveSalaryRevision()->ctc_annual;
            } else {
                $ctcToUse = null;
            }

            if ($ctcToUse !== null) {
                $this->salaryRevisionService->createSalaryRevision((int)$companyId, $employee->id, $employee->salary_group_id, $ctcToUse);
            }
        }

        return redirect()->route('employees.show', $employee->id)->with('success', 'Employee updated successfully.');
    }

    public function destroy(int $employee)
    {
        $companyId = Auth::user()?->company_id;
        $query = User::forTenantDirectory();
        if ($companyId) {
            $query->where('company_id', $companyId);
        }
        $employee = $query->findOrFail($employee);
        $employee->delete();
        return redirect()->route('employees.index')->with('success', 'Employee removed successfully.');
    }

    public function storeDocument(Request $request, int $employee)
    {
        $companyId = Auth::user()?->company_id;
        $query = User::forTenantDirectory();
        if ($companyId) {
            $query->where('company_id', $companyId);
        }
        $employee = $query->findOrFail($employee);
        $uploadCategories = $this->getUploadCategories();
        $validated = $request->validate([
            'type' => ['required', 'string', 'max:50', Rule::in(array_keys($uploadCategories))],
            'document' => 'required|file|max:10240',
        ]);
        $file = $request->file('document');
        $path = $file->store('documents/' . ($companyId ?? 'shared'), 'public');
        $categoryLabel = $uploadCategories[$validated['type']] ?? ucfirst(str_replace('_', ' ', $validated['type']));
        $employeeRef = $employee->employee_number ?: ('EMP' . $employee->id);
        $generatedName = $categoryLabel . ' - ' . $employeeRef . ' - ' . now()->format('YmdHis');
        Document::create([
            'company_id' => $companyId,
            'name' => $generatedName,
            'type' => $validated['type'],
            'path' => $path,
            'documentable_type' => User::class,
            'documentable_id' => $employee->id,
            'uploaded_by' => Auth::id(),
        ]);
        return redirect()->route('employees.show', $employee->id)->with('success', 'Document uploaded.');
    }

    /**
     * @return array<string, string>
     */
    private function getUploadCategories(): array
    {
        return config('employee_documents.upload_categories', []);
    }

    /**
     * @return array<string, array{label: string, aliases: array<int, string>}>
     */
    private function getMandatoryCategories(): array
    {
        return config('employee_documents.mandatory', []);
    }

    /**
     * @param EloquentCollection<int, Document> $documents
     * @return array{
     *   total_required: int,
     *   completed_count: int,
     *   completed_keys: array<int, string>,
     *   missing_keys: array<int, string>,
     *   missing_labels: array<int, string>,
     *   is_compliant: bool
     * }
     */
    private function buildDocumentCompliance(EloquentCollection $documents): array
    {
        $mandatory = $this->getMandatoryCategories();
        $requiredKeys = array_keys($mandatory);

        $matched = [];
        foreach ($documents as $document) {
            $docType = mb_strtolower(trim((string) ($document->type ?? '')));
            $docName = mb_strtolower(trim((string) ($document->name ?? '')));
            foreach ($mandatory as $categoryKey => $meta) {
                if (isset($matched[$categoryKey])) {
                    continue;
                }
                $aliases = collect($meta['aliases'] ?? [])
                    ->map(fn ($alias) => mb_strtolower(trim((string) $alias)))
                    ->filter()
                    ->all();
                $categoryTerms = array_values(array_unique(array_filter([
                    mb_strtolower(trim((string) $categoryKey)),
                    mb_strtolower(trim((string) ($meta['label'] ?? ''))),
                    ...$aliases,
                ])));

                foreach ($categoryTerms as $term) {
                    if ($term !== '' && ($docType === $term || str_contains($docType, $term) || str_contains($docName, $term))) {
                        $matched[$categoryKey] = true;
                        break;
                    }
                }
            }
        }

        $completedKeys = array_values(array_keys($matched));
        $missingKeys = array_values(array_diff($requiredKeys, $completedKeys));
        $missingLabels = array_values(array_map(
            fn ($key) => (string) ($mandatory[$key]['label'] ?? $key),
            $missingKeys
        ));

        return [
            'total_required' => count($requiredKeys),
            'completed_count' => count($completedKeys),
            'completed_keys' => $completedKeys,
            'missing_keys' => $missingKeys,
            'missing_labels' => $missingLabels,
            'is_compliant' => count($missingKeys) === 0,
        ];
    }

    /**
     * @return array{
     *   rows: array<int, array{name:string, monthly:float, annual:float, type:string}>,
     *   monthly_total: float,
     *   annual_total: float,
     *   effective_date: ?string,
     *   source: string
     * }
     */
    private function buildSalaryStructureForEmployee(User $employee, ?SalaryRevision $revision = null): array
    {
        $salaryGroup = $employee->salaryGroup;
        $rows = [];

        if ($revision && is_array($revision->components_json) && ! empty($revision->components_json)) {
            foreach ($this->sanitizeRevisionComponents($revision->components_json) as $component) {
                $annual = round(max(0, (float) ($component['annual'] ?? 0)), 2);
                $rows[] = [
                    'name' => (string) ($component['name'] ?? 'Component'),
                    'monthly' => round($annual / 12, 2),
                    'annual' => $annual,
                    'type' => 'earning',
                ];
            }
            $monthlyTotal = round(array_sum(array_map(fn ($row) => (float) $row['monthly'], $rows)), 2);
            $annualTotal = round(array_sum(array_map(fn ($row) => (float) $row['annual'], $rows)), 2);
            return [
                'rows' => $rows,
                'monthly_total' => $monthlyTotal,
                'annual_total' => $annualTotal,
                'effective_date' => $revision->effective_date?->format('M j, Y'),
                'source' => 'revision',
            ];
        }

        if ($salaryGroup && $salaryGroup->relationLoaded('salaryGroupComponents') && $salaryGroup->salaryGroupComponents->isNotEmpty()) {
            $resolvedComponents = app(SalaryStructureResolver::class)
                ->resolveMonthlyComponents($employee, (int) (Auth::user()?->company_id ?? 0), now());
            foreach ($resolvedComponents as $component) {
                $monthly = (float) ($component['amount'] ?? 0);
                $rows[] = [
                    'name' => (string) ($component['name'] ?? 'Component'),
                    'monthly' => round(max(0, $monthly), 2),
                    'annual' => round(max(0, $monthly) * 12, 2),
                    'type' => (string) ($component['type'] ?? 'other'),
                ];
            }

            $effectiveDate = $salaryGroup->updated_at ?? $employee->updated_at ?? $employee->joining_date;
            $source = 'salary_group';
        } else {
            $effectiveDate = null;
            $source = 'none';
        }

        $monthlyTotal = round(array_sum(array_map(fn ($row) => (float) $row['monthly'], $rows)), 2);
        $annualTotal = round(array_sum(array_map(fn ($row) => (float) $row['annual'], $rows)), 2);

        return [
            'rows' => $rows,
            'monthly_total' => $monthlyTotal,
            'annual_total' => $annualTotal,
            'effective_date' => $effectiveDate ? $effectiveDate->format('M j, Y') : null,
            'source' => $source,
        ];
    }

    private function nextEmployeeNumber(?int $companyId): string
    {
        if (! $companyId) {
            return '' . str_pad('1', 5, '0', STR_PAD_LEFT);
        }

        $sequence = DB::table('employee_number_sequences')
            ->where('company_id', $companyId)
            ->lockForUpdate()
            ->first();

        if (! $sequence) {
            DB::table('employee_number_sequences')->insert([
                'company_id' => $companyId,
                'prefix' => null,
                'digits' => 1,
                'last_number' => 1,
                'created_at' => now(),
                'updated_at' => now(),
            ]);

            return '' . str_pad('1', 5, '0', STR_PAD_LEFT);
        }

        $prefix = strtoupper(trim((string) ($sequence->prefix ?? null)));

        $digits = max(1, min(10, (int) ($sequence->digits ?? 5)));
        $nextNumber = (int) $sequence->last_number + 1;

        DB::table('employee_number_sequences')
            ->where('company_id', $companyId)
            ->update([
                'last_number' => $nextNumber,
                'prefix' => $prefix,
                'digits' => $digits,
                'updated_at' => now(),
            ]);

        return $prefix . str_pad((string) $nextNumber, $digits, '0', STR_PAD_LEFT);
    }

    /**
     * @param mixed $components
     * @return array<int, array{name:string, annual:float}>
     */
    private function sanitizeRevisionComponents($components): array
    {
        if (!is_array($components)) {
            return [];
        }
        $rows = [];
        foreach ($components as $component) {
            if (!is_array($component)) {
                continue;
            }
            $name = trim((string) ($component['name'] ?? ''));
            if ($name === '') {
                continue;
            }
            $annual = round(max(0, (float) ($component['annual'] ?? 0)), 2);
            $monthly = isset($component['monthly']) ? round(max(0, (float) $component['monthly']), 2) : round($annual / 12, 2);
            $rows[] = [
                'name'       => $name,
                'type'       => (string) ($component['type'] ?? 'earning'),
                'value'      => round(max(0, (float) ($component['value'] ?? 0)), 2),
                'value_type' => (string) ($component['value_type'] ?? 'fixed'),
                'annual'     => $annual,
                'monthly'    => $monthly,
            ];
        }
        return $rows;
    }

    /**
     * Default employee list to active only; explicit empty status = all.
     *
     * @param  \Illuminate\Database\Eloquent\Builder<User>  $query
     */
    protected function applyEmployeeStatusFilter($query, Request $request): string
    {
        $status = $request->has('status')
            ? (string) $request->input('status', '')
            : User::STATUS_ACTIVE;

        if ($status !== '' && $status !== 'all') {
            $query->where('status', $status);
        }

        return $status === '' ? 'all' : $status;
    }

    protected function canFullyManageEmployee(?User $authUser): bool
    {
        if (! $authUser) {
            return false;
        }

        return $authUser->hasPermission('users_edit')
            && ($authUser->isTenantAdmin() || $authUser->isTenantHr() || $authUser->isCompanyAccountAdmin());
    }
}
