<?php

namespace App\Controllers\Api;

use CodeIgniter\RESTful\ResourceController;
use App\Models\UjianModel;
use App\Models\HasilUjianModel;
use App\Models\SoalModel;
use App\Models\SoalOpsiModel;
use App\Models\JawabanModel;
use Ramsey\Uuid\Uuid;

class UjianPesertaApiController extends ResourceController
{
    protected $ujianModel;
    protected $hasilUjianModel;
    protected $soalModel;
    protected $soalOpsiModel;
    protected $jawabanModel;

    public function __construct()
    {
        $this->ujianModel = new UjianModel();
        $this->hasilUjianModel = new HasilUjianModel();
        $this->soalModel = new SoalModel();
        $this->soalOpsiModel = new SoalOpsiModel();
        $this->jawabanModel = new JawabanModel();
    }

    protected function getPesertaFromToken()
    {
        $authHeader = $this->request->getHeaderLine('Authorization');
        if (!$authHeader || !str_starts_with($authHeader, 'Bearer ')) {
            return ['success' => false, 'message' => 'Header Authorization tidak ditemukan atau format salah.'];
        }

        $token = substr($authHeader, 7);
        $peserta = db_connect()->table('peserta')->where('api_token', $token)->get()->getRowArray();

        if (!$peserta) {
            return ['success' => false, 'message' => 'Token peserta tidak valid.'];
        }

        return ['success' => true, 'id' => $peserta['id'] ?? null, 'data' => $peserta];
    }

    protected function failResponse($message)
    {
        return $this->respond(['success' => false, 'message' => $message, 'data' => null]);
    }

    public function cekToken()
    {
        $peserta = $this->getPesertaFromToken();
        if (!$peserta['success']) {
            return $this->failUnauthorized($peserta['message']);
        }

        $ujianId = $this->request->getVar('ujian_id');
        $inputToken = $this->request->getVar('token');

        if (!$ujianId || !$inputToken) {
            return $this->fail('Ujian ID dan token wajib diisi.');
        }

        $ujian = $this->ujianModel->find($ujianId);
        if (!$ujian) {
            return $this->failNotFound('Ujian tidak ditemukan.');
        }

        if (empty($ujian['pakai_token']) || !$ujian['pakai_token']) {
            return $this->respond(['success' => true, 'message' => 'Ujian tidak memerlukan token.', 'data' => null]);
        }

        $tokenBenar = strtoupper(trim($ujian['token']));
        $tokenInput = strtoupper(trim($inputToken));

        if ($tokenBenar !== $tokenInput) {
            return $this->failValidationErrors('Token tidak valid.');
        }

        $this->hasilUjianModel
            ->where('ujian_id', $ujianId)
            ->where('peserta_id', $peserta['id'])
            ->set(['token_valid' => 1])
            ->update();

        return $this->respond(['success' => true, 'message' => 'Token valid.', 'data' => null]);
    }




    public function mulaiUjian()
    {
        $ujianId = $this->request->getVar('ujian_id');
        if (!$ujianId) {
            return $this->failValidationErrors('Ujian ID wajib diisi.');
        }

        $peserta = $this->getPesertaFromToken();
        if (!$peserta || !isset($peserta['id'])) {
            return $this->failUnauthorized('Token tidak valid atau peserta tidak ditemukan.');
        }

        $validasi = $this->validasiUjianPeserta($ujianId, $peserta['id'], 'mulai');
        if (isset($validasi['error'])) {
            return $this->fail(['message' => $validasi['error']]);
        }

        return $this->respond([
            'success' => true,
            'message' => 'Ujian dimulai.',
            'data' => [
                'status_ujian' => 'mulai',
                'hasil' => $validasi['hasil'],
                'ujian' => $validasi['ujian'],
            ]
        ]);
    }



    public function lanjutUjian()
    {
        $ujianId = $this->request->getVar('ujian_id');
        if (!$ujianId) {
            return $this->failValidationErrors('Ujian ID wajib diisi.');
        }

        $peserta = $this->getPesertaFromToken();
        if (!$peserta || !isset($peserta['id'])) {
            return $this->failUnauthorized('Token tidak valid atau peserta tidak ditemukan.');
        }

        $validasi = $this->validasiUjianPeserta($ujianId, $peserta['id'], 'lanjut');
        if (isset($validasi['error'])) {
            return $this->fail(['message' => $validasi['error']]);
        }

        return $this->respond([
            'success' => true,
            'message' => 'Lanjut ujian.',
            'data' => [
                'status_ujian' => 'lanjut',
                'ujian' => $validasi['ujian'],
                'hasil' => $validasi['hasil'],
                'peserta' => $peserta,
            ]
        ]);
    }


    private function validasiUjianPeserta(string $ujianId, string $pesertaId, string $mode = 'mulai')
    {
        $ujian = $this->ujianModel
            ->select('ujian.*, bank_soal.nama as nama_bank_soal')
            ->join('bank_soal', 'bank_soal.id = ujian.bank_soal_id')
            ->find($ujianId);

        if (!$ujian) return ['error' => 'Ujian tidak ditemukan.'];

        $now = date('Y-m-d H:i:s');
        if ($now < $ujian['waktu_mulai']) return ['error' => 'Ujian belum dimulai.'];
        if ($now > $ujian['waktu_selesai']) return ['error' => 'Waktu ujian sudah berakhir.'];

        $hasil = $this->hasilUjianModel
            ->where('ujian_id', $ujianId)
            ->where('peserta_id', $pesertaId)
            ->first();

        if (!$hasil) return ['error' => 'Data hasil ujian belum tersedia untuk peserta ini.'];
        if ($hasil['status'] === 'selesai') return ['error' => 'Kamu sudah menyelesaikan ujian ini.'];

        if ($ujian['pakai_token'] == '1' && !$hasil['token_valid']) {
            return ['error' => 'Token belum diverifikasi.'];
        }

        if ($mode === 'mulai' && $hasil['status'] === 'sedang_ujian') {
            return ['error' => 'Ujian sudah dimulai sebelumnya.'];
        }

        if ($mode === 'lanjut' && $hasil['status'] !== 'sedang_ujian') {
            return ['error' => 'Ujian belum dimulai.'];
        }

        $perluUpdate = [];
        $statusSebelum = $hasil['status'];

        $soalList = json_decode($hasil['urutan_soal'] ?? '[]', true);
        if (!is_array($soalList) || count($soalList) === 0) {
            $soalList = $this->soalModel->getSoalIdsByBank($ujian['bank_soal_id']);
            if ($ujian['acak_soal'] == '1') shuffle($soalList);
            $perluUpdate['urutan_soal'] = json_encode($soalList);
            $hasil['urutan_soal'] = $perluUpdate['urutan_soal'];
        }

        $opsiMap = json_decode($hasil['urutan_opsi'] ?? '{}', true);
        if (!is_array($opsiMap) || count($opsiMap) === 0) {
            $opsiMap = $this->soalModel->getOpsiOrderMap($ujian['bank_soal_id'], $ujian['acak_opsi'] == '1');
            $perluUpdate['urutan_opsi'] = json_encode($opsiMap);
            $hasil['urutan_opsi'] = $perluUpdate['urutan_opsi'];
        }

        if ($mode === 'mulai' && $statusSebelum !== 'sedang_ujian') {
            $perluUpdate['status'] = 'sedang_ujian';
            $perluUpdate['waktu_mulai'] = $now;
            $hasil['status'] = 'sedang_ujian';
            $hasil['waktu_mulai'] = $now;
        }

        if (!empty($perluUpdate)) {
            $this->hasilUjianModel->update($hasil['id'], $perluUpdate);
        }

        return [
            'ujian' => $ujian,
            'hasil' => $hasil,
            'status_awal' => $statusSebelum
        ];
    }


    public function soal($ujianId)
    {
        $peserta = $this->getPesertaFromToken();
        if (!$peserta) return $this->failUnauthorized('Token tidak valid.');

        // Validasi status ujian peserta
        $validasi = $this->validasiUjianPeserta($ujianId, $peserta['id'], 'getsoal');
        if (isset($validasi['error'])) {
            return $this->respond([
                'success' => false,
                'message' => $validasi['error'],
                'data' => null
            ]);
        }

        $hasil = $validasi['hasil'];
        $soalIds = json_decode($hasil['urutan_soal'], true);
        $opsiUrutan = json_decode($hasil['urutan_opsi'], true);

        if (!is_array($soalIds) || !is_array($opsiUrutan)) {
            return $this->respond([
                'success' => false,
                'message' => 'Data urutan soal atau opsi tidak valid.',
                'data' => null
            ]);
        }

        $soal = $this->soalModel->getSoalByUrutan($soalIds, $opsiUrutan);

        // Hilangkan kunci jawaban
        foreach ($soal as &$s) {
            foreach ($s['opsi'] as &$op) {
                unset($op['is_true']);
            }
        }

        return $this->respond([
            'success' => true,
            'message' => 'Soal berhasil diambil.',
            'data' => [
                'jumlah_soal' => count($soal),  // <- Tambahkan ini
                'soal' => $soal
            ]
        ]);
    }
    public function listUjian()
    {
        $peserta = $this->getPesertaFromToken();
        if (!$peserta) {
            return $this->respond([
                'success' => false,
                'message' => 'Token tidak valid.',
                'data' => null
            ]);
        }

        $data = $this->ujianModel->getAllPeserta($peserta['id']);
        $now = date('Y-m-d H:i:s');

        foreach ($data as &$ujian) {
            $waktuMulai = $ujian['waktu_mulai'];
            $waktuSelesai = $ujian['waktu_selesai'];

            // Status waktu ujian
            if ($now < $waktuMulai) {
                $ujian['status_waktu'] = 'belum_mulai';
            } elseif ($now >= $waktuMulai && $now <= $waktuSelesai) {
                $ujian['status_waktu'] = 'dibuka';
            } else {
                $ujian['status_waktu'] = 'terlambat';
            }

            $hasil = $this->hasilUjianModel
                ->where('ujian_id', $ujian['id'])
                ->where('peserta_id', $peserta['id'])
                ->first();

            // Status aktivitas peserta
            if ($hasil) {
                if ($hasil['status'] === 'selesai') {
                    $ujian['status_peserta'] = 'selesai';
                    $ujian['status_waktu'] = 'selesai'; // tidak relevan cek waktu
                } elseif ($hasil['status'] === 'sedang_ujian') {
                    $ujian['status_peserta'] = 'sedang_mengerjakan';
                    $ujian['status_waktu'] = 'dibuka'; // masih dalam sesi
                } else {
                    $ujian['status_peserta'] = 'belum_mulai';
                }

                $ujian['waktu_mulai_ujian'] = $hasil['waktu_mulai'];
            } else {
                $ujian['status_peserta'] = 'belum_mulai';

                // Baru cek status waktu jika belum ada hasil
                if ($now < $waktuMulai) {
                    $ujian['status_waktu'] = 'belum_mulai';
                    $ujian['waktu_mulai_ujian'] = null;
                } elseif ($now >= $waktuMulai && $now <= $waktuSelesai) {
                    $ujian['status_waktu'] = 'dibuka';
                } else {
                    $ujian['status_waktu'] = 'terlambat';
                }
            }
        }

        return $this->respond([
            'success' => true,
            'message' => 'Berhasil mengambil daftar ujian.',
            'data' => $data
        ]);
    }
    public function simpanJawaban()
    {
        $peserta = $this->getPesertaFromToken();
        if (!$peserta || !isset($peserta['id'])) {
            return $this->failUnauthorized('Token tidak valid.');
        }

        $post = $this->request->getJSON(true);
        $ujianId = $post['ujian_id'] ?? null;
        $jawabanInput = $post['jawaban'] ?? null;

        if (!$ujianId || !is_array($jawabanInput)) {
            return $this->fail('Data tidak lengkap atau format salah.');
        }

        foreach ($jawabanInput as $soalId => $jawaban) {
            // Deteksi format jawaban
            if (is_array($jawaban)) {
                $isAssoc = array_keys($jawaban) !== range(0, count($jawaban) - 1);

                if ($isAssoc) {
                    // Menjodohkan atau Benar/Salah
                    $jawabanJson = json_encode($jawaban, JSON_UNESCAPED_UNICODE);
                } else {
                    // MPG (Multiple pilihan ganda)
                    $jawabanJson = json_encode(['values' => $jawaban], JSON_UNESCAPED_UNICODE);
                }
            } else {
                // PG, Esai, Isian
                $jawabanJson = json_encode(['value' => $jawaban], JSON_UNESCAPED_UNICODE);
            }

            $data = [
                'id' => Uuid::uuid4()->toString(),
                'ujian_id' => $ujianId,
                'peserta_id' => $peserta['id'],
                'soal_id' => $soalId,
                'jawaban' => $jawabanJson,
                'skor' => 0,
            ];

            $this->jawabanModel->updateOrInsert([
                'ujian_id' => $ujianId,
                'peserta_id' => $peserta['id'],
                'soal_id' => $soalId,
            ], $data);
        }

        return $this->respond([
            'success' => true,
            'message' => 'Jawaban berhasil disimpan',
        ]);
    }



    public function getJawabanPeserta($ujianId)
    {
        $peserta = $this->getPesertaFromToken();
        if (!$peserta['success']) {
            return $this->failUnauthorized($peserta['message']);
        }

        $rows = $this->jawabanModel
            ->where('ujian_id', $ujianId)
            ->where('peserta_id', $peserta['id'])
            ->findAll();

        $data = [];
        foreach ($rows as $row) {
            $decoded = json_decode($row['jawaban'], true);
            $data[$row['soal_id']] = json_last_error() === JSON_ERROR_NONE ? $decoded : $row['jawaban'];
        }

        return $this->respond([
            'success' => true,
            'message' => 'Jawaban berhasil diambil',
            'data' => $data
        ]);
    }

    public function selesaiUjian()
    {
        $peserta = $this->getPesertaFromToken();
        if (!$peserta || !isset($peserta['id'])) {
            return $this->failUnauthorized("Token tidak valid.");
        }

        $pesertaId = $peserta['id'];
        $pesertaId = $peserta['id'];
        $input = $this->request->getJSON(true);
        $ujianId = $input['ujian_id'] ?? null;

        if (!$ujianId) {
            return $this->fail('ujian_id wajib dikirim.');
        }


        $hasilUjian = $this->hasilUjianModel
            ->where('ujian_id', $ujianId)
            ->where('peserta_id', $pesertaId)
            ->first();

        if (!$hasilUjian || $hasilUjian['status'] !== 'sedang_ujian') {
            return $this->fail('Data hasil tidak ditemukan atau status tidak valid.');
        }

        $ujian = $this->ujianModel->find($ujianId);
        if (!$ujian) {
            return $this->fail('Data ujian tidak ditemukan.');
        }

        $ujian = $this->ujianModel->find($ujianId);
        $daftarSoal = $this->soalModel->where('bank_soal_id', $ujian['bank_soal_id'])->findAll();
        $jawabanPesertaList = $this->jawabanModel
            ->where('ujian_id', $ujianId)
            ->where('peserta_id', $pesertaId)
            ->findAll();

        // Validasi durasi minimal
        $waktuMulai = strtotime($hasilUjian['waktu_mulai']);
        $waktuSekarang = time();
        $durasiMenit = ($waktuSekarang - $waktuMulai) / 60;
        $minimalDurasi = (int) ($ujian['minimal_durasi'] ?? 0);

        // if ($minimalDurasi > 0 && $durasiMenit < $minimalDurasi) {
        //     return $this->fail("Belum saatnya untuk menyelesaikan ujian. Silakan baca dan pelajari kembali soal yang ada.");
        // }

        $jawabanMap = [];
        foreach ($jawabanPesertaList as $jawaban) {
            $jawabanMap[$jawaban['soal_id']] = json_decode($jawaban['jawaban'], true);
        }

        $totalPoinBenar = 0;
        $totalBobotMaks = 0;
        $jumlahSoalBenar = 0;
        $koreksiPerSoal = [];
        $arsipJawaban = [];

        foreach ($daftarSoal as $soal) {
            $soalId = $soal['id'];
            $jenis = $soal['jenis_soal'];
            $daftarOpsi = $this->soalOpsiModel->where('soal_id', $soalId)->findAll();
            $jawaban = $jawabanMap[$soalId] ?? null;

            $skorDiperoleh = 0;
            $isBenar = false;

            switch ($jenis) {
                case 'pg':
                    $labelJawaban = $jawaban['value'] ?? null;
                    $opsiDipilih = array_filter($daftarOpsi, fn($o) => $o['label'] === $labelJawaban);
                    $bobot = $opsiDipilih ? (int) array_values($opsiDipilih)[0]['bobot'] : 0;
                    $skorDiperoleh = $bobot;

                    $opsiBenar = array_filter($daftarOpsi, fn($o) => isset($o['is_true']) && $o['is_true']);
                    $labelBenar = $opsiBenar ? array_values($opsiBenar)[0]['label'] ?? null : null;
                    $isBenar = $labelJawaban === $labelBenar;
                    break;

                case 'mpg':
                    $jawabanList = $jawaban['values'] ?? [];

                    foreach ($jawabanList as $label) {
                        $opsi = array_values(array_filter($daftarOpsi, fn($o) => $o['label'] === $label));
                        if ($opsi) {
                            $bobot = (int) $opsi[0]['bobot'];
                            $skorDiperoleh += $bobot;
                        }
                    }

                    $labelBenar = array_column(array_filter($daftarOpsi, fn($o) => !empty($o['is_true'])), 'label');
                    sort($labelBenar);
                    $labelJawaban = $jawabanList;
                    sort($labelJawaban);
                    $isBenar = $labelBenar === $labelJawaban;
                    break;

                case 'benar_salah':
                    $skorDiperoleh = 0;
                    $isBenar = true;

                    foreach ($daftarOpsi as $opsi) {
                        $label = $opsi['label'];
                        $jawabanPeserta = $jawaban[$label] ?? null;
                        $jawabanBenar = $opsi['is_true'] ? 'Benar' : 'Salah';
                        $bobot = (int) $opsi['bobot'];

                        if ($jawabanPeserta === $jawabanBenar) {
                            $skorDiperoleh += $bobot;
                        } else {
                            if ($bobot < 0) $skorDiperoleh += $bobot;
                            $isBenar = false;
                        }
                    }
                    break;

                case 'jodohkan':
                    $skorDiperoleh = 0;
                    $isBenar = true;

                    foreach ($daftarOpsi as $opsi) {
                        $jawab = $jawaban[$opsi['label']] ?? null;
                        $bobot = (int) $opsi['bobot'];

                        if ($jawab === $opsi['pasangan']) {
                            $skorDiperoleh += $bobot;
                        } else {
                            if ($bobot < 0) $skorDiperoleh += $bobot;
                            $isBenar = false;
                        }
                    }
                    break;

                case 'esai':
                case 'isian':
                    $jawabanUser = is_array($jawaban) ? ($jawaban['value'] ?? '') : (string) $jawaban;
                    $kunciJawaban = json_decode($soal['jawaban'], true) ?? [];
                    $isBenar = $this->cocokIsian($jawabanUser, $kunciJawaban);
                    $skorDiperoleh = $isBenar ? (int) $soal['bobot'] : 0;
                    $totalBobotMaks += (int) $soal['bobot'];
                    break;
            }

            if ($jenis === 'jodohkan') {
                foreach ($daftarOpsi as $opsi) {
                    if ((int) $opsi['bobot'] > 0) {
                        $totalBobotMaks += (int) $opsi['bobot'];
                    }
                }
            } else {
                foreach ($daftarOpsi as $opsi) {
                    if (!empty($opsi['is_true']) && (int) $opsi['bobot'] > 0) {
                        $totalBobotMaks += (int) $opsi['bobot'];
                    }
                }
            }

            $totalPoinBenar += $skorDiperoleh;
            if ($isBenar) $jumlahSoalBenar++;

            $koreksiPerSoal[] = [
                'soal_id' => $soalId,
                'jenis' => $jenis,
                'jawaban' => $jawaban,
                'is_benar' => $isBenar,
                'bobot_diperoleh' => $skorDiperoleh
            ];

            if ($jawaban !== null) {
                $arsipJawaban[$soalId] = is_array($jawaban)
                    ? array_merge($jawaban, [
                        'is_benar' => $isBenar,
                        'poin' => $skorDiperoleh
                    ]) : [
                        'value' => $jawaban,
                        'is_benar' => $isBenar,
                        'poin' => $skorDiperoleh
                    ];
            }
        }

        $nilaiAkhir = $totalBobotMaks > 0 ? round(($totalPoinBenar / $totalBobotMaks) * 100, 2) : 0;
        $jumlahSoalSalah = count($daftarSoal) - $jumlahSoalBenar;
        $totalPoinSalah = $totalBobotMaks - $totalPoinBenar;

        $this->hasilUjianModel->update($hasilUjian['id'], [
            'status' => 'selesai',
            'waktu_selesai' => date('Y-m-d H:i:s'),
            'nilai_total' => $nilaiAkhir,
            'poin_benar' => $totalPoinBenar,
            'poin_salah' => $totalPoinSalah,
            'poin_maksimal' => $totalBobotMaks,
            'soal_benar' => $jumlahSoalBenar,
            'soal_salah' => $jumlahSoalSalah,
            'jawaban_json' => json_encode($arsipJawaban),
        ]);

        return $this->respond([
            'success' => true,
            'message' => 'Ujian telah diselesaikan dan dikoreksi.',
            'data' => [
                'jumlah_soal' => count($daftarSoal),
                'poin_benar' => $totalPoinBenar,
                'poin_salah' => $totalPoinSalah,
                'poin_maksimal' => $totalBobotMaks,
                'soal_benar' => $jumlahSoalBenar,
                'soal_salah' => $jumlahSoalSalah,
                'nilai' => $nilaiAkhir,
                'koreksi_detail' => $koreksiPerSoal
            ]
        ]);
    }
    protected function cocokIsian(string $jawabanUser, array $kunciJawaban): bool
    {
        $normalizedUser = $this->normalizeTextAdvanced($jawabanUser);

        foreach ($kunciJawaban as $kunci) {
            if ($normalizedUser === $this->normalizeTextAdvanced($kunci)) {
                return true;
            }
        }

        return false;
    }

    protected function normalizeTextAdvanced(?string $text): string
    {
        $text = strtolower(trim($text ?? ''));

        // Hilangkan semua tanda baca (kecuali huruf dan angka)
        $text = preg_replace('/[^\p{L}\p{N}\s]/u', '', $text);

        // Ganti spasi ganda atau tab dengan satu spasi
        $text = preg_replace('/\s+/', ' ', $text);

        return $text;
    }
}
