<?php

namespace App\Support;

use Illuminate\Validation\Rule;

class EmployeeValidation
{
    public const ALPHA_SPACES = '/^[A-Za-z\s]+$/';

    public const ALPHANUM_SPACES = '/^[A-Za-z0-9\s]+$/';

    public const PAN = '/^[A-Z]{5}[0-9]{4}[A-Z]$/';

    public const UAN = '/^[0-9]{12}$/';

    public const ACCOUNT_NUMBER = '/^[0-9]+$/';

    public const IFSC = '/^[A-Z]{4}0[A-Z0-9]{6}$/';

    /**
     * Lowercase email and uppercase PAN before validation/persist.
     *
     * @param  array<string, mixed>  $input
     * @return array<string, mixed>
     */
    public static function normalize(array $input): array
    {
        if (array_key_exists('email', $input) && is_string($input['email'])) {
            $input['email'] = strtolower(trim($input['email']));
        }

        if (array_key_exists('pan_number', $input) && is_string($input['pan_number']) && $input['pan_number'] !== '') {
            $input['pan_number'] = strtoupper(trim($input['pan_number']));
        }

        if (array_key_exists('phone', $input) && is_string($input['phone'])) {
            $trimmed = trim($input['phone']);
            $input['phone'] = $trimmed === '' ? null : $trimmed;
        }

        if (array_key_exists('gender', $input) && is_string($input['gender'])) {
            $trimmed = trim($input['gender']);
            $input['gender'] = $trimmed === '' ? null : strtolower($trimmed);
        }

        if (array_key_exists('attendance_mode', $input)) {
            $mode = is_string($input['attendance_mode']) ? trim($input['attendance_mode']) : $input['attendance_mode'];
            if ($mode === '' || $mode === 'tenant_default') {
                $input['attendance_mode'] = null;
            } else {
                $input['attendance_mode'] = $mode;
            }
        }

        return $input;
    }

    /**
     * Shared field rules used by create/edit employee forms.
     *
     * @return array<string, mixed>
     */
    public static function formRules(?int $ignoreUserId, ?int $companyId, bool $passwordRequired = true): array
    {
        $emailUnique = Rule::unique('users', 'email')
            ->where(fn ($query) => $companyId
                ? $query->where('company_id', $companyId)
                : $query->whereNull('company_id'));
        if ($ignoreUserId) {
            $emailUnique = $emailUnique->ignore($ignoreUserId);
        }

        $employeeNumberUnique = Rule::unique('users', 'employee_number')
            ->where(fn ($query) => $companyId ? $query->where('company_id', $companyId) : $query);
        if ($ignoreUserId) {
            $employeeNumberUnique = $employeeNumberUnique->ignore($ignoreUserId);
        }

        $rules = [
            'name' => ['required', 'string', 'max:191', 'regex:'.self::ALPHA_SPACES],
            'email' => ['required', 'email', 'max:191', $emailUnique],
            'password' => $passwordRequired ? ['required', 'string', 'min:8'] : ['nullable', 'string', 'min:8'],
            'employee_number' => ['nullable', 'string', 'max:50', $employeeNumberUnique],
            'phone' => ['nullable', 'digits:10'],
            'gender' => ['required', 'in:male,female,other'],
            'dob' => ['nullable', 'date', 'before_or_equal:today'],
            'father_name' => ['nullable', 'string', 'max:191', 'regex:'.self::ALPHA_SPACES],
            'joining_date' => ['nullable', 'date'],
            'registration_date' => ['nullable', 'date'],
            'confirmation_date' => ['nullable', 'date', 'after:probation_end_date'],
            'probation_end_date' => ['nullable', 'date'],
            'last_working_date' => ['nullable', 'date'],
            'department_id' => ['nullable', 'exists:departments,id'],
            'designation_id' => ['required', 'exists:designations,id'],
            'location_id' => ['required', 'exists:locations,id'],
            'shift_id' => ['required', 'exists:shifts,id'],
            'attendance_mode' => ['nullable', 'in:tenant_default,normal,office_geofence,remote,hybrid'],
            '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', 'regex:'.self::ALPHA_SPACES],
            'bank_name' => ['nullable', 'string', 'max:191', 'regex:'.self::ALPHA_SPACES],
            'account_number' => ['nullable', 'string', 'max:50', 'regex:'.self::ACCOUNT_NUMBER],
            'branch_name' => ['nullable', 'string', 'max:191', 'regex:'.self::ALPHA_SPACES],
            'city' => ['nullable', 'string', 'max:100', 'regex:'.self::ALPHA_SPACES],
            'ifsc_code' => ['nullable', 'string', 'size:11', 'regex:'.self::IFSC],
            'pan_number' => ['nullable', 'string', 'size:10', 'regex:'.self::PAN],
            'uan_number' => ['nullable', 'digits:12'],
            'pf_join_date' => ['nullable', 'date', 'after_or_equal:joining_date'],
            'work_state' => ['nullable', 'string', 'max:100', 'regex:'.self::ALPHA_SPACES],
            'pt_location' => ['nullable', 'string', 'max:191', 'regex:'.self::ALPHANUM_SPACES],
            'role_id' => array_merge(
                ['nullable', 'integer'],
                $companyId ? [Rule::exists('roles', 'id')->where('company_id', (int) $companyId)] : []
            ),
        ];

        if (! $passwordRequired) {
            $rules['status'] = ['nullable', 'in:active,inactive'];
            $rules['allow_login'] = ['nullable', 'boolean'];
        }

        return $rules;
    }

    /**
     * CSV import rules (name-based org fields instead of IDs).
     *
     * @return array<string, mixed>
     */
    public static function importRules(?int $companyId): array
    {
        return [
            'name' => ['required', 'string', 'max:191', 'regex:'.self::ALPHA_SPACES],
            'email' => [
                'required',
                'email',
                'max:191',
                Rule::unique('users', 'email')
                    ->where(fn ($query) => $companyId
                        ? $query->where('company_id', $companyId)
                        : $query->whereNull('company_id')),
            ],
            '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', 'digits:10'],
            'gender' => ['nullable', 'in:male,female,other'],
            'dob' => ['nullable', 'date', 'before_or_equal:today'],
            'father_name' => ['nullable', 'string', 'max:191', 'regex:'.self::ALPHA_SPACES],
            'joining_date' => ['nullable', 'date'],
            'registration_date' => ['nullable', 'date'],
            'confirmation_date' => ['nullable', 'date', 'after:probation_end_date'],
            'probation_end_date' => ['nullable', 'date'],
            'last_working_date' => ['nullable', 'date'],
            'department' => ['nullable', 'string', 'max:191'],
            // Name-based columns stay nullable: import resolves/creates IDs and can default shift.
            'designation' => ['nullable', 'string', 'max:191'],
            'location' => ['nullable', 'string', 'max:191'],
            'shift' => ['nullable', 'string', 'max:191'],
            'salary_group' => ['nullable', 'string', 'max:191'],
            'role' => ['nullable', 'string', 'max:191'],
            'report_to' => ['nullable', 'string', 'max:191'],
            'annual_ctc' => ['nullable', 'numeric', 'min:0'],
            'account_holder_name' => ['nullable', 'string', 'max:191', 'regex:'.self::ALPHA_SPACES],
            'bank_name' => ['nullable', 'string', 'max:191', 'regex:'.self::ALPHA_SPACES],
            'account_number' => ['nullable', 'string', 'max:50', 'regex:'.self::ACCOUNT_NUMBER],
            'branch_name' => ['nullable', 'string', 'max:191', 'regex:'.self::ALPHA_SPACES],
            'city' => ['nullable', 'string', 'max:100', 'regex:'.self::ALPHA_SPACES],
            'ifsc_code' => ['nullable', 'string', 'size:11', 'regex:'.self::IFSC],
            'pan_number' => ['nullable', 'string', 'size:10', 'regex:'.self::PAN],
            'uan_number' => ['nullable', 'digits:12'],
            'pf_join_date' => ['nullable', 'date', 'after_or_equal:joining_date'],
            'work_state' => ['nullable', 'string', 'max:100', 'regex:'.self::ALPHA_SPACES],
            'pt_location' => ['nullable', 'string', 'max:191', 'regex:'.self::ALPHANUM_SPACES],
        ];
    }

    /**
     * @return array<string, string>
     */
    public static function messages(): array
    {
        return [
            'name.regex' => 'The name may only contain letters and spaces.',
            'father_name.regex' => 'The father name may only contain letters and spaces.',
            'account_holder_name.regex' => 'The account holder name may only contain letters and spaces.',
            'bank_name.regex' => 'The bank name may only contain letters and spaces.',
            'account_number.regex' => 'The account number may only contain numbers.',
            'branch_name.regex' => 'The branch name may only contain letters and spaces.',
            'city.regex' => 'The city may only contain letters and spaces.',
            'ifsc_code.regex' => 'The IFSC code must match the format SBIN0001234.',
            'ifsc_code.size' => 'The IFSC code must be exactly 11 characters.',
            'work_state.regex' => 'The work state may only contain letters and spaces.',
            'pt_location.regex' => 'The PT location may only contain letters, numbers, and spaces.',
            'pan_number.regex' => 'The PAN number must match the format ABCDE1234F.',
            'pan_number.size' => 'The PAN number must be exactly 10 characters.',
            'uan_number.digits' => 'The UAN number must be exactly 12 digits.',
            'phone.digits' => 'The phone number must be exactly 10 digits.',
            'gender.required' => 'Please select a gender.',
            'dob.before_or_equal' => 'Date of birth cannot be in the future.',
            'confirmation_date.after' => 'The confirmation date must be later than the probation end date.',
            'pf_join_date.after_or_equal' => 'The PF join date must be on or after the joining date.',
            'designation_id.required' => 'Please select a designation.',
            'location_id.required' => 'Please select a location.',
            'shift_id.required' => 'Please select a shift.',
            'report_to.required' => 'Please select a reporting manager.',
            'designation.required' => 'Designation is required.',
            'location.required' => 'Location is required.',
            'shift.required' => 'Shift is required.',
            'report_to.required' => 'Reporting manager is required.',
        ];
    }

    /**
     * @return array<string, mixed>
     */
    public static function profileBasicRules(): array
    {
        return [
            'phone' => ['nullable', 'digits:10'],
            'gender' => ['nullable', 'in:male,female,other'],
            'dob' => ['nullable', 'date', 'before_or_equal:today'],
            'father_name' => ['nullable', 'string', 'max:191', 'regex:'.self::ALPHA_SPACES],
            'pan_number' => ['nullable', 'string', 'size:10', 'regex:'.self::PAN],
        ];
    }

    /**
     * @return array<string, mixed>
     */
    public static function profileWorkRules(): array
    {
        return [
            'uan_number' => ['nullable', 'digits:12'],
            'work_state' => ['nullable', 'string', 'max:100', 'regex:'.self::ALPHA_SPACES],
            'pt_location' => ['nullable', 'string', 'max:191', 'regex:'.self::ALPHANUM_SPACES],
        ];
    }

    /**
     * @return array<string, mixed>
     */
    public static function profileBankRules(): array
    {
        return [
            'account_holder_name' => ['nullable', 'string', 'max:191', 'regex:'.self::ALPHA_SPACES],
            'bank_name' => ['nullable', 'string', 'max:191', 'regex:'.self::ALPHA_SPACES],
            'account_number' => ['nullable', 'string', 'max:50', 'regex:'.self::ACCOUNT_NUMBER],
            'branch_name' => ['nullable', 'string', 'max:191', 'regex:'.self::ALPHA_SPACES],
            'city' => ['nullable', 'string', 'max:100', 'regex:'.self::ALPHA_SPACES],
            'ifsc_code' => ['nullable', 'string', 'size:11', 'regex:'.self::IFSC],
        ];
    }
}
