<?php

namespace App\Http\Controllers\Admin;

use Log;
use Exception;
use ZipArchive;
use Illuminate\Http\Request;
use App\Models\ProgrammingLanguage;
use App\Http\Controllers\Controller;
use App\Models\KaaliiPackageVersion;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;

class PackageVesrionController extends Controller
{
    public function index(Request $request)
{
    $query = KaaliiPackageVersion::with('techStack')->orderBy('id', 'desc');

    if ($request->filled('search')) {
        $search = $request->search;
        $query->where('version', 'like', "%{$search}%")
              ->orWhereHas('techStack', function ($q) use ($search) {
                  $q->where('name', 'like', "%{$search}%");
              });
    }

    if ($request->filled('tech_stack')) {
        $techStackId = $request->tech_stack;
        $query->where('tech_stack', $techStackId);
    }

    if ($request->filled('status')) {
        $status = $request->status;
        $query->where('status', $status);
    }

    $versions = $query->paginate(10)->withQueryString();
    $techStacks = ProgrammingLanguage::all();

    return view('admin.package-version.index', compact('versions', 'techStacks'));
}

    public function create()
    {
        $tech_stacks =ProgrammingLanguage::all(); 
        return view('admin.package-version.create', compact('tech_stacks'));
    }


    public function store(Request $request)
    {
        $request->validate([
            'tech_stack'   => 'required|string|max:50',
            'version'      => 'required|string|max:50',
            'zip_file'     => 'required|file|mimes:zip|max:5120', // 5MB
            'release_notes'=> 'nullable|string',
        ]);

        try {
            $exists = KaaliiPackageVersion::where('tech_stack', $request->tech_stack)
                                        ->where('version', $request->version)
                                        ->exists();

            if ($exists) {
                return back()->with('error', 'This version already exists for the selected tech stack.');
            }

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

            if (!$zipFile || !$zipFile->isValid()) {
                return back()->with('error', 'Invalid ZIP file.');
            }

            $zipName = time() . '_' . $zipFile->getClientOriginalName();
            $tempZipDir = storage_path("app/temp_zips");

            if (!file_exists($tempZipDir)) {
                mkdir($tempZipDir, 0777, true);
            }

            $zipPath = $tempZipDir . "/" . $zipName;
            $zipFile->move($tempZipDir, $zipName);

            // Encode ZIP to Base64
            $zipBase64 = base64_encode(file_get_contents($zipPath));

            // Extract ZIP
            $zip = new \ZipArchive();
            if ($zip->open($zipPath) !== true) {
                return back()->with('error', 'Failed to open ZIP file.');
            }

            $extractFolder = "extracted_packages/" . uniqid('pkg_');
            $extractPath = storage_path("app/" . $extractFolder);
            mkdir($extractPath, 0777, true);

            if (!$zip->extractTo($extractPath)) {
                $zip->close();
                return back()->with('error', 'Failed to extract ZIP file.');
            }

            $zip->close();

            if (file_exists($zipPath)) {
                unlink($zipPath);
            }

            KaaliiPackageVersion::create([
                'tech_stack'  => $request->tech_stack,
                'version'     => $request->version,
                'file_base64' => $zipBase64,
                'path'        => $extractFolder,
                'status'      => 'draft',
            ]);
            return redirect()->route('admin.packageversions.index')->with('success', 'Package uploaded, extracted, and saved successfully!');

        } catch (Exception $e) {
            return back()->with('error', 'Error: ' . $e->getMessage());
        }
    }


    public function show($id)
    {
        $version = KaaliiPackageVersion::findOrFail($id);

        if (!$version->path) {
            $tree = "No extracted folder found.";
            return view('admin.package-version.show', compact('version','tree'));
        }

        $rootPath = storage_path("app/" . $version->path);

        if (!is_dir($rootPath)) {
            $tree = "Extracted directory does not exist: " . $rootPath;
            return view('admin.package-version.show', compact('version','tree'));
        }

        $tree = $this->buildDirectoryTree($rootPath);

        return view('admin.package-version.show', compact('version','tree'));
    }

    private function buildDirectoryTree($path, $prefix = "")
    {
        if (!is_dir($path)) return "";

        $output = "";
        $items = array_values(array_diff(scandir($path), ['.', '..']));

        foreach ($items as $item) {

            $fullPath = $path . DIRECTORY_SEPARATOR . $item;

            $output .= $prefix . "├── " . $item . "\n";

            $newPrefix = $prefix . "│   ";
            // dd($fullPath);

            if (is_dir($fullPath)) {
                $output .= $this->buildDirectoryTree($fullPath, $newPrefix);
            }
        }

        return $output;
    }


    public function edit($id)
    {
        $version = KaaliiPackageVersion::findOrFail($id);
        $tech_stacks = ProgrammingLanguage::all();
        return view('admin.package-version.edit', compact('version', 'tech_stacks'));
    }
    
    public function update(Request $request, $id)
    {
        $version = KaaliiPackageVersion::findOrFail($id);

        $request->validate([
            'tech_stack' => 'required|string|max:50',
            'version'    => 'required|string|max:50',
            'zip_file'   => 'required|file|mimes:zip|max:5120', 
        ]);

        try {
            $exists = KaaliiPackageVersion::where('tech_stack', $request->tech_stack)
                        ->where('version', $request->version)
                        ->where('id', '!=', $id)
                        ->exists();

            if ($exists) {
                return redirect()
                    ->route('admin.packageversions.index')
                    ->with('error', 'This version already exists for the selected tech stack.');
            }

            if ($request->hasFile('zip_file') && $request->file('zip_file')->isValid()) {

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

                $tempDir = storage_path("app/temp_zips");
                if (!file_exists($tempDir)) {
                    mkdir($tempDir, 0777, true);
                }

                $zipName = time() . '_' . $zipFile->getClientOriginalName();
                $zipPath = $tempDir . "/" . $zipName;
                $zipFile->move($tempDir, $zipName);

                $zipBase64 = base64_encode(file_get_contents($zipPath));

                $extractFolder = "extracted_packages/" . uniqid('pkg_');
                $extractPath = storage_path("app/" . $extractFolder);
                mkdir($extractPath, 0777, true);

                $zip = new \ZipArchive();
                if ($zip->open($zipPath) !== true) {
                    return redirect()
                        ->route('admin.packageversions.index')
                        ->with('error', 'Unable to open ZIP file.');
                }

                if (!$zip->extractTo($extractPath)) {
                    $zip->close();
                    return redirect()
                        ->route('admin.packageversions.index')
                        ->with('error', 'Failed to extract ZIP file.');
                }

                $zip->close();
                if (file_exists($zipPath)) {
                    unlink($zipPath);
                }

                $version->file_base64 = $zipBase64;
                $version->path = $extractFolder;
            }

            $version->tech_stack = $request->tech_stack;
            $version->version = $request->version;
            $version->save();

            return redirect()
                ->route('admin.packageversions.index')
                ->with('success', 'Package version updated successfully!');

        } catch (QueryException $e) {
            if ($e->errorInfo[1] == 1062) {
                return redirect()
                    ->route('admin.packageversions.index')
                    ->with('error', 'Duplicate version entry for this tech stack.');
            }
            return redirect()
                ->route('admin.packageversions.index')
                ->with('error', 'Database Error: ' . $e->getMessage());
        } catch (Exception $e) {
            return redirect()
                ->route('admin.packageversions.index')
                ->with('error', 'Error: ' . $e->getMessage());
        }
    }

    
    public function destroy($id)
    {
        $version = KaaliiPackageVersion::findOrFail($id);
        if ($version->file_base64) {
            $paths = json_decode($version->file_base64, true);
            if (isset($paths['root'])) {
                $extractPath = storage_path('app/' . $paths['root']);
                if (is_dir($extractPath)) {
                    $this->deleteDirectory($extractPath);
                }
            }
        }
        $version->delete();

        return redirect()
            ->route('admin.packageversions.index')
            ->with('success', 'Package version deleted successfully!');
    }

    private function deleteDirectory($dir)
    {
        if (!file_exists($dir)) return;

        $files = array_diff(scandir($dir), ['.', '..']);
        foreach ($files as $file) {
            $path = "$dir/$file";
            if (is_dir($path)) {
                $this->deleteDirectory($path);
            } else {
                unlink($path);
            }
        }
        rmdir($dir);
    }

    public function toggleStatus($id)
    {
        $version = KaaliiPackageVersion::findOrFail($id);
        $states = ['draft', 'published', 'archived'];
        $currentIndex = array_search($version->status, $states);
        $nextIndex = ($currentIndex + 1) % count($states);
        $version->status = $states[$nextIndex];
        $version->save();
        return back()->with('success', 'Status updated!');
    }

    public function downloadExtracted($id)
{
    $version = KaaliiPackageVersion::findOrFail($id);
    // dd($version->path);

    if (empty($version->path)) {
        return back()->with('error', 'No extracted folder found.');
    }

    $folderPath = storage_path("app/" . $version->path);

    if (!is_dir($folderPath)) {
        return back()->with('error', 'Extracted folder is missing.');
    }

    $tmpDir = storage_path("app/tmp");
    if (!is_dir($tmpDir)) {
        mkdir($tmpDir, 0777, true);
    }

    $zipPath = $tmpDir . "/package_{$version->id}.zip";

    $zip = new \ZipArchive;
    if ($zip->open($zipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) === TRUE) {

        $files = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($folderPath, \RecursiveDirectoryIterator::SKIP_DOTS),
            \RecursiveIteratorIterator::SELF_FIRST
        );

        foreach ($files as $file) {
            $filePath = $file->getRealPath();
            $relative = substr($filePath, strlen($folderPath) + 1);
            if ($file->isFile()) {
                $zip->addFile($filePath, $relative);
            }
        }

        $zip->close();
    } else {
        return back()->with('error', 'Could not create ZIP file.');
    }

    return response()->download($zipPath)->deleteFileAfterSend(true);
}




}
