<?php

namespace App\Controllers\Panel\App\Install;

use App\Controllers\Panel\BC;
use CodeIgniter\API\ResponseTrait;
use Exception;
use ZipArchive;

class Init extends BC
{
    use ResponseTrait;
    private $sesspath, $db;
    private $role = [
        ['name' => 'admin', 'description' => 'Administrator'],
        ['name' => 'guru', 'description' => 'Guru'],
        ['name' => 'pengawas', 'description' => 'Pengawas'],
        ['name' => 'piket', 'description' => 'Piket'],
        ['name' => 'kepsek', 'description' => 'Kepala Sekolah'],
        ['name' => 'karyawan', 'description' => 'Karyawan'],
    ];
    private $config = null;
    public function __construct()
    {
        parent::__construct();
        $this->db = db_connect();
        $this->sesspath = WRITEPATH . 'session';
    }
    public function index()
    {
        if (options('appname')) return view('Utils/404');
        $data['page_title'] = "Install";
        $data['no_sidebar'] = true;
        return view('Panel/App/Install/Init', $data);
    }

    public function new()
    {
        if (!$this->request->isAJAX() && ENVIRONMENT != 'development') {
            exit('No direct script access allowed');
        }
        if (options('appname')) return view('Utils/404');
        if ($this->request->getVar('namasekolah')) $opt['namasekolah'] = $this->request->getVar('namasekolah');
        if ($this->request->getVar('npsn')) $opt['npsn'] = $this->request->getVar('npsn');
        if ($this->request->getVar('tid')) $opt['tid'] = $this->request->getVar('tid');
        $valid = checkLicense($this->request->getVar('licensekey'), $opt ?? []);
        if ($valid['state'] != 1) {
            if (!isset($valid['requiredFields'])) $valid['requiredFields'] = [];
            if ($this->request->getVar('tid') && !in_array('tid', $valid['requiredFields'])) $valid['requiredFields'][] = 'tid';
            if ($this->request->getVar('npsn') && !in_array('npsn', $valid['requiredFields'])) $valid['requiredFields'][] = 'npsn';
            if ($this->request->getVar('namasekolah') && !in_array('namasekolah', $valid['requiredFields'])) $valid['requiredFields'][] = 'namasekolah';
            return $this->fail(['csrf' => csrf_hash(), 'message' => $valid['msg'], 'requiredFields' => $valid['requiredFields'] ?? false], 403);
        }
        $this->migrate();
        try {
            if (file_exists($this->sesspath)) {
                foreach (scandir($this->sesspath) as $key => $value) {
                    if (file_exists("{$this->sesspath}/$value") && !in_array($value, ['.', '..'])) {
                        unlink("{$this->sesspath}/$value");
                    }
                }
            }
            $ca = config('app');
            if (!file_exists($ca->audioPath + "/soal")) {
                mkdir($ca->audioPath + "/soal", 0777, true);
            }
            if (!file_exists($ca->imgPath + "/soal")) {
                mkdir($ca->imgPath + "/soal", 0777, true);
            }
            if (!file_exists($ca->pdfPath + "/soal")) {
                mkdir($ca->pdfPath + "/soal", 0777, true);
            }
            if (!file_exists($ca->videoPath + "/soal")) {
                mkdir($ca->videoPath + "/soal", 0777, true);
            }
        } catch (\Throwable $th) {
        }
        $db = db_connect();
        $now = date("Y-m-d H:i:s");
        $db->transStart();
        $db->table("users")->insert([
            'id' => 1,
            'username' => $this->request->getVar('username'),
            'fullname' => $this->request->getVar('fullname'),
            'password_hash' => setPassword($this->request->getVar('password')),
            'status' => 1,
            'created_at' => $now,
            'updated_at' => $now
        ]);
        $db->table("roles")->insertBatch($this->role);
        $db->table("users_roles")->insert(['role_id' => 1, 'user_id' => 1]);
        $map_data = ['lat' => -1.669685500986571, 'lon' => 118.43261718750001, 'student_radius' => 20, 'staff_radius' => 30];
        $opt = [
            ['option_name' => 'rfid_latest', 'option_value' => '', 'autoload' => "no"],
            ['option_name' => 'sck', 'option_value' => generateRandomString(10), 'autoload' => "no"],
            ['option_name' => 'appname', 'option_value' => $this->request->getVar('appname'), 'autoload' => "yes"],
            ['option_name' => 'display_video', 'option_value' => '', 'autoload' => "no"],
            ['option_name' => 'onesignal_config', 'option_value' => json_encode(['app_id' => '', 'safari_web_id' => '', 'key' => '']), 'autoload' => "no"],
            ['option_name' => 'attendance_siswa', 'option_value' => json_encode(['timeIn' => '', 'timeOut' => '']), 'autoload' => "no"],
            ['option_name' => 'attendance_staff', 'option_value' =>  json_encode(['timeIn' => '', 'timeOut' => '']), 'autoload' => "no"],
            ['option_name' => 'attendance_qr', 'option_value' => generateRandomString(10), 'autoload' => "no"],
            ['option_name' => 'attendance_map_data', 'option_value' => json_encode($map_data), 'autoload' => "no"],
            ['option_name' => 'licensekey', 'option_value' => $this->request->getVar('licensekey'), 'autoload' => "yes"],
            ['option_name' => 'alamat', 'option_value' => null, 'autoload' => "no"], ['option_name' => 'telp', 'option_value' => null, 'autoload' => "no"],
            ['option_name' => 'display_news', 'option_value' => '', 'autoload' => "no"], ['option_name' => 'el_mode', 'option_value' => null, 'autoload' => "yes"],
            ['option_name' => 'maintenance', 'option_value' => '1', 'autoload' => "yes"], ['option_name' => 'tinymcekey', 'option_value' => null, 'autoload' => "no"],
            ['option_name' => 'chat', 'option_value' => 0, 'autoload' => "yes"],
            ['option_name' => 'kecamatan', 'option_value' => null, 'autoload' => "no"], ['option_name' => 'website', 'option_value' => null, 'autoload' => "no"],
            ['option_name' => 'kota', 'option_value' => null, 'autoload' => "no"], ['option_name' => 'nip_kepala_sekolah', 'option_value' => null, 'autoload' => "no"],
            ['option_name' => 'email', 'option_value' => null, 'autoload' => "no"], ['option_name' => 'nama_kepala_sekolah', 'option_value' => null, 'autoload' => "no"],
            ['option_name' => 'npsn', 'option_value' => $valid['NPSN'], 'autoload' => "no"],
            ['option_name' => 'eat', 'option_value' => $valid['expired_at'], 'autoload' => "yes"],
            ['option_name' => 'logo', 'option_value' => 'logo.png', 'autoload' => "yes"],
            ['option_name' => 'panel_session_timeout', 'option_value' => 7, 'autoload' => "yes"],
            ['option_name' => 'panel_session_unit', 'option_value' => 'days', 'autoload' => "yes"],
            ['option_name' => 'mobile_session_timeout', 'option_value' => 15, 'autoload' => "yes"],
            ['option_name' => 'mobile_session_unit', 'option_value' => 'hours', 'autoload' => "yes"],
            ['option_name' => 'kementrian_image', 'option_value' => 'kementrian.png', 'autoload' => "no"],
            ['option_name' => 'ttd_image', 'option_value' => 'ttd.png', 'autoload' => "no"],
            ['option_name' => 'browserCheck', 'option_value' => 0, 'autoload'   => "yes"],
            ['option_name' => 'nama_sekolah', 'option_value' => $valid['NamaSekolah'], 'autoload' => "yes"],
            ['option_name' => 'exambro_version', 'option_value' => '1', 'autoload' => "no"],
            ['option_name' => 'strict_browser', 'option_value' => '0', 'autoload' => "no"],
            ['option_name' => 'enable_app_scan_qr', 'option_value' => '1', 'autoload' => "no"],
            ['option_name' => 'passcode_locked_app', 'option_value' => '123456', 'autoload' => "no"],
            [
                'option_name' => 'redis-config', 'option_value' => json_encode([
                    'state'   => false,
                    'host'   => "localhost",
                    'port'   => 6379,
                    'password' => "",
                    'database' => 0,
                ]), 'autoload' => "yes"
            ],
            [
                'option_name' => 'whatsapp',
                'option_value' =>
                json_encode(
                    [
                        'message_siswa_presensi_masuk' => "",
                        'message_siswa_presensi_keluar' => "",
                        "siswa_presensi_masuk" => false,
                        "siswa_presensi_keluar" => false,
                        'randomize' => false,
                    ]
                ),
                'autoload' => "no"
            ],
        ];
        $db->table("options")->insertBatch($opt);
        $db->transComplete();
        if ($db->transStatus() === FALSE) {
            $this->regress();
            return $this->fail(['csrf' => csrf_hash(), 'message' => "Gagal Install Aplikasi"], 500);
        }
        $manifest = json_decode(file_get_contents(FCPATH . "manifest.json"), true);
        $manifest['name'] = $this->request->getVar('appname');
        $manifest['start_url'] = trim(base_url(), '/');
        $manifest['short_name'] = $this->request->getVar('appname');
        $manifest['description'] = $this->request->getVar('appname') . ($this->request->getVar('nama_sekolah') ? ' - ' . $this->request->getVar('nama_sekolah') : '');
        file_put_contents(FCPATH . "manifest.json", json_encode($manifest, JSON_UNESCAPED_SLASHES));
        return $this->respond(['message' => "Berhasil Install"]);
    }

    public function delete_database()
    {
        $db = \Config\Database::connect();
        $tables = $db->listTables();
        if (!$tables) return;
        $db->transStart();
        $db->query("SET FOREIGN_KEY_CHECKS=0");
        $db->query("DROP TABLE " . implode(',', $tables));
        $db->query("SET FOREIGN_KEY_CHECKS=1");
        $db->transComplete();
    }

    public function restore()
    {
        if (!$this->request->isAJAX() && ENVIRONMENT != 'development') {
            exit('No direct script access allowed');
        }
        set_time_limit(60 * 20);
        if (options('appname')) return view('Utils/404');
        helper('Panel/utils');
        $bf = $this->request->getFile('dbfile');
        if (!$bf->isValid()) return $this->fail(['csrf' => csrf_hash(), 'message' => "File Tidak Valid"], 403);
        $dbData = [];
        $restorePath = WRITEPATH . "db/restores";
        $folName = '';
        try {
            $za = new ZipArchive();
            $filePath = $bf->getPath() . "/" . $bf->getFilename();
            $za->open($filePath);
            $za->extractTo($restorePath);
            $folName = explode('/', $za->statIndex(1)['name'])[0];
            $za->close();
            if (!file_exists("$restorePath/$folName/db")) throw new Exception("Data tidak valid!");
            foreach (scandir("$restorePath/$folName/db") as $key => $value) {
                if (in_array($value, ['.', '..'])) continue;
                $dbData[] = [
                    'name' => $value,
                    'value' => file_get_contents("$restorePath/$folName/db/$value")
                ];
            }
            if (file_exists("$restorePath/$folName/config.json")) {
                $this->config = json_decode(file_get_contents("$restorePath/$folName/config.json"), true);
            }
        } catch (\Throwable $e) {
            if (file_exists($restorePath)) {
                rrmdir($restorePath);
                mkdir($restorePath);
                file_put_contents("$restorePath/index.html", "");
            }
            if (ENVIRONMENT != 'production') {
                throw new Exception($e);
            } else {
                return $this->fail(['csrf' => csrf_hash(), 'message' => "Terjadi Kesalahan Ekstrak File. $e->getMessage()"], 403);
            }
        }
        if (!$this->config) return $this->fail(['message' => "Data tidak bisa di pulihkan di versi ini", 'csrf' => csrf_hash()]);
        if (!in_array($this->config['app_version_code'], $this->allowedRestoreVersion)) return $this->fail(['message' => "Data hanya untuk versi {$this->config['app_version_name']}", 'csrf' => ['name' => csrf_token(), 'value' => csrf_hash()]]);
        $this->migrate();
        $seeder = $this->seederBackup($dbData);
        if (!$seeder) {
            $this->regress();
            return $this->fail(['csrf' => csrf_hash(), 'message' => "Terjadi Kesalahan Memasukan Data."], 403);
        }
        if (file_exists($this->sesspath)) {
            foreach (scandir($this->sesspath) as $key => $value) {
                if (file_exists("{$this->sesspath}/$value") && !in_array($value, ['.', '..'])) {
                    unlink("{$this->sesspath}/$value");
                }
            }
        }
        if (file_exists("$restorePath/$folName/public")) {
            recurse_copy("$restorePath/$folName/public", FCPATH);
        }
        rrmdir("$restorePath/$folName");
        file_put_contents(WRITEPATH . "cache/lct", time());
        try {
            $manifest = json_decode(file_get_contents(FCPATH . "manifest.json"), true);
            $manifest['name'] = options('appname');
            $manifest['start_url'] = trim(base_url(), '/');
            $manifest['short_name'] = options('appname');
            $manifest['description'] = options('appname') . (options('nama_sekolah') ? ' - ' . options('nama_sekolah') : '');
            file_put_contents(FCPATH . "manifest.json", json_encode($manifest, JSON_UNESCAPED_SLASHES));
        } catch (\Throwable $th) {
            return $this->fail(['csrf' => csrf_hash(), 'message' => $th->getMessage()], 500);
        }
        return $this->respond(['message' => "Berhasil Install"]);
    }

    private function seederBackup($data)
    {
        $db = db_connect();
        $tbln = null;
        try {
            $firstData = [];
            $tables = [];
            foreach ($data as $d) {
                $d['name'] = str_replace('.json', '', $d['name']);
                if (!in_array($d['name'], $this->firstTable)) {
                    $tables[] = $d;
                } else {
                    $firstData[array_search($d['name'], $this->firstTable)] = $d;
                }
            }
            ksort($firstData, SORT_REGULAR);
            $tables = array_merge($firstData, $tables);
            $db->transStart();
            foreach ($tables as $table) {
                $tbln = $table['name'];
                $table['name'] = str_replace('.json', '', $table['name']);
                if ($table['name'] == 'migrations') {
                    continue;
                }
                $data = json_decode($table['value'], true);
                if ($data) {
                    $db->query('DELETE FROM ' . $table['name']);
                    if ($table['name'] == 'm_siswa') {
                        $temp = [];
                        foreach ($data as $key => $value) {
                            if ($value['password_hash']) $value['password_hash'] = hex2bin($value['password_hash']);
                            $temp[] = $value;
                        }
                        $data = $temp;
                    } else if ($table['name'] == 'draft_ujian') {
                        $temp = [];
                        foreach ($data as $key => $value) {
                            if (isset($value['bobot_pg'])) {
                                $value['bobot_tf'] = $value['bobot_pg'];
                                unset($value['bobot_pg']);
                            }
                            $temp[] = $value;
                        }
                        $data = $temp;
                    }
                    if ($db->tableExists($table['name'])) {
                        $db->table($table['name'])->insertBatch($data);
                    }
                }
            }
            $db->transComplete();
            if ($db->transStatus() === FALSE) return false;
            return true;
        } catch (\Throwable $e) {
            if (ENVIRONMENT != 'production') {
                throw new \RuntimeException("Table : $tbln
                " . $e);
            } else {
                return false;
            }
        }
    }
    private function migrate()
    {
        $migrate = \Config\Services::migrations();
        try {
            $this->delete_database();
            $migrate->ensureTable();
            $migrate->latest();
            return true;
        } catch (\Throwable $e) {
            if (ENVIRONMENT != 'production') {
                throw new \RuntimeException($e);
            } else {
                return false;
            }
        }
    }

    private function regress()
    {
        $migrate = \Config\Services::migrations();
        $migrate->regress();
    }
}
