<?php

namespace App\Controllers;

use CodeIgniter\Controller;
use ZipArchive;
use RecursiveIteratorIterator;
use RecursiveDirectoryIterator;

class Updater extends Controller
{
    protected $updateServer = 'https://appmi.mimpkkertonatan.sch.id/version.json';
    protected $localVersionFile;
    protected $writablePath;

    public function __construct()
    {
        $this->writablePath = rtrim(WRITEPATH, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
        $this->localVersionFile = $this->writablePath . 'version.json';
    }

    /**
     * 🔍 Cek apakah ada versi baru di server update
     */
    public function check()
    {
        if (!file_exists($this->localVersionFile)) {
            return $this->response->setJSON([
                'status' => 'error',
                'message' => 'File versi lokal tidak ditemukan',
            ]);
        }

        $localData = json_decode(file_get_contents($this->localVersionFile), true);
        $localVersion = $localData['version'] ?? '0.0.0';

        $remoteData = $this->fetchRemoteFile($this->updateServer);
        if ($remoteData === false) {
            return $this->response->setJSON([
                'status' => 'offline',
                'message' => 'Tidak dapat terhubung ke server update',
            ]);
        }

        $remote = json_decode($remoteData, true);
        $remoteVersion = $remote['version'] ?? '0.0.0';

        if (version_compare($remoteVersion, $localVersion, '>')) {
            return $this->response->setJSON([
                'status' => 'available',
                'message' => 'Versi baru tersedia',
                'current_version' => $localVersion,
                'new_version' => $remoteVersion,
                'url' => $remote['url'] ?? '',
                'changelog' => $remote['changelog'] ?? '',
            ]);
        }

        return $this->response->setJSON([
            'status' => 'up_to_date',
            'message' => 'Aplikasi sudah versi terbaru',
            'version' => $localVersion,
        ]);
    }

    /**
     * ⚙️ Terapkan update dari server (online)
     */
    public function apply()
    {
        $url = $this->request->getPost('url');
        if (!$url) {
            return $this->response->setJSON([
                'status' => 'error',
                'message' => 'URL update tidak ditemukan',
            ]);
        }

        $zipPath = $this->writablePath . 'update.zip';

        $zipData = $this->fetchRemoteFile($url);
        if ($zipData === false || strlen($zipData) < 1000) {
            log_message('error', 'Gagal mengunduh file update dari: ' . $url);
            return $this->response->setJSON([
                'status' => 'error',
                'message' => 'Gagal mengunduh file update. Pastikan URL benar dan file dapat diakses.'
            ]);
        }

        file_put_contents($zipPath, $zipData);

        $this->createBackup();

        $result = $this->extractZip($zipPath);
        if (!$result['success']) {
            return $this->response->setJSON($result);
        }

        @unlink($zipPath);

        $remoteData = $this->fetchRemoteFile($this->updateServer);
        if ($remoteData) {
            file_put_contents($this->localVersionFile, $remoteData);
        }

        return $this->response->setJSON([
            'status' => 'success',
            'message' => 'Update berhasil diterapkan',
        ]);
    }

    /**
     * 💾 Terapkan update dari file ZIP lokal (offline)
     */
    public function applyOffline()
    {
        $file = $this->request->getFile('update_zip');
        if (!$file || !$file->isValid()) {
            return $this->response->setJSON([
                'status' => 'error',
                'message' => 'File ZIP tidak valid',
            ]);
        }

        $this->createBackup();

        $result = $this->extractZip($file->getTempName());
        if (!$result['success']) {
            return $this->response->setJSON($result);
        }

        return $this->response->setJSON([
            'status' => 'success',
            'message' => 'Update offline berhasil diterapkan',
        ]);
    }

    /**
     * 🧩 Membuat backup sebelum update
     */
    protected function createBackup()
    {
        $backupFile = $this->writablePath . 'backup_' . date('Ymd_His') . '.zip';
        $zipBackup = new ZipArchive();

        if ($zipBackup->open($backupFile, ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) {
            $foldersToBackup = [
                'app/Controllers/',
                'app/Models/',
                'app/Views/',
                'public/css/',
                'public/js/',
            ];

            foreach ($foldersToBackup as $folder) {
                $path = ROOTPATH . $folder;
                if (!is_dir($path)) continue;

                $iterator = new RecursiveIteratorIterator(
                    new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS)
                );

                foreach ($iterator as $file) {
                    if (!$file->isDir()) {
                        $filePath = $file->getRealPath();
                        $relativePath = str_replace(ROOTPATH, '', $filePath);
                        $zipBackup->addFile($filePath, $relativePath);
                    }
                }
            }
            $zipBackup->close();
        }
    }

    /**
     * 🌐 Ambil file jarak jauh (cURL + fallback)
     */
    protected function fetchRemoteFile($url)
    {
        $data = false;

        if (function_exists('curl_init')) {
            $ch = curl_init();
            curl_setopt_array($ch, [
                CURLOPT_URL => $url,
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_FOLLOWLOCATION => true,
                CURLOPT_CONNECTTIMEOUT => 10,
                CURLOPT_TIMEOUT => 20,
                CURLOPT_SSL_VERIFYPEER => false,
                CURLOPT_USERAGENT => 'CodeIgniter-Updater/1.0',
            ]);
            $data = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            $error = curl_error($ch);
            curl_close($ch);

            if ($httpCode < 200 || $httpCode >= 300 || $data === false) {
                log_message('error', "Gagal ambil data via cURL: $url | HTTP $httpCode | $error");
                $data = false;
            }
        }

        if ($data === false && ini_get('allow_url_fopen')) {
            $context = stream_context_create([
                'http' => [
                    'timeout' => 15,
                    'follow_location' => 1,
                    'user_agent' => 'CodeIgniter-Updater/1.0'
                ],
                'ssl' => [
                    'verify_peer' => false,
                    'verify_peer_name' => false
                ]
            ]);
            $data = @file_get_contents($url, false, $context);
            if ($data === false) {
                log_message('error', 'Gagal ambil data via file_get_contents: ' . $url);
            }
        }

        return $data;
    }

    /**
     * 📦 Ekstrak ZIP ke root project (tanpa path ganda)
     */
    protected function extractZip($zipPath)
    {
        helper('filesystem');

        log_message('debug', 'ROOTPATH = ' . ROOTPATH);
        log_message('debug', 'WRITEPATH = ' . WRITEPATH);

        $tmpPath = rtrim(WRITEPATH, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'tmp_update' . DIRECTORY_SEPARATOR;

        $this->deleteDir($tmpPath);
        if (!is_dir($tmpPath)) mkdir($tmpPath, 0777, true);

        $zip = new ZipArchive;
        if ($zip->open($zipPath) === TRUE) {
            $zip->extractTo($tmpPath);
            $zip->close();
        } else {
            return ['success' => false, 'message' => 'Gagal membuka file ZIP'];
        }

        $tmpPath = str_replace(['\\', '//'], '/', realpath($tmpPath)) . '/';
        $rootPath = str_replace(['\\', '//'], '/', realpath(ROOTPATH)) . '/';

        $entries = array_diff(scandir($tmpPath), ['.', '..']);
        if (count($entries) === 1 && is_dir($tmpPath . reset($entries))) {
            $tmpPath .= reset($entries) . '/';
        }

        $iterator = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($tmpPath, RecursiveDirectoryIterator::SKIP_DOTS),
            RecursiveIteratorIterator::SELF_FIRST
        );

        foreach ($iterator as $item) {
            $sourcePath = str_replace(['\\', '//'], '/', $item->getPathname());
            $relativePath = ltrim(str_replace($tmpPath, '', $sourcePath), '/');
            $destPath = $rootPath . $relativePath;

            if ($item->isDir()) {
                if (!is_dir($destPath)) mkdir($destPath, 0777, true);
            } else {
                $destDir = dirname($destPath);
                if (!is_dir($destDir)) mkdir($destDir, 0777, true);

                if (file_exists($destPath)) {
                    @chmod($destPath, 0777);
                    @unlink($destPath);
                }
                if (!@copy($sourcePath, $destPath)) {
                    log_message('error', "Gagal menyalin: $sourcePath → $destPath");
                }
            }
        }

        $this->deleteDir($tmpPath);

        return ['success' => true, 'message' => 'Update berhasil diterapkan'];
    }

    /**
     * 🧹 Hapus folder rekursif
     */
    protected function deleteDir($dir)
    {
        if (!is_dir($dir)) return;
        $it = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
        $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
        foreach ($files as $file) {
            $file->isDir() ? @rmdir($file->getRealPath()) : @unlink($file->getRealPath());
        }
        @rmdir($dir);
    }

    /**
     * 🧾 View halaman updater
     */
    public function index()
    {
        return view('updater_view');
    }

    public function testConnection()
    {
        return $this->response->setJSON([
            'status' => 'ok',
            'message' => 'Server update aktif dan bisa diakses.'
        ]);
    }
}
