<?php

namespace App\Modules\Dashboard\Services;

use App\Modules\Academic\Models\StudentModel;
use App\Modules\Attendance\Models\AttendanceSessionModel;
use App\Modules\Devices\Models\DeviceModel;

/**
 * Dashboard Service
 *
 * Aggregates data for dashboard monitoring endpoints.
 */
class DashboardService
{
    protected AttendanceSessionModel $attendanceModel;
    protected DeviceModel $deviceModel;
    protected StudentModel $studentModel;

    /**
     * Minutes threshold for device "online" status
     */
    protected int $deviceOnlineThresholdMinutes = 2;

    public function __construct()
    {
        $this->attendanceModel = new AttendanceSessionModel();
        $this->deviceModel     = new DeviceModel();
        $this->studentModel    = new StudentModel();
    }

    /**
     * Get today's attendance summary
     *
     * @return array{total_students: int, present_today: int, late_today: int, outside_zone_today: int, no_schedule_today: int, invalid_device_today: int}
     */
    public function getSummary(): array
    {
        $db = \Config\Database::connect();

        $totalStudents = (int) $this->studentModel->countAll();

        // Count by status for today only (date part of checkin_at = today)
        $today = date('Y-m-d');
        $builder = $db->table('attendance_sessions');
        $builder->select('status, COUNT(*) as cnt');
        $builder->where('DATE(checkin_at)', $today);
        $builder->groupBy('status');
        $rows = $builder->get()->getResultArray();

        $counts = [
            'PRESENT'         => 0,
            'LATE'            => 0,
            'OUTSIDE_ZONE'    => 0,
            'NO_SCHEDULE'     => 0,
            'INVALID_DEVICE'  => 0,
        ];

        foreach ($rows as $row) {
            if (isset($counts[$row['status']])) {
                $counts[$row['status']] = (int) $row['cnt'];
            }
        }

        return [
            'total_students'        => $totalStudents,
            'present_today'         => $counts['PRESENT'],
            'late_today'            => $counts['LATE'],
            'outside_zone_today'    => $counts['OUTSIDE_ZONE'],
            'no_schedule_today'     => $counts['NO_SCHEDULE'],
            'invalid_device_today'  => $counts['INVALID_DEVICE'],
        ];
    }

    /**
     * Get last N attendance check-ins with student, class, subject, device info
     *
     * @param int $limit
     * @return array<int, array{student_name: string, class_name: string, subject: string, checkin_at: string, status: string, device_code: string}>
     */
    public function getRealtimeCheckins(int $limit = 20): array
    {
        $db = \Config\Database::connect();

        $builder = $db->table('attendance_sessions AS a');
        $builder->select('
            s.name AS student_name,
            c.name AS class_name,
            COALESCE(sub.name, "-") AS subject,
            a.checkin_at,
            a.status,
            d.device_code
        ');
        $builder->join('students AS s', 's.id = a.student_id', 'left');
        $builder->join('classes AS c', 'c.id = s.class_id', 'left');
        $builder->join('schedules AS sch', 'sch.id = a.schedule_id', 'left');
        $builder->join('subjects AS sub', 'sub.id = sch.subject_id', 'left');
        $builder->join('devices AS d', 'd.id = a.device_id', 'left');
        $builder->orderBy('a.checkin_at', 'DESC');
        $builder->limit($limit);

        $rows = $builder->get()->getResultArray();

        $out = [];
        foreach ($rows as $row) {
            $out[] = [
                'student_name' => (string) ($row['student_name'] ?? '-'),
                'class_name'   => (string) ($row['class_name'] ?? '-'),
                'subject'      => (string) ($row['subject'] ?? '-'),
                'checkin_at'   => (string) $row['checkin_at'],
                'status'       => (string) $row['status'],
                'device_code'  => (string) ($row['device_code'] ?? '-'),
            ];
        }

        return $out;
    }

    /**
     * Get devices with online status (online if last_seen_at within threshold minutes)
     *
     * @return array<int, array{
     *   id: int,
     *   device_code: string,
     *   device_name: string,
     *   is_active: bool,
     *   last_seen_at: string|null,
     *   online_status: string,
     *   latitude: float|null,
     *   longitude: float|null,
     *   radius_meters: int|null
     * }>
     */
    public function getDevices(): array
    {
        $devices = $this->deviceModel->findAll();
        $thresholdSeconds = $this->deviceOnlineThresholdMinutes * 60;
        $now = time();

        $out = [];
        foreach ($devices as $d) {
            $lastSeenAt = $d->last_seen_at;
            $lastSeenStr = null;
            $lastSeenTs = null;
            if ($lastSeenAt !== null) {
                $lastSeenStr = is_object($lastSeenAt) && method_exists($lastSeenAt, 'format')
                    ? $lastSeenAt->format('Y-m-d H:i:s')
                    : (string) $lastSeenAt;
                $lastSeenTs = strtotime($lastSeenStr);
            }
            $onlineStatus = 'offline';
            if ($lastSeenTs !== null && ($now - $lastSeenTs) < $thresholdSeconds) {
                $onlineStatus = 'online';
            }

            $out[] = [
                'id'            => (int) $d->id,
                'device_code'   => $d->device_code,
                'device_name'   => (string) ($d->device_name ?? ''),
                'is_active'     => (bool) $d->is_active,
                'last_seen_at'  => $lastSeenStr,
                'online_status' => $onlineStatus,
                'latitude'      => $d->latitude !== null ? (float) $d->latitude : null,
                'longitude'     => $d->longitude !== null ? (float) $d->longitude : null,
                'radius_meters' => $d->radius_meters !== null ? (int) $d->radius_meters : null,
            ];
        }

        return $out;
    }
}
