<?php

namespace App\Jobs;

use App\Enums\BuildStatus;
use App\Models\BuildOrder;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\Process\Process;
use Throwable;
use ZipArchive;

class ProcessBuildLatest implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public $tries = 3;

    public $backoff = [120, 240, 480];

    public $maxExceptions = 3;

    public $timeout = 3600;

    public function middleware(): array
    {
        return [(new WithoutOverlapping($this->buildOrderId))->releaseAfter(300)];
    }

    private const FILE_TYPE_APK = 'apk';

    private const FILE_TYPE_AAB = 'aab';

    private const FILE_TYPE_ANDROID_TXT = 'android-txt';

    private const FILE_TYPE_IOS_TXT = 'ios-txt';

    private const FILE_TYPE_RUNNER = 'runner';

    public function __construct(public int $buildOrderId) {}

    public function handle(): void
    {
        $buildDir = null;
        $buildOrder = null;

        try {
            Log::info('=============================================================================================================');
            Log::info('ProcessBuildLatest starting: Step 1 - Initialization', ['buildOrderId' => $this->buildOrderId]);

            set_time_limit(3600);
            ini_set('memory_limit', '2G'); // safer memory limit

            $appzaApiUrl = config('app.appza_api_url');
            if (empty($appzaApiUrl)) {
                throw new \RuntimeException('API URL not configured');
            }

            $buildOrder = BuildOrder::find($this->buildOrderId);
            if (! $buildOrder) {
                Log::error("Build Order #{$this->buildOrderId} not found. Job terminated.");

                return;
            }

            if ($buildOrder->status !== BuildStatus::Pending) {
                Log::info("Ignoring Build Order #{$buildOrder->id}, already in {$buildOrder->status->value} status.");

                return;
            }

            Log::info('Starting ProcessBuildLatest Job', [
                'buildOrderId' => $buildOrder->id,
                'target' => $buildOrder->build_target,
                'attempt' => $this->attempts(),
                'maxAttempts' => $this->tries,
            ]);

            $buildOrder->update(['process_start' => DB::raw('NOW()'), 'status' => BuildStatus::Processing]);
            $this->validateBuildRequirements($buildOrder);

            Log::info('ProcessBuildLatest step: Preparing build directory', ['buildOrderId' => $buildOrder->id]);
            $buildDir = $this->prepareBuildDir($buildOrder);

            if ($buildOrder->build_target === 'android') {
                Log::info('ProcessBuildLatest step: Starting Android build', ['buildOrderId' => $buildOrder->id]);
                $this->processAndroidBuild($buildDir, $buildOrder);

                Log::info('ProcessBuildLatest step: Uploading Android build artifacts', ['buildOrderId' => $buildOrder->id]);
                $apkUrl = $this->uploadFileIntoR2($buildDir, 'android-apk', $buildOrder, self::FILE_TYPE_APK);
                $aabUrl = $this->uploadFileIntoR2($buildDir, 'android-aab', $buildOrder, self::FILE_TYPE_AAB);
                $txtUrl = $this->uploadFileIntoR2($buildDir, 'android-build-output', $buildOrder, self::FILE_TYPE_ANDROID_TXT);

                $buildOrder->update([
                    'apk_url' => $apkUrl,
                    'aab_url' => $aabUrl,
                    'android_output_url' => $txtUrl,
                ]);
            } elseif ($buildOrder->build_target === 'ios') {
                Log::info('ProcessBuildLatest step: Starting iOS build', ['buildOrderId' => $buildOrder->id]);
                $this->processIosBuild($buildDir, $buildOrder);

                Log::info('ProcessBuildLatest step: Uploading iOS build artifacts', ['buildOrderId' => $buildOrder->id]);
                $txtUrl = $this->uploadFileIntoR2($buildDir, 'ios-build-output', $buildOrder, self::FILE_TYPE_IOS_TXT);

                $buildOrder->update([
                    'ios_output_url' => $txtUrl,
                ]);

            } else {
                throw new \InvalidArgumentException("Unsupported target: {$buildOrder->build_target}");
            }

            $parts = explode('/', $buildDir);
            $buildOrder->update([
                'status' => BuildStatus::Completed,
                'build_dir' => implode('/', array_slice($parts, -2)),
                'is_build_dir_delete' => false,
            ]);

            $this->sendBuildNotification($buildOrder);

        } catch (Throwable $e) {
            Log::error('ProcessBuildLatest failed', [
                'buildOrderId' => $this->buildOrderId,
                'message' => $e->getMessage(),
                'exception' => get_class($e),
                'line' => $e->getLine(),
            ]);

            if (isset($buildDir, $buildOrder)) {
                try {
                    $txtUrl = $buildOrder->build_target === 'android'
                        ? $this->uploadFileIntoR2($buildDir, 'android-build-output', $buildOrder, self::FILE_TYPE_ANDROID_TXT)
                        : $this->uploadFileIntoR2($buildDir, 'ios-build-output', $buildOrder, self::FILE_TYPE_IOS_TXT);

                    $buildOrder->update([
                        $buildOrder->build_target === 'android' ? 'android_output_url' : 'ios_output_url' => $txtUrl,
                    ]);
                } catch (Throwable $uploadException) {
                    Log::error('Failed to upload error logs', ['error' => $uploadException->getMessage()]);
                }

                $parts = explode('/', $buildDir);
                $buildOrder->update(['status' => BuildStatus::Failed, 'build_dir' => implode('/', array_slice($parts, -2))]);
            }

            throw $e;
        } finally {
            if (! empty($buildDir) && isset($buildOrder)) {
                try {
                    $zipPath = $this->createBuildZip($buildDir, $buildOrder);
                    $zipUrl = $this->uploadZipToR2($zipPath, $buildOrder);
                    $buildOrder->update(['build_zip_url' => $zipUrl]);
                    $this->cleanupBuildDirectory($buildDir);
                } catch (Throwable $zipError) {
                    Log::error('ZIP upload/cleanup failed', ['error' => $zipError->getMessage()]);
                }
            }
        }
    }

    private function sendBuildNotification(BuildOrder $buildOrder): void
    {
        $appzaApiUrl = config('app.appza_api_url');
        if (config('app.is_mail_send') && ! empty($appzaApiUrl)) {
            try {
                Http::post($appzaApiUrl."build/response/{$buildOrder->id}", [
                    'build_message' => "Build {$buildOrder->status->value} for Order #{$buildOrder->id}",
                ]);
                Log::info("Mail notification sent for build #{$buildOrder->id}");
            } catch (Throwable $e) {
                Log::error('Build notification failed', ['error' => $e->getMessage()]);
            }
        }
    }

    private function validateBuildRequirements(BuildOrder $buildOrder): void
    {
        $required = [
            'package_name', 'app_name', 'domain', 'base_suffix', 'base_url', 'build_number',
            'icon_url', 'build_plugin_slug', 'app_license_check_url',
        ];

        if ($buildOrder->build_target === 'android') {
            $required = array_merge($required, ['jks_url', 'key_properties_url']);
        }
        if ($buildOrder->build_target === 'ios') {
            $required = array_merge($required, ['issuer_id', 'key_id', 'api_key_url', 'team_id', 'app_identifier']);
        }

        $missing = array_filter($required, fn ($f) => empty($buildOrder->$f));
        if (! empty($missing)) {
            throw new \InvalidArgumentException('Missing required fields: '.implode(', ', $missing));
        }
    }

    /**
     * @throws \Exception
     */
    /*private function processAndroidBuild(string $buildDir, BuildOrder $buildOrder): void
    {
        Log::debug("Processing Android build", ['buildDir' => $buildDir, 'buildOrderId' => $buildOrder->id]);
        $diskPath = $this->getRelativeDiskPath($buildDir);

        // Download files safely using Laravel HTTP client
        $this->downloadFile($buildOrder->jks_url, "$diskPath/android/app/upload-keystore.jks");
        $this->downloadFile($buildOrder->key_properties_url, "$diskPath/android/key.properties");
        $this->downloadFile($buildOrder->icon_url, "$diskPath/assets/icons/launcher_icon.png");

        if ($buildOrder->splash_screen) $this->downloadFile($buildOrder->splash_screen, "$diskPath/assets/icons/splash_screen.png");
        if ($buildOrder->is_push_notification) $this->downloadFile($buildOrder->android_push_notification_url, "$diskPath/android/app/google-services.json");

        $this->runBuildCommand($buildDir, $buildOrder);
    }*/

    private function processAndroidBuild(string $buildDir, BuildOrder $buildOrder): void
    {
        Log::debug('Processing Android build', ['buildDir' => $buildDir, 'buildOrderId' => $buildOrder->id]);

        $diskPath = $this->getRelativeDiskPath($buildDir);

        // Validate URLs before downloading content
        if (! $this->isValidUrl($buildOrder->jks_url)) {
            throw new \Exception("Invalid JKS URL: {$buildOrder->jks_url}");
        }

        if (! $this->isValidUrl($buildOrder->key_properties_url)) {
            throw new \Exception("Invalid key properties URL: {$buildOrder->key_properties_url}");
        }

        if (! $this->isValidUrl($buildOrder->icon_url)) {
            throw new \Exception("Invalid icon URL: {$buildOrder->icon_url}");
        }

        // Download and save keystore file
        $jksContent = $this->getFileContents($buildOrder->jks_url);
        if (empty($jksContent)) {
            throw new \Exception("Failed to download keystore file from {$buildOrder->jks_url}");
        }

        Storage::disk('builds')->put("$diskPath/android/app/upload-keystore.jks", $jksContent)
        || throw new \Exception('Failed to save keystore file to disk');

        // Download and save key properties
        $keyPropertiesContent = $this->getFileContents($buildOrder->key_properties_url);
        if (empty($keyPropertiesContent)) {
            throw new \Exception("Failed to download key properties from {$buildOrder->key_properties_url}");
        }

        Storage::disk('builds')->put("$diskPath/android/key.properties", $keyPropertiesContent)
        || throw new \Exception('Failed to save key.properties file to disk');

        // Download and save icon file
        $iconContent = $this->getFileContents($buildOrder->icon_url);
        if (empty($iconContent)) {
            throw new \Exception("Failed to download icon from {$buildOrder->icon_url}");
        }

        Storage::disk('builds')->put("$diskPath/assets/icons/launcher_icon.png", $iconContent)
        || throw new \Exception('Failed to save icon file to disk');

        // Download and save splash screen file
        if (! empty($buildOrder->splash_screen) && $this->isValidUrl($buildOrder->splash_screen)) {
            $splashScreenContent = $this->getFileContents($buildOrder->splash_screen);
            if (empty($splashScreenContent)) {
                throw new \Exception("Failed to download splash screen from {$buildOrder->splash_screen}");
            }

            Storage::disk('builds')->put("$diskPath/assets/icons/splash_screen.png", $splashScreenContent)
            || throw new \Exception('Failed to save splash screen file to disk');
        }

        if ($buildOrder->is_push_notification) {
            // Download and save android push notification json
            $androidPushNotificationContent = $this->getFileContents($buildOrder->android_push_notification_url);
            if (empty($androidPushNotificationContent)) {
                throw new \Exception("Failed to download android push notification json from {$buildOrder->android_push_notification_url}");
            }

            Storage::disk('builds')->put("$diskPath/android/app/google-services.json", $androidPushNotificationContent)
            || throw new \Exception('Failed to save google-services.json file to disk');
        }

        $this->runBuildCommand($buildDir, $buildOrder);
    }

    /**
     * Validate if a URL is properly formatted
     */
    private function isValidUrl(string $url): bool
    {
        return filter_var($url, FILTER_VALIDATE_URL) !== false;
    }

    /**
     * Safely get file contents from a URL with validation
     */
    private function getFileContents(string $url): ?string
    {
        try {
            $context = stream_context_create([
                'http' => [
                    'timeout' => 30,
                    'user_agent' => 'AppzaBuilder/1.0',
                ],
            ]);

            $content = @file_get_contents($url, false, $context);

            if ($content === false) {
                Log::warning('Failed to download file from URL', ['url' => $url]);

                return null;
            }

            return $content;
        } catch (\Exception $e) {
            Log::error('Exception while downloading file', [
                'url' => $url,
                'error' => $e->getMessage(),
            ]);

            return null;
        }
    }

    /**
     * @throws \Exception
     */
    private function processIosBuild(string $buildDir, BuildOrder $buildOrder): void
    {
        Log::debug('Processing iOS build', ['buildDir' => $buildDir, 'buildOrderId' => $buildOrder->id]);
        $diskPath = $this->getRelativeDiskPath($buildDir);
        $keyFile = "AuthKey_{$buildOrder->key_id}.p8";

        $this->downloadFile($buildOrder->api_key_url, "$diskPath/ios/{$keyFile}");
        $this->downloadFile($buildOrder->icon_url, "$diskPath/assets/icons/launcher_icon.png");

        if ($buildOrder->splash_screen) {
            $this->downloadFile($buildOrder->splash_screen, "$diskPath/assets/icons/splash_screen.png");
        }
        if ($buildOrder->is_push_notification) {
            $this->downloadFile($buildOrder->ios_push_notification_url, "$diskPath/ios/Runner/GoogleService-Info.plist");
        }

        $this->runBuildCommand($buildDir, $buildOrder, ['key-filepath' => $keyFile]);
    }

    private function downloadFile(string $url, string $savePath): void
    {
        if (! filter_var($url, FILTER_VALIDATE_URL)) {
            throw new \Exception("Invalid URL: $url");
        }

        $response = Http::timeout(30)->retry(3, 1000)->get($url);
        if ($response->failed()) {
            throw new \Exception("Failed to download file: $url");
        }

        Storage::disk('builds')->put($savePath, $response->body());
    }

    /**
     * @throws \Exception
     */
    private function prepareBuildDir(BuildOrder $buildOrder): string
    {
        Log::debug('Preparing build directory', ['buildOrderId' => $buildOrder->id]);
        $buildDir = $this->getTargetDir($buildOrder);

        $templates = [
            'wordpress' => config('app.build_template_for_woo'),
            'woocommerce' => config('app.build_template_for_woo'),
            'tutor-lms' => config('app.build_template_for_tutor_lms'),
            'fluent-community' => config('app.build_template_for_fluent_community'),
        ];

        if (! isset($templates[$buildOrder->build_plugin_slug])) {
            throw new \Exception("Unknown plugin: {$buildOrder->build_plugin_slug}");
        }

        $templateDir = $templates[$buildOrder->build_plugin_slug];
        if (! File::isReadable($templateDir)) {
            throw new \Exception("Template dir not readable: $templateDir");
        }

        if (File::isDirectory($buildDir)) {
            File::deleteDirectory($buildDir);
        }
        File::copyDirectory($templateDir, $buildDir);
        Log::debug('Copying template directory', ['from' => $templateDir, 'to' => $buildDir]);
        chmod($buildDir.'/build_flow.sh', 0755);

        return $buildDir;
    }

    /**
     * @throws \Exception
     */
    private function runBuildCommand(string $buildDir, BuildOrder $buildOrder, array $extra = []): void
    {
        $command = $this->makeBuildCommand($buildOrder, $extra);
        Log::info('Running build command', ['command' => $command, 'buildOrderId' => $buildOrder->id]);

        $process = Process::fromShellCommandline($command, $buildDir);
        $process->setTimeout($this->timeout);
        $process->run();

        $diskPath = $this->getRelativeDiskPath($buildDir);
        Storage::disk('builds')->put($diskPath.'/build_output.txt', $process->getOutput().$process->getErrorOutput());

        if (! $process->isSuccessful()) {
            Log::error('Build command failed', [
                'returnCode' => $process->getExitCode(),
                'buildOrderId' => $buildOrder->id,
                'outputLines' => count($process->getOutput()),
            ]);
            $txtUrl = $buildOrder->build_target === 'android'
                ? $this->uploadFileIntoR2($buildDir, 'android-build-output', $buildOrder, self::FILE_TYPE_ANDROID_TXT)
                : $this->uploadFileIntoR2($buildDir, 'ios-build-output', $buildOrder, self::FILE_TYPE_IOS_TXT);

            $buildOrder->update([
                $buildOrder->build_target === 'android' ? 'android_output_url' : 'ios_output_url' => $txtUrl,
            ]);

            throw new \RuntimeException("Build failed with return code: {$process->getExitCode()}");
        }

        Log::info('Build command completed successfully', ['buildOrderId' => $buildOrder->id]);
    }

    private function getTargetDir(BuildOrder $buildOrder): string
    {
        $dirName = "{$buildOrder->id}_{$buildOrder->build_target}_".date('Ymd_His');

        return Storage::disk('builds')->path("$dirName");
    }

    private function getRelativeDiskPath(string $path): string
    {
        return str_replace(Storage::disk('builds')->path(''), '', $path);
    }

    private function makeBuildCommand(BuildOrder $buildOrder, array $extra = []): string
    {
        $iosExtra = '';
        $pushNotify = $buildOrder->is_push_notification ? ' --has-push-notification' : '';
        $googleAuth = '';

        if ($buildOrder->is_build_google_auth) {
            $googleAuth = sprintf(
                ' --has-google-login --google-web-client-id %s --google-ios-client-id %s',
                escapeshellarg($buildOrder->google_web_client_id),
                escapeshellarg($buildOrder->google_ios_client_id)
            );
        }

        if ($buildOrder->build_target === 'ios') {
            $iosExtra = sprintf(
                ' --key-id %s --issuer-id %s --key-filepath %s --app-identifier %s --team-id %s',
                escapeshellarg($buildOrder->key_id),
                escapeshellarg($buildOrder->issuer_id),
                escapeshellarg($extra['key-filepath'] ?? ''),
                escapeshellarg($buildOrder->app_identifier),
                escapeshellarg($buildOrder->team_id)
            );
        }

        return sprintf(
            'sh build_flow.sh --app-license-check-url %s --package-name %s --app-name %s --domain %s --base-suffix %s --base-url %s --build-number %d --should-build-%s%s%s%s',
            escapeshellarg($buildOrder->app_license_check_url),
            escapeshellarg($buildOrder->package_name),
            escapeshellarg($buildOrder->app_name),
            escapeshellarg(rtrim($buildOrder->domain, '/').'/'),
            escapeshellarg($buildOrder->base_suffix),
            escapeshellarg($buildOrder->base_url),
            (int) $buildOrder->build_number,
            escapeshellarg($buildOrder->build_target),
            $iosExtra,
            $pushNotify,
            $googleAuth
        );
    }

    /**
     * @throws \Exception
     */
    private function uploadFileIntoR2(string $directory, string $r2Folder, BuildOrder $buildOrder, string $filetype): ?string
    {
        Log::debug('Uploading file to R2', [
            'directory' => $directory,
            'r2Folder' => $r2Folder,
            'filetype' => $filetype,
            'buildOrderId' => $buildOrder->id,
        ]);

        // Determine file extension based on type
        $fileExtension = match ($filetype) {
            self::FILE_TYPE_APK => '.apk',
            self::FILE_TYPE_AAB => '.aab',
            self::FILE_TYPE_ANDROID_TXT, self::FILE_TYPE_IOS_TXT => '.txt',
            self::FILE_TYPE_RUNNER => '.log',
            default => throw new \InvalidArgumentException("Unsupported file type: $filetype"),
        };

        // For binary files (APK/AAB), ensure they exist
        if (in_array($filetype, [self::FILE_TYPE_APK, self::FILE_TYPE_AAB]) && ! $this->fileExists($directory, $fileExtension)) {
            throw new \Exception("No {$filetype} file found in directory: $directory");
        }

        // Find the file
        $filePath = $this->getFileByExtension($directory, $fileExtension);
        if (empty($filePath)) {
            Log::warning('File not found', [
                'directory' => $directory,
                'extension' => $fileExtension,
                'buildOrderId' => $buildOrder->id,
            ]);

            return null;
        }

        try {
            // Create new file name from package
            $appName = $this->getAppNameFromPackage($buildOrder->package_name);
            $extension = pathinfo($filePath, PATHINFO_EXTENSION);
            $timestamp = now()->format('Ymd_His');
            $newFileName = "{$appName}_build_{$buildOrder->build_number}_{$timestamp}.{$extension}";
            $r2Path = "{$r2Folder}/{$newFileName}";

            // Set appropriate content type
            $contentType = match ($filetype) {
                self::FILE_TYPE_APK, self::FILE_TYPE_AAB => 'application/vnd.android.package-archive',
                self::FILE_TYPE_ANDROID_TXT, self::FILE_TYPE_IOS_TXT => 'text/plain',
                self::FILE_TYPE_RUNNER => 'text/plain',
                default => 'application/octet-stream',
            };

            // Get file content with error handling
            $fileContent = @file_get_contents($filePath);
            if ($fileContent === false) {
                throw new \Exception("Failed to read file: {$filePath}");
            }

            // Upload to R2
            $success = Storage::disk('r2')->put($r2Path, $fileContent, [
                'visibility' => 'public',
                'Content-Type' => $contentType,
            ]);

            if (! $success) {
                throw new \Exception("Failed to upload file to R2: {$r2Path}");
            }

            Log::info('File uploaded successfully to R2', [
                'r2Path' => $r2Path,
                'buildOrderId' => $buildOrder->id,
            ]);

            return config('app.image_public_path').$r2Path;
        } catch (\Exception $e) {
            Log::error('Failed to upload file to R2', [
                'error' => $e->getMessage(),
                'buildOrderId' => $buildOrder->id,
            ]);

            return null;
        }
    }

    /**
     * Check if file with specific extension exists in directory
     */
    private function fileExists(string $directory, string $fileExtension): bool
    {
        return $this->getFileByExtension($directory, $fileExtension) !== null;
    }

    private function getFileByExtension(string $dir, string $ext): ?string
    {
        $files = glob("$dir/*$ext");

        return $files[0] ?? null;
    }

    private function getAppNameFromPackage(string $package): string
    {
        $parts = explode('.', $package);

        return end($parts) ?: 'app';
    }

    /* =====================================================
     | ZIP FIX (IOS SAFE)
     ===================================================== */

    private function createBuildZip(string $buildDir, BuildOrder $buildOrder): string
    {
        $zipPath = "{$buildDir}/build_{$buildOrder->build_target}_{$buildOrder->id}.zip";
        $password = config('app.build_zip_password');

        Log::info('ZIP creation started', [
            'buildDir' => $buildDir,
            'zipPath' => $zipPath,
            'target' => $buildOrder->build_target,
        ]);

        $zip = new ZipArchive;
        if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
            throw new \RuntimeException('Cannot create zip file');
        }

        $zip->setPassword($password);

        /**
         * Files & folders to include (ROOT)
         */
        $allowed = [
            'lib',
            'assets',
            'android',
            'ios',
            'pubspec.yaml',
            '.fvmrc',
            'analysis_options.yaml',
            'build_flow.sh',
            'refresh_the_engine.sh',
        ];

        foreach ($allowed as $item) {
            $fullPath = "{$buildDir}/{$item}";

            if (! file_exists($fullPath)) {
                //                Log::debug('ZIP skip missing', ['path' => $fullPath]);
                continue;
            }

            if (is_dir($fullPath)) {
                $this->zipDirectoryFiltered(
                    zip: $zip,
                    sourceDir: $fullPath,
                    zipRoot: $item,
                    password: $password,
                    buildTarget: $buildOrder->build_target
                );
            } else {
                $zip->addFile($fullPath, $item);
                $zip->setEncryptionName($item, ZipArchive::EM_AES_256);
            }
        }

        $zip->close();

        Log::info('ZIP created successfully', [
            'zipPath' => $zipPath,
            'buildOrderId' => $buildOrder->id,
        ]);

        return $zipPath;
    }

    private function zipDirectoryFiltered(
        ZipArchive $zip,
        string $sourceDir,
        string $zipRoot,
        string $password,
        string $buildTarget
    ): void {
        $basePath = realpath($sourceDir);
        if (! $basePath) {
            return;
        }

        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($basePath, \FilesystemIterator::SKIP_DOTS),
            \RecursiveIteratorIterator::SELF_FIRST
        );

        foreach ($iterator as $file) {
            if ($file->isLink()) {
                continue;
            }

            $realPath = $file->getRealPath();
            if (! $realPath) {
                continue;
            }

            $relativePath = ltrim(str_replace($basePath, '', $realPath), DIRECTORY_SEPARATOR);
            $zipPath = $zipRoot.'/'.$relativePath;

            /**
             * ---------------------------------------
             * iOS CLEANUP RULES (ONLY FOR IOS BUILDS)
             * ---------------------------------------
             */
            if ($buildTarget === 'ios') {
                if (
                    str_starts_with($zipPath, 'ios/Pods/') ||
                    str_starts_with($zipPath, 'ios/.symlinks/') ||
                    str_starts_with($zipPath, 'ios/build/') ||
                    str_ends_with($zipPath, '.ipa') ||
                    str_ends_with($zipPath, '.dSYM') ||
                    str_ends_with($zipPath, 'ios/Runner.app.dSYM.zip')
                ) {
                    continue;
                }
            }

            if ($file->isDir()) {
                $zip->addEmptyDir($zipPath);
            } else {
                $zip->addFile($realPath, $zipPath);
                $zip->setEncryptionName($zipPath, ZipArchive::EM_AES_256);
            }
        }
    }

    private function uploadZipToR2(string $zipPath, BuildOrder $buildOrder): string
    {
        $r2Path = 'build-zips/'.basename($zipPath);
        Storage::disk('r2')->putFileAs('build-zips', $zipPath, basename($r2Path), ['visibility' => 'public']);
        Log::info("Zip upload complete okay: $buildOrder->id");

        return config('app.image_public_path').$r2Path;
    }

    private function cleanupBuildDirectory(?string $buildDir): void
    {
        if (! empty($buildDir) && File::isDirectory($buildDir)) {
            // Get the parent directory of the given buildDir
            $parentDir = dirname($buildDir); // This will return ".../132_android_20250423_075239"

            if (File::isDirectory($parentDir)) {
                Log::info('Cleaning up parent build directory', ['buildDir' => $parentDir]);
                try {
                    File::deleteDirectory($parentDir);
                } catch (\Exception $e) {
                    Log::warning('Failed to clean up parent build directory', [
                        'buildDir' => $parentDir,
                        'error' => $e->getMessage(),
                    ]);
                }
            } else {
                Log::warning('Parent build directory does not exist', ['parentDir' => $parentDir]);
            }
        }
    }
}
