<?php

namespace App\Modules\HR\Attendance\Repositories;

use App\Modules\HR\Attendance\Models\AttendanceException;
use App\Modules\HR\Attendance\Support\AttendanceExceptionSeverity;
use App\Modules\HR\Attendance\Support\AttendanceExceptionStatus;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\LazyCollection;

class AttendanceExceptionRepository
{
    public const OPERATIONAL_SNAPSHOT_CACHE_SUFFIX = 'operational_snapshot_v1';

    /**
     * @param  array<string, mixed>  $metadata
     */
    public function tryInsert(
        int $tenantId,
        int $employeeId,
        ?int $attendanceRecordId,
        string $type,
        string $severity,
        string $dedupeKey,
        array $metadata = [],
    ): bool {
        $now = now();
        $inserted = DB::table('attendance_exceptions')->insertOrIgnore([
            'tenant_id' => $tenantId,
            'employee_id' => $employeeId,
            'attendance_record_id' => $attendanceRecordId,
            'type' => $type,
            'severity' => $severity,
            'status' => AttendanceExceptionStatus::PENDING,
            'detected_at' => $now,
            'resolved_by' => null,
            'resolved_at' => null,
            'resolution_note' => null,
            'metadata' => json_encode($metadata ?: [], JSON_THROW_ON_ERROR),
            'dedupe_key' => $dedupeKey,
            'created_at' => $now,
            'updated_at' => $now,
        ]);
        if ($inserted > 0) {
            $this->forgetOperationalSnapshot($tenantId);
        }

        return $inserted > 0;
    }

    public function pendingCountForTenant(int $tenantId): int
    {
        return AttendanceException::query()
            ->where('tenant_id', $tenantId)
            ->where('status', AttendanceExceptionStatus::PENDING)
            ->count();
    }

    public function criticalPendingCountForTenant(int $tenantId): int
    {
        return AttendanceException::query()
            ->where('tenant_id', $tenantId)
            ->where('status', AttendanceExceptionStatus::PENDING)
            ->where('severity', AttendanceExceptionSeverity::CRITICAL)
            ->count();
    }

    /**
     * @param  array{type?: string, severity?: string, status?: string, employee_id?: int, from?: string, to?: string}  $filters
     * @return LengthAwarePaginator<int, AttendanceException>
     */
    public function paginateForTenant(int $tenantId, array $filters, int $perPage = 25): LengthAwarePaginator
    {
        return $this->filteredQuery($tenantId, $filters)
            ->orderByDesc('detected_at')
            ->paginate($perPage)
            ->withQueryString();
    }

    /**
     * @param  array{type?: string, severity?: string, status?: string, employee_id?: int, from?: string, to?: string}  $filters
     * @return LazyCollection<int, AttendanceException>
     */
    public function cursorForTenant(int $tenantId, array $filters): LazyCollection
    {
        return $this->filteredQuery($tenantId, $filters)
            ->orderByDesc('detected_at')
            ->cursor();
    }

    /**
     * @param  array{type?: string, severity?: string, status?: string, employee_id?: int, from?: string, to?: string}  $filters
     */
    private function filteredQuery(int $tenantId, array $filters): Builder
    {
        $q = AttendanceException::query()
            ->where('tenant_id', $tenantId)
            ->with(['employee:id,full_name', 'attendanceRecord:id,attendance_date,check_in_at,check_out_at']);

        if (! empty($filters['type'])) {
            $q->where('type', $filters['type']);
        }
        if (! empty($filters['severity'])) {
            $q->where('severity', $filters['severity']);
        }
        if (! empty($filters['status'])) {
            $q->where('status', $filters['status']);
        }
        if (! empty($filters['employee_id'])) {
            $q->where('employee_id', (int) $filters['employee_id']);
        }
        if (! empty($filters['from'])) {
            $q->whereDate('detected_at', '>=', $filters['from']);
        }
        if (! empty($filters['to'])) {
            $q->whereDate('detected_at', '<=', $filters['to']);
        }

        return $q;
    }

    public function pendingCountForEmployee(int $tenantId, int $employeeId): int
    {
        return AttendanceException::query()
            ->where('tenant_id', $tenantId)
            ->where('employee_id', $employeeId)
            ->where('status', AttendanceExceptionStatus::PENDING)
            ->count();
    }

    public function findForTenant(int $tenantId, int $id): ?AttendanceException
    {
        return AttendanceException::query()
            ->where('tenant_id', $tenantId)
            ->whereKey($id)
            ->first();
    }

    public function forgetOperationalSnapshot(int $tenantId): void
    {
        Cache::forget('tenant:'.$tenantId.':'.self::OPERATIONAL_SNAPSHOT_CACHE_SUFFIX);
    }
}
