<?php

namespace App\Modules\Academic\Controllers;

use App\Core\BaseApiController;
use App\Modules\Academic\Models\DapodikSyncJobModel;
use App\Modules\Academic\Models\DapodikRombelMappingModel;
use App\Modules\Academic\Services\DapodikSyncService;
use CodeIgniter\HTTP\ResponseInterface;

/**
 * Dapodik sync and rombel mapping API (ADMIN only).
 */
class DapodikSyncController extends BaseApiController
{
    /**
     * POST /api/academic/dapodik/sync/students
     * Starts sync job, returns job_id immediately, processes in background (same request after response sent).
     * If a students-sync job is already running, returns existing job_id (job locking).
     * Body/Query: limit, max_pages (optional).
     */
    public function syncStudents(): ResponseInterface
    {
        $body     = $this->request->getJSON(true) ?? [];
        $limit    = (int) ($body['limit'] ?? $this->request->getGet('limit') ?? 100);
        $maxPages = (int) ($body['max_pages'] ?? $this->request->getGet('max_pages') ?? 50);

        $limit    = max(1, min(500, $limit));
        $maxPages = max(1, min(100, $maxPages));

        $jobModel = new DapodikSyncJobModel();

        // Job locking: if there is already a running students-sync job, reuse it.
        $existing = $jobModel
            ->where('type', DapodikSyncJobModel::TYPE_STUDENTS)
            ->where('status', DapodikSyncJobModel::STATUS_RUNNING)
            ->orderBy('started_at', 'DESC')
            ->first();

        if ($existing) {
            return $this->successResponse([
                'job_id'     => (int) $existing['id'],
                'total_rows' => (int) ($existing['total_rows'] ?? 0),
                'status'     => $existing['status'],
            ], 'Sync already running');
        }

        $service   = new DapodikSyncService();
        $estimate  = $service->estimateTotalStudents($limit);
        $totalRows = $estimate['total'];

        $now = date('Y-m-d H:i:s');
        $jobId = $jobModel->insert([
            'type'          => DapodikSyncJobModel::TYPE_STUDENTS,
            'total_rows'    => $totalRows,
            'processed_rows'=> 0,
            'status'        => DapodikSyncJobModel::STATUS_RUNNING,
            'message'       => null,
            'started_at'    => $now,
            'finished_at'   => null,
        ]);

        $payload = [
            'job_id'     => (int) $jobId,
            'total_rows' => $totalRows,
            'status'     => 'running',
        ];

        $this->successResponse($payload, 'Sync started')->send();

        if (function_exists('fastcgi_finish_request')) {
            fastcgi_finish_request();
        }
        ignore_user_abort(true);
        set_time_limit(0);

        $service->syncStudentsWithJob((int) $jobId, $limit, $maxPages);
        exit;
    }

    /**
     * GET /api/academic/dapodik/sync/status/{job_id}
     * Returns job progress for polling.
     */
    public function status(int $jobId): ResponseInterface
    {
        $jobModel = new DapodikSyncJobModel();
        $job = $jobModel->find($jobId);
        if (! $job) {
            return $this->errorResponse('Job not found', null, null, 404);
        }

        $total = (int) ($job['total_rows'] ?? 0);
        $processed = (int) ($job['processed_rows'] ?? 0);
        $percent = $total > 0 ? min(100, (int) round(($processed / $total) * 100)) : ($processed > 0 ? 1 : 0);

        return $this->successResponse([
            'total_rows'     => $total,
            'processed_rows' => $processed,
            'percent'        => $percent,
            'status'         => $job['status'],
            'message'        => $job['message'] ?? null,
        ]);
    }

    /**
     * GET /api/academic/dapodik/rombels
     * Query: unmapped_only=1 to filter.
     */
    public function rombels(): ResponseInterface
    {
        $unmappedOnly = $this->request->getGet('unmapped_only') === '1' || $this->request->getGet('unmapped_only') === 'true';

        $db = \Config\Database::connect();
        $builder = $db->table('dapodik_rombel_mappings AS m')
            ->select('m.id, m.dapodik_rombel, m.class_id, m.last_seen_at, m.updated_at, c.grade, c.major, c.name AS class_name')
            ->join('classes AS c', 'c.id = m.class_id', 'left')
            ->orderBy('m.dapodik_rombel', 'ASC');

        if ($unmappedOnly) {
            $builder->where('m.class_id', null);
        }

        $rows = $builder->get()->getResultArray();
        $data = array_map(static function ($r) {
            $classLabel = null;
            if ($r['class_id'] !== null && isset($r['grade'])) {
                $classLabel = trim(($r['grade'] ?? '') . ' ' . ($r['major'] ?? '') . ' ' . ($r['class_name'] ?? ''));
            }
            return [
                'id'              => (int) $r['id'],
                'dapodik_rombel'  => (string) $r['dapodik_rombel'],
                'class_id'        => $r['class_id'] !== null ? (int) $r['class_id'] : null,
                'class_label'     => $classLabel,
                'last_seen_at'    => $r['last_seen_at'],
                'updated_at'      => $r['updated_at'],
            ];
        }, $rows);

        return $this->successResponse($data, 'Rombel mappings');
    }

    /**
     * PUT /api/academic/dapodik/rombels/{id}
     * Body: class_id (nullable)
     */
    public function updateRombel(int $id): ResponseInterface
    {
        $payload = $this->request->getJSON(true) ?? $this->request->getPost();
        $classId = null;
        if (isset($payload['class_id']) && $payload['class_id'] !== '' && $payload['class_id'] !== null) {
            $classId = (int) $payload['class_id'];
            $db = \Config\Database::connect();
            $exists = $db->table('classes')->where('id', $classId)->countAllResults();
            if ($exists < 1) {
                return $this->errorResponse('class_id must exist in classes', null, null, 422);
            }
        }

        $model = new DapodikRombelMappingModel();
        $row = $model->find($id);
        if (! $row) {
            return $this->errorResponse('Mapping not found', null, null, 404);
        }

        $model->update($id, ['class_id' => $classId]);

        $updated = $model->find($id);
        $classLabel = null;
        if ($updated['class_id']) {
            $db = \Config\Database::connect();
            $c = $db->table('classes')->select('grade, major, name')->where('id', $updated['class_id'])->get()->getRowArray();
            if ($c) {
                $classLabel = trim(($c['grade'] ?? '') . ' ' . ($c['major'] ?? '') . ' ' . ($c['name'] ?? ''));
            }
        }

        return $this->successResponse([
            'id'             => (int) $updated['id'],
            'dapodik_rombel' => (string) $updated['dapodik_rombel'],
            'class_id'       => $updated['class_id'] !== null ? (int) $updated['class_id'] : null,
            'class_label'    => $classLabel,
        ], 'Mapping updated');
    }
}
