<?php

namespace App\Http\Controllers\Api\Common;

use App\Classes\Common;
use App\Classes\LangTrans;
use App\Classes\PermsSeed;
use App\Http\Controllers\ApiBaseController;
use Examyou\RestAPI\ApiResponse;
use Examyou\RestAPI\Exceptions\ApiException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
use GuzzleHttp\Client;
use Illuminate\Support\Arr;
use ZipArchive;

class UpdateAppController extends ApiBaseController
{
    public function index()
    {
        $laravel = app();
        $updateVersionInfo['laravelVersion'] = $laravel::VERSION;
        $appVersion = $this->getAppVersion();

        $appDetails = [
            [
                'name' => 'app_details',
                'value' => '',
            ],
            [
                'name' => 'app_version',
                'value' => $appVersion,
            ],
            [
                'name' => 'php_version',
                'value' => phpversion(),
            ],
            [
                'name' => 'laravel_version',
                'value' => $laravel::VERSION,
            ],
            [
                'name' => 'vue_version',
                'value' => '',
            ],
            [
                'name' => 'mysql_version',
                'value' => mysqli_get_client_info(),
            ],
        ];

        return ApiResponse::make('Data fetched', [
            'app_details' => $appDetails,
            'app_version' => $appVersion
        ]);
    }

    public function updateApp(Request $request)
    {
        $response = Http::post('https://envato.codeifly.com/install', [
            'verified_name' => $request->verified_name,
            'domain' => $request->domain,
        ]);

        $responseData = $response->object();

        if ((isset($responseData->message))) {
            throw new ApiException($responseData->message);
        }

        $tempPath = storage_path() . '/app';
        $fileName = $responseData->file_name;
        $tempFileName = $tempPath . '/' . $fileName;

        $fileHandler = fopen($tempFileName, 'w');

        $fileUrl = $responseData->url;

        $client = new Client();
        $client->request('GET', $fileUrl,  [
            'sink' => $fileHandler,
            'progress' => function ($downloadTotalSize, $downloadTotalSoFar, $uploadTotalSize, $uploadSizeSoFar) {
                $percentageDownloaded = ($downloadTotalSize > 0) ? (($downloadTotalSoFar / $downloadTotalSize) * 100) : 0;
                File::put(public_path() . '/download-percentage.txt', $percentageDownloaded);
            },
            'verify' => false
        ]);

        $modulesData = Common::moduleInformations();

        return ApiResponse::make('Success', [
            'modules' => $modulesData,
            'file_name' => $fileName,
        ]);
    }

    public function extractZip(Request $request)
    {
        $moduleName = $request->verified_name;
        $fileName = $request->file_name;

        // Check if it's a manual upload (stored in updates folder)
        if ($moduleName === 'manual_upload') {
            $tempFileName = storage_path('app/updates/' . $fileName);
        } else {
            $tempPath = storage_path() . '/app';
            $tempFileName = $tempPath . '/' . $fileName;
        }

        if (!File::exists($tempFileName)) {
            throw new ApiException('Update file not found');
        }

        $extractPath = base_path();

        // Use PHP's built-in ZipArchive
        $zip = new ZipArchive;
        $result = $zip->open($tempFileName);

        if ($result !== true) {
            throw new ApiException('Failed to open ZIP file. Error code: ' . $result);
        }

        if (!$zip->extractTo($extractPath)) {
            $zip->close();
            throw new ApiException('Failed to extract ZIP file');
        }

        $zip->close();

        PermsSeed::seedMainPermissions();
        LangTrans::seedMainTranslations();
        sleep(3);
        Artisan::call('migrate', ['--force' => true]);

        // Create storage symlink if it doesn't exist
        if (!File::exists(public_path('storage'))) {
            Artisan::call('storage:link');
        }

        $this->configClear();

        // Delete Downloaded File after successful extraction
        if ($moduleName === 'manual_upload') {
            File::delete($tempFileName);
        }

        $modulesData = Common::moduleInformations();

        return ApiResponse::make('Success', [
            'installed_modules' => $modulesData['installed_modules'],
            'enabled_modules' => Arr::pluck($modulesData['enabled_modules'], 'verified_name'),
            'verified_name' => $moduleName,
            'version'    => $this->getAppVersion()
        ]);
    }

    public function configClear()
    {
        Artisan::call('config:clear');
        Artisan::call('route:clear');
        Artisan::call('view:clear');
        Artisan::call('cache:clear');
    }


    public function getAppVersion()
    {
        $versionFileName = app_type() == 'saas' ? 'superadmin_version.txt' : 'version.txt';
        $appVersion = File::get($versionFileName);

        return preg_replace("/\r|\n/", "", $appVersion);
    }

    public function downloadPercent()
    {
        $percentage = File::get(public_path() . '/download-percentage.txt');

        return ApiResponse::make('Success', [
            'percentage' => $percentage
        ]);
    }

    public function manualUpload(Request $request)
    {
        $request->validate([
            'file' => 'required|file|mimes:zip,application/zip,application/x-zip-compressed|max:204800', // Max 100MB
        ]);

        try {
            if (!$request->hasFile('file')) {
                throw new ApiException('No file uploaded');
            }

            $file = $request->file('file');

            if (!$file->isValid()) {
                throw new ApiException('File upload failed');
            }

            $fileName = 'manual_update_' . time() . '_' . $file->getClientOriginalName();

            // Ensure the updates directory exists
            $uploadsDir = storage_path('app/updates');
            if (!File::exists($uploadsDir)) {
                File::makeDirectory($uploadsDir, 0755, true);
            }

            // Delete all existing files in the updates folder before uploading new one
            $existingFiles = File::files($uploadsDir);
            foreach ($existingFiles as $existingFile) {
                File::delete($existingFile);
            }

            // Move the file directly to storage/app/updates folder
            $fullPath = $uploadsDir . '/' . $fileName;
            $file->move($uploadsDir, $fileName);

            // Verify the file was actually stored
            if (!File::exists($fullPath)) {
                throw new ApiException('File was not saved properly');
            }

            return ApiResponse::make('File uploaded successfully', [
                'file_name' => $fileName,
                'file_path' => 'updates/' . $fileName,
                'file_size' => File::size($fullPath),
                'full_path' => $fullPath,
                'uploaded_at' => now()->format('Y-m-d H:i:s')
            ]);
        } catch (\Exception $e) {
            throw new ApiException('Failed to upload file: ' . $e->getMessage());
        }
    }

    public function listUploadedFiles()
    {
        try {
            $uploadsPath = storage_path('app/updates');

            // Create directory if it doesn't exist
            if (!File::exists($uploadsPath)) {
                File::makeDirectory($uploadsPath, 0755, true);
                return ApiResponse::make('Success', ['files' => []]);
            }

            $files = File::files($uploadsPath);
            $fileList = [];

            foreach ($files as $file) {
                $fileList[] = [
                    'name' => $file->getFilename(),
                    'size' => $file->getSize(),
                    'size_formatted' => $this->formatBytes($file->getSize()),
                    'uploaded_at' => date('Y-m-d H:i:s', $file->getMTime()),
                    'path' => 'updates/' . $file->getFilename()
                ];
            }

            // Sort by upload time, newest first
            usort($fileList, function ($a, $b) {
                return strtotime($b['uploaded_at']) - strtotime($a['uploaded_at']);
            });

            return ApiResponse::make('Success', [
                'files' => $fileList
            ]);
        } catch (\Exception $e) {
            throw new ApiException('Failed to list files: ' . $e->getMessage());
        }
    }

    public function deleteUploadedFile($fileName)
    {
        try {
            $filePath = storage_path('app/updates/' . $fileName);

            if (!File::exists($filePath)) {
                throw new ApiException('File not found');
            }

            File::delete($filePath);

            return ApiResponse::make('File deleted successfully');
        } catch (\Exception $e) {
            throw new ApiException('Failed to delete file: ' . $e->getMessage());
        }
    }

    private function formatBytes($bytes, $precision = 2)
    {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];

        for ($i = 0; $bytes > 1024; $i++) {
            $bytes /= 1024;
        }

        return round($bytes, $precision) . ' ' . $units[$i];
    }
}
