<?php

namespace App\Controllers\Panel\BankSoal;

use App\Controllers\Panel\BC;
use App\Libraries\Adityampc\DocxToHtml;
use App\Models\Panel\BankSoal;
use CodeIgniter\API\ResponseTrait;
use finfo;
use ZipArchive;

class Import extends BC
{
    use ResponseTrait;
    private $filesSearch = [];
    private $filesReplace = [];
    private $id, $bankSoalModel, $filename, $path, $mime, $files;
    public function __construct()
    {
        parent::__construct();
        $this->bankSoalModel = new BankSoal();
    }
    public function run()
    {
        if (!$this->request->isAJAX() && ENVIRONMENT != 'development') {
            exit('No direct script access allowed');
        }
        $file = $this->request->getFile('file');
        $allowedFile = [
            'application/zip',
            'application/x-zip-compressed',
            'multipart/x-zip',
            'application/vnd.ms-excel',
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'application/octet-stream'
        ];
        if ($file->hasMoved()) return $this->failNotFound("File Tidak Ditemukan");
        if (!in_array($file->getMimeType(), $allowedFile)) return $this->failForbidden("Format Tidak Diizinkan");
        $this->filename = $file->getClientName();
        $this->path = WRITEPATH . "file/soal";
        $this->mime = $file->getMimeType();
        $this->id = $this->request->getPost('id');
        $bank = (new BankSoal())->find($this->id);
        if (!$bank) return $this->fail(['message' => 'Soal Tidak Valid!']);
        $file->move($this->path, null, true);
        rmbfe('php', $this->path);

        setLog("Mengunggah soal ke $bank[kode]");
        if ($this->mime == 'application/zip' || $this->mime == 'application/x-zip-compressed' || $this->mime == 'multipart/x-zip') {
            return $this->zip();
        }
        switch ($this->mime) {
            case 'application/vnd.ms-excel':
                return $this->excel();
            case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
                return $this->excel();
            case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
                return $this->docx();
            case 'application/octet-stream':
                return $this->docx();
        }
        return $this->fail(['message' => 'File Tidak Valid!']);
    }

    private function docx()
    {
        $ppp = ["<p><p>", "<p></p>", "<p><p></p>", "</p><p><p></p>", "<p></p><p></p>"];
        $app = config('app');
        $assetPath['audio'] = FCPATH . "{$app->audioPath}/soal";
        $assetPath['image'] = FCPATH . "{$app->imgPath}/soal";
        $src = "{$this->path}/{$this->filename}";
        $tempPath = WRITEPATH . "uploads/" . pathinfo($src, PATHINFO_FILENAME);
        $docxToHtml = new DocxToHtml($src, $tempPath, $assetPath);
        try {
            $soal = $docxToHtml->run();
        } catch (\Throwable $th) {
            return $this->fail(['message' => $th->getMessage()]);
        }
        $db  = db_connect();
        $db->transStart();
        $db->table('soal_data')->where('bank_id', $this->id)->delete();
        foreach ($soal as $k => $s) {
            if ((empty($s["question"]) || in_array($s["question"], $ppp)) && $s['type'] != 4) continue;
            $s['question'] = str_replace(["{…}", "{&hellip;}"], ["{...}", "{...}"], $s['question']);
            $si = $db->table('soal_data')->insert(['bank_id' => $this->id, 'shortentry' => $s['shortentry'], 'true_default_point' => $s['true_default_point'], 'false_default_point' => $s['false_default_point'], 'jenis_soal' => $s['type'], 'question' => $s["question"], 'created_at' => time(), 'updated_at' => time()]);
            $si = $db->insertID();
            $coupledKeys = [];
            foreach ($s['couples'] as $k => $c) {
                $db->table('soal_data_couple')->insert([
                    'soal_id' => $si,
                    'body' => $c['body'],
                ]);
                $coupledKeys[$c['id']] = $db->insertID();
            }
            $responds = [];
            foreach ($s['responds'] as $k => $v) {
                $responds[] = [
                    "soal_id" => $si,
                    "soal_couple_id" => isset($coupledKeys[$v['soal_couple_id']]) ? $coupledKeys[$v['soal_couple_id']] : null,
                    "body" => $v['body'],
                    "is_key" => $v['is_key'],
                ];
            }
            if ($responds) {
                $db->table('soal_opsi')->insertBatch($responds);
            }
        }
        $db->transComplete();
        if ($db->transStatus() === FALSE) {
            $db->transRollback();
            return $this->fail(['message' => "Gagal Memproses Data"]);
        }
        return $this->respond(['message' => "Berhasil Memproses Data"]);
    }
    private function zip()
    {
        $za = new ZipArchive();
        $filename = $this->filename;
        $app = config('app');
        $filePath = "$this->path/$filename";
        $za->open($filePath);
        $soal = null;
        $this->files = [];
        for ($i = 0; $i < $za->numFiles; $i++) {
            $stat = $za->statIndex($i);
            $n = explode('.', basename($stat['name']));
            if (end($n) == 'xlsx' || end($n) == 'xls') {
                $soal = basename($stat['name']);
            } else {
                $this->files[basename($stat['name'])] = uniqid($i) . "." . end($n);
            }
        }
        if (!$soal) return $this->fail(['message' => 'Soal Tidak Ditemukan!']);
        $za->extractTo("$this->path");
        $za->close();
        foreach ($this->files as $key => $value) {
            $n = explode('.', $value);
            $ext = strtolower(end($n));
            if ($ext == 'mp3') {
                rename("$this->path/$key", FCPATH . "$app->audioPath/soal/$value");
            } else if (strtolower(end($n)) == 'php') {
                unlink("$this->path/$key");
                continue;
            } else if (in_array($ext, ['png', 'jpg', 'jpeg', 'gif', 'webp'])) {
                rename("$this->path/$key", FCPATH . "$app->imgPath/soal/$value");
            } else if (in_array($ext, ['mp4', '3gp', 'mkv'])) {
                rename("$this->path/$key", FCPATH . "$app->videoPath/soal/$value");
            }
            $this->filesSearch[] = $key;
            $this->filesReplace[] = $value;
        }
        unlink($filePath);
        $inputFileType = \PhpOffice\PhpSpreadsheet\IOFactory::identify("$this->path/$soal");
        $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType);
        $spreadsheet = $reader->load("$this->path/$soal");
        $db = db_connect();
        $db->transStart();
        $db->table('soal_data')->where('bank_id', $this->id)->delete();
        $acceptedKeys = [1, 2, 3, 4, 5];
        foreach ($spreadsheet->getActiveSheet()->toArray() as $key => $value) {
            if ($key == 0) continue;
            $value[0] = $this->changeString($value[0]);
            if (empty($value[0]) && $value[7] != 4) continue;
            $si = $db->table('soal_data')->insert(['bank_id' => $this->id, 'jenis_soal' => $value[7], 'true_default_point' => $value[8] ?? 0, 'false_default_point' => $value[9] ?? 0, 'question' => $value[0], 'shortentry' => $value[7] == 5 ? $value[6] : null, 'created_at' => time(), 'updated_at' => time()]);
            $si = $db->insertID();
            switch ($value[7]) {
                case 1:
                    $option = [];
                    if ($value[1]) $option[] = ['soal_id' => $si, 'body' => $this->changeString($value[1]), 'is_key' => $value[6] == 1 ? 1 : 0];
                    if ($value[2]) $option[] = ['soal_id' => $si, 'body' => $this->changeString($value[2]), 'is_key' => $value[6] == 2 ? 1 : 0];
                    if ($value[3]) $option[] = ['soal_id' => $si, 'body' => $this->changeString($value[3]), 'is_key' => $value[6] == 3 ? 1 : 0];
                    if ($value[4]) $option[] = ['soal_id' => $si, 'body' => $this->changeString($value[4]), 'is_key' => $value[6] == 4 ? 1 : 0];
                    if ($value[5]) $option[] = ['soal_id' => $si, 'body' => $this->changeString($value[5]), 'is_key' => $value[6] == 5 ? 1 : 0];
                    if (count($option) > 0) $db->table('soal_opsi')->insertBatch($option);
                    break;
                case 2:
                    break;
                case 5:
                    break;
                case 3:
                    $kunci = explode(',', $value[6]);
                    $option = [];
                    if ($value[1]) $option[] = ['soal_id' => $si, 'body' => $this->changeString($value[1]), 'is_key' => in_array(1, $kunci) ? 1 : 0];
                    if ($value[2]) $option[] = ['soal_id' => $si, 'body' => $this->changeString($value[2]), 'is_key' => in_array(2, $kunci) ? 1 : 0];
                    if ($value[3]) $option[] = ['soal_id' => $si, 'body' => $this->changeString($value[3]), 'is_key' => in_array(3, $kunci) ? 1 : 0];
                    if ($value[4]) $option[] = ['soal_id' => $si, 'body' => $this->changeString($value[4]), 'is_key' => in_array(4, $kunci) ? 1 : 0];
                    if ($value[5]) $option[] = ['soal_id' => $si, 'body' => $this->changeString($value[5]), 'is_key' => in_array(5, $kunci) ? 1 : 0];
                    if (count($option) > 0) $db->table('soal_opsi')->insertBatch($option);
                    break;
                case 4:
                    $keys = explode(',', $value[6]);
                    $couples = [1 => null, 2 => null, 3 => null, 4 => null, 5 => null];
                    if ($value[10]) {
                        $db->table('soal_data_couple')->insert(['soal_id' => $si, 'body' => $this->changeString($value[10])]);
                        foreach ($keys as $sk => $sv) {
                            $sv = explode('-', $sv);
                            if (count($sv) != 2) continue;
                            if (!in_array($sv[0], $acceptedKeys) || !in_array($sv[1], $acceptedKeys)) continue;
                            if ($sv[0] == 1) {
                                $couples[$sv[1]] = $db->insertID();
                                break;
                            }
                        }
                    }
                    if ($value[11]) {
                        $db->table('soal_data_couple')->insert(['soal_id' => $si, 'body' => $this->changeString($value[11])]);
                        foreach ($keys as $sk => $sv) {
                            $sv = explode('-', $sv);
                            if (count($sv) != 2) continue;
                            if (!in_array($sv[0], $acceptedKeys) || !in_array($sv[1], $acceptedKeys)) continue;
                            if ($sv[0] == 2) {
                                $couples[$sv[1]] = $db->insertID();
                                break;
                            }
                        }
                    }
                    if ($value[12]) {
                        $db->table('soal_data_couple')->insert(['soal_id' => $si, 'body' => $this->changeString($value[12])]);
                        foreach ($keys as $sk => $sv) {
                            $sv = explode('-', $sv);
                            if (count($sv) != 2) continue;
                            if (!in_array($sv[0], $acceptedKeys) || !in_array($sv[1], $acceptedKeys)) continue;
                            if ($sv[0] == 3) {
                                $couples[$sv[1]] = $db->insertID();
                                break;
                            }
                        }
                    }
                    if ($value[13]) {
                        $db->table('soal_data_couple')->insert(['soal_id' => $si, 'body' => $this->changeString($value[13])]);
                        foreach ($keys as $sk => $sv) {
                            $sv = explode('-', $sv);
                            if (count($sv) != 2) continue;
                            if (!in_array($sv[0], $acceptedKeys) || !in_array($sv[1], $acceptedKeys)) continue;
                            if ($sv[0] == 4) {
                                $couples[$sv[1]] = $db->insertID();
                                break;
                            }
                        }
                    }
                    if ($value[14]) {
                        $db->table('soal_data_couple')->insert(['soal_id' => $si, 'body' => $this->changeString($value[14])]);
                        foreach ($keys as $sk => $sv) {
                            $sv = explode('-', $sv);
                            if (count($sv) != 2) continue;
                            if (!in_array($sv[0], $acceptedKeys) || !in_array($sv[1], $acceptedKeys)) continue;
                            if ($sv[0] == 5) {
                                $couples[$sv[1]] = $db->insertID();
                                break;
                            }
                        }
                    }
                    $option = [];
                    if ($value[1]) $option[] = ['soal_id' => $si, 'soal_couple_id' => $couples[1], 'body' => $this->changeString($value[1])];
                    if ($value[2]) $option[] = ['soal_id' => $si, 'soal_couple_id' => $couples[2], 'body' => $this->changeString($value[2])];
                    if ($value[3]) $option[] = ['soal_id' => $si, 'soal_couple_id' => $couples[3], 'body' => $this->changeString($value[3])];
                    if ($value[4]) $option[] = ['soal_id' => $si, 'soal_couple_id' => $couples[4], 'body' => $this->changeString($value[4])];
                    if ($value[5]) $option[] = ['soal_id' => $si, 'soal_couple_id' => $couples[5], 'body' => $this->changeString($value[5])];
                    if (count($option) > 0) $db->table('soal_opsi')->insertBatch($option);
                    break;

                case 6:
                    $keys = explode(',', $value[6]);
                    $option = [];
                    foreach ($keys as $sk => $skv) {
                        $option[] = ['soal_id' => $si, 'body' => $this->changeString($value[$sk + 1]), 'is_key' => $skv == ($sk + 1) . "-1" ? 1 : 0];
                    }
                    $db->table('soal_opsi')->insertBatch($option);
                    break;


                default:
                    return $this->fail(['message' => 'Jenis Tidak Valid!']);
                    break;
            }
        }
        $db->transComplete();
        unlink("$this->path/$soal");
        if ($db->transStatus() === FALSE) {
            $db->transRollback();
            return $this->fail(['message' => "Gagal Memproses Data"]);
        }
        return $this->respond(['message' => "Berhasil Memproses Data"]);
    }

    private function excel()
    {
        $inputFileType = \PhpOffice\PhpSpreadsheet\IOFactory::identify("$this->path/$this->filename");
        $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType);
        $spreadsheet = $reader->load("$this->path/$this->filename");
        $db = db_connect();
        $db->transStart();
        $db->table('soal_data')->where('bank_id', $this->id)->delete();
        $acceptedKeys = [1, 2, 3, 4, 5];
        foreach ($spreadsheet->getActiveSheet()->toArray() as $key => $value) {
            if ($key == 0) continue;
            if (empty($value[0]) && $value[7] != 4) continue;
            $si = $db->table('soal_data')->insert(['bank_id' => $this->id, 'jenis_soal' => $value[7], 'true_default_point' => $value[8] ?? 0, 'false_default_point' => $value[9] ?? 0, 'question' => $value[0], 'shortentry' => $value[7] == 5 ? $value[6] : null, 'created_at' => time(), 'updated_at' => time()]);
            $si = $db->insertID();
            switch ($value[7]) {
                case 1:
                    $option = [];
                    if ($value[1]) $option[] = ['soal_id' => $si, 'body' => $value[1], 'is_key' => $value[6] == 1 ? 1 : 0];
                    if ($value[2]) $option[] = ['soal_id' => $si, 'body' => $value[2], 'is_key' => $value[6] == 2 ? 1 : 0];
                    if ($value[3]) $option[] = ['soal_id' => $si, 'body' => $value[3], 'is_key' => $value[6] == 3 ? 1 : 0];
                    if ($value[4]) $option[] = ['soal_id' => $si, 'body' => $value[4], 'is_key' => $value[6] == 4 ? 1 : 0];
                    if ($value[5]) $option[] = ['soal_id' => $si, 'body' => $value[5], 'is_key' => $value[6] == 5 ? 1 : 0];
                    $db->table('soal_opsi')->insertBatch($option);
                    break;
                case 2:
                    break;
                case 5:
                    break;
                case 3:
                    $kunci = explode(',', $value[6]);
                    $option = [];
                    if ($value[1]) $option[] = ['soal_id' => $si, 'body' => $value[1], 'is_key' => in_array(1, $kunci) ? 1 : 0];
                    if ($value[2]) $option[] = ['soal_id' => $si, 'body' => $value[2], 'is_key' => in_array(2, $kunci) ? 1 : 0];
                    if ($value[3]) $option[] = ['soal_id' => $si, 'body' => $value[3], 'is_key' => in_array(3, $kunci) ? 1 : 0];
                    if ($value[4]) $option[] = ['soal_id' => $si, 'body' => $value[4], 'is_key' => in_array(4, $kunci) ? 1 : 0];
                    if ($value[5]) $option[] = ['soal_id' => $si, 'body' => $value[5], 'is_key' => in_array(5, $kunci) ? 1 : 0];
                    $db->table('soal_opsi')->insertBatch($option);
                    break;
                case 4:
                    $keys = explode(',', $value[6]);
                    $couples = [1 => null, 2 => null, 3 => null, 4 => null, 5 => null];
                    if ($value[10]) {
                        $db->table('soal_data_couple')->insert(['soal_id' => $si, 'body' => $this->changeString($value[10])]);
                        foreach ($keys as $sk => $sv) {
                            $sv = explode('-', $sv);
                            if (count($sv) != 2) continue;
                            if (!in_array($sv[0], $acceptedKeys) || !in_array($sv[1], $acceptedKeys)) continue;
                            if ($sv[0] == 1) {
                                $couples[$sv[1]] = $db->insertID();
                                break;
                            }
                        }
                    }
                    if ($value[11]) {
                        $db->table('soal_data_couple')->insert(['soal_id' => $si, 'body' => $this->changeString($value[11])]);
                        foreach ($keys as $sk => $sv) {
                            $sv = explode('-', $sv);
                            if (count($sv) != 2) continue;
                            if (!in_array($sv[0], $acceptedKeys) || !in_array($sv[1], $acceptedKeys)) continue;
                            if ($sv[0] == 2) {
                                $couples[$sv[1]] = $db->insertID();
                                break;
                            }
                        }
                    }
                    if ($value[12]) {
                        $db->table('soal_data_couple')->insert(['soal_id' => $si, 'body' => $this->changeString($value[12])]);
                        foreach ($keys as $sk => $sv) {
                            $sv = explode('-', $sv);
                            if (count($sv) != 2) continue;
                            if (!in_array($sv[0], $acceptedKeys) || !in_array($sv[1], $acceptedKeys)) continue;
                            if ($sv[0] == 3) {
                                $couples[$sv[1]] = $db->insertID();
                                break;
                            }
                        }
                    }
                    if ($value[13]) {
                        $db->table('soal_data_couple')->insert(['soal_id' => $si, 'body' => $this->changeString($value[13])]);
                        foreach ($keys as $sk => $sv) {
                            $sv = explode('-', $sv);
                            if (count($sv) != 2) continue;
                            if (!in_array($sv[0], $acceptedKeys) || !in_array($sv[1], $acceptedKeys)) continue;
                            if ($sv[0] == 4) {
                                $couples[$sv[1]] = $db->insertID();
                                break;
                            }
                        }
                    }
                    if ($value[14]) {
                        $db->table('soal_data_couple')->insert(['soal_id' => $si, 'body' => $this->changeString($value[14])]);
                        foreach ($keys as $sk => $sv) {
                            $sv = explode('-', $sv);
                            if (count($sv) != 2) continue;
                            if (!in_array($sv[0], $acceptedKeys) || !in_array($sv[1], $acceptedKeys)) continue;
                            if ($sv[0] == 5) {
                                $couples[$sv[1]] = $db->insertID();
                                break;
                            }
                        }
                    }
                    $option = [];
                    if ($value[1]) $option[] = ['soal_id' => $si, 'soal_couple_id' => $couples[1], 'body' => $this->changeString($value[1])];
                    if ($value[2]) $option[] = ['soal_id' => $si, 'soal_couple_id' => $couples[2], 'body' => $this->changeString($value[2])];
                    if ($value[3]) $option[] = ['soal_id' => $si, 'soal_couple_id' => $couples[3], 'body' => $this->changeString($value[3])];
                    if ($value[4]) $option[] = ['soal_id' => $si, 'soal_couple_id' => $couples[4], 'body' => $this->changeString($value[4])];
                    if ($value[5]) $option[] = ['soal_id' => $si, 'soal_couple_id' => $couples[5], 'body' => $this->changeString($value[5])];
                    $db->table('soal_opsi')->insertBatch($option);
                    break;
                case 6:
                    $keys = explode(',', $value[6]);
                    $option = [];
                    foreach ($keys as $sk => $skv) {
                        $option[] = ['soal_id' => $si, 'body' => $value[$sk + 1], 'is_key' => $skv == ($sk + 1) . "-1" ? 1 : 0];
                    }
                    $db->table('soal_opsi')->insertBatch($option);
                    break;

                default:
                    return $this->fail(['message' => 'Jenis Tidak Valid!']);
                    break;
            }
        }
        $db->transComplete();
        unlink("$this->path/$this->filename");
        if ($db->transStatus() === FALSE) {
            $db->transRollback();
            return $this->fail(['message' => "Gagal Memproses Data"]);
        }
        return $this->respond(['message' => "Berhasil Memproses Data"]);
    }


    private function changeString($str)
    {
        try {
            $str = $str ?? '';
            $app = config('app');
            $pattern = '/(?<path>..\/..\/..\/)(?<name>\w+).(?<extension>png|webp|jpg|jpeg)/';
            while (preg_match($pattern, $str, $mat)) {
                $fileName = "$mat[name].$mat[extension]";
                if (isset($this->files[$fileName])) $fileName = $this->files[$fileName];
                $path = "";
                if (in_array($mat['extension'], ['png', 'jpg', 'jpeg', 'gif', 'webp'])) $path = "$app->imgPath";
                else if (in_array($mat['extension'], ['mkv', 'mp4', '3gp'])) $path = "$app->videoPath";
                else if (in_array($mat['extension'], ['mp3'])) $path = "$app->audioPath";
                $str = str_replace($mat[0], "{BASEURL}/$path/soal/$fileName", $str);
            }
            $pattern = "/\{{(.*?)\}}/i";
            if (preg_match_all($pattern, $str, $matches)) {
                foreach ($matches[1] as $k => $v) {
                    if (isset($this->files[$v])) {
                        $n = explode('.', $v);
                        $f = $this->files[$v];
                        $ext = strtolower(end($n) ?? '');
                        if ($ext == 'mp3') {
                            $str = str_replace($matches[0][$k], htmlMP3("../../../$app->audioPath/soal/$f"), $str);
                        } else if (in_array($ext, ['png', 'jpg', 'jpeg', 'gif', 'webp'])) {
                            $str = str_replace($matches[0][$k], htmlIMAGE("../../../$app->imgPath/soal/$f"), $str);
                        } else if (in_array($ext, ['mp4', '3gp', 'mkv'])) {
                            $file_info = new finfo(FILEINFO_MIME_TYPE);
                            $mime_type = $file_info->buffer(file_get_contents(FCPATH . "$app->videoPath/soal/$f"));
                            $str = str_replace($matches[0][$k], htmlVideo("../../../$app->videoPath/soal/$f", $mime_type), $str);
                        }
                    }
                }
            }
            $str = str_replace($this->filesSearch, $this->filesReplace, $str);
            $str = str_replace(["{BASEURL}"], ["../../.."], $str);
        } catch (\Throwable $th) {
            //throw $th;
        }
        return $str;
    }
    public function example($type)
    {
        switch ($type) {
            case 'zip':
                return $this->response->download($this->fileLocation['soal.zip'], null);
                break;
            case 'excel':
                return $this->response->download($this->fileLocation['soal.xlsx'], null);
                break;
            case 'docx':
                return $this->response->download($this->fileLocation['soal.docx'], null);
                break;

            default:
                # code...
                break;
        }
    }
}
