<?php

namespace App\Services\Attendance;

use App\Models\AttendancePolicy;
use App\Models\User;

class AttendancePolicyResolver
{
    public function resolveForUser(User $user): ResolvedAttendancePolicy
    {
        $tenant = $this->tenantPolicy((int) ($user->company_id ?? 0));
        $employeeMode = $this->normalizeMode($user->attendance_mode ?? null);

        if ($employeeMode !== null) {
            return $this->makeResolved($employeeMode, $tenant, 'employee', $employeeMode);
        }

        return $this->makeResolved($tenant['mode'], $tenant, 'tenant', null);
    }

    /**
     * @return array<string, mixed>
     */
    public function tenantPolicy(?int $companyId): array
    {
        $defaults = AttendancePolicy::systemDefaults();
        if (! $companyId) {
            return $defaults;
        }

        $row = AttendancePolicy::query()->where('company_id', $companyId)->first();
        if (! $row) {
            return $defaults;
        }

        $mode = $this->normalizeMode($row->mode) ?? AttendancePolicy::MODE_NORMAL;
        $flags = AttendancePolicy::syncDerivedFlags($mode);

        return array_merge($defaults, [
            'mode' => $mode,
            'geo_fence_enabled' => $flags['geo_fence_enabled'],
            'remote_attendance_enabled' => $flags['remote_attendance_enabled'],
            'remote_approval_required' => (bool) $row->remote_approval_required,
            'default_radius_meters' => max(1, (int) ($row->default_radius_meters ?: AttendancePolicy::DEFAULT_RADIUS_METERS)),
            'gps_accuracy_requirement' => $row->gps_accuracy_requirement !== null ? (int) $row->gps_accuracy_requirement : null,
            'require_location_for_office' => (bool) $row->require_location_for_office,
            'allow_web_checkin' => (bool) $row->allow_web_checkin,
            'allow_mobile_checkin' => (bool) $row->allow_mobile_checkin,
            'biometric_attendance_mode' => (string) ($row->biometric_attendance_mode ?: AttendancePolicy::BIOMETRIC_MODE_FIRST_IN_LAST_OUT),
            'biometric_min_punch_gap_minutes' => max(1, (int) ($row->biometric_min_punch_gap_minutes ?: 1)),
        ]);
    }

    public function forCompany(?int $companyId): AttendancePolicy
    {
        $defaults = AttendancePolicy::systemDefaults();
        if (! $companyId) {
            $policy = new AttendancePolicy($defaults);
            $policy->company_id = null;

            return $policy;
        }

        return AttendancePolicy::query()->firstOrCreate(
            ['company_id' => $companyId],
            $defaults
        );
    }

    /**
     * @param  array<string, mixed>  $tenant
     */
    private function makeResolved(
        string $mode,
        array $tenant,
        string $source,
        ?string $employeeMode
    ): ResolvedAttendancePolicy {
        $flags = AttendancePolicy::syncDerivedFlags($mode);

        return new ResolvedAttendancePolicy(
            mode: $mode,
            geoFenceEnabled: $flags['geo_fence_enabled'],
            remoteAttendanceEnabled: $flags['remote_attendance_enabled'],
            remoteApprovalRequired: (bool) $tenant['remote_approval_required'],
            defaultRadiusMeters: (int) $tenant['default_radius_meters'],
            gpsAccuracyRequirement: $tenant['gps_accuracy_requirement'] !== null
                ? (int) $tenant['gps_accuracy_requirement']
                : null,
            requireLocationForOffice: (bool) $tenant['require_location_for_office'],
            allowWebCheckin: (bool) $tenant['allow_web_checkin'],
            allowMobileCheckin: (bool) $tenant['allow_mobile_checkin'],
            source: $source,
            employeeMode: $employeeMode,
        );
    }

    public function normalizeMode(mixed $mode): ?string
    {
        if (! is_string($mode) || trim($mode) === '' || $mode === 'tenant_default') {
            return null;
        }

        $normalized = strtolower(trim($mode));

        return in_array($normalized, AttendancePolicy::MODES, true) ? $normalized : null;
    }
}
