<?php

namespace App\Http\Livewire\Pages\Patch;

use Livewire\Component;
use App\Models\ConfSmartUpdate;
use App\Models\TabPatchFile;
use App\Models\User;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Support\Facades\Cache;

class PatchEdit extends Component
{
    public $patchId;
    public $message;
    public $progressSave = 0, $isProcessing = false, $progress = 0, $currentStep = 0;
    public $searchProject = '';
    public $selectedBranch, $selectedPatch, $showModal;
    public $selectedProject;
    public $projects = [];
    public $branches = [];
    public $fileGitChanges = [];
    public $filePatchChanges = [];
    public $filePatch = [];
    public $fileChangesTemp = [];
    public $fileChanges = [];
    public $startCommit = '';
    public $endCommit = '';
    public $PATCHNAME;
    public $PDATE;
    public $PHP_VERSION;
    public $PLEVEL;
    public $PCODE;
    public $MAJOR_VERSION;
    public $PDESC;
    public $Remark;
    public $POWNER;
    public $POWNERNAME;
    public $PAPPROVEDATE;
    public $PTYPE;
    public $PATCHCODE;
    public $UNINSTALL;
    public $PATCHCODE_SERVER;
    public $filePathChanges = [];
    protected $listeners = ['gotoModal', 'reloadComponent', 'deletePatchFile'];
    public $modalLoaded = false;

    public $contentType = 'application/json';
    public function loadModal()
    {
        $this->modalLoaded = true;
    }
    public function mount($editPid)
    {
        // $editPid = '15689';  // remove this when real 
        $this->patchId = $editPid;
        $this->loadPatchData();
    }

    
    public function updatedSearchProject()
    {
        $this->fetchProjects();
    }
    public function fetchProjects()
    {
        $client = new Client([
            'headers' => [
                'Authorization' => 'Bearer ' . env('GITLAB_API_TOKEN'),
                'Accept' => $this->contentType,
            ],
            'verify' => false,
        ]);

        $perPage = 100;
        $response = $client->get(env('GITLAB_API_URL') . '/projects', [
            'query' => [
                'membership' => true,
                'min_access_level' => 30,
                'search' => $this->searchProject,
                'per_page' => $perPage,
            ],
        ]);
        $this->projects = json_decode($response->getBody(), true);
        $this->dispatchBrowserEvent('projects-fetched');
    }
    public function getChangedFiles()
    {
        $this->fileChangesTemp = [];

        $client = new Client([
            'headers' => [
                'Authorization' => 'Bearer ' . env('GITLAB_API_TOKEN'),
                'Accept' => $this->contentType,
            ],
            'verify' => false,
        ]);

        try {
            $response = $client->get(env('GITLAB_API_URL') . "/projects/{$this->selectedProject}/repository/compare", [
                'query' => [
                    'from' => $this->startCommit,
                    'to' => $this->endCommit,
                ],
            ]);

            $data = json_decode($response->getBody(), true);


            foreach ($data['diffs'] as $file) {


                $this->fileChangesTemp[] = $file['new_path'];
            }

            $this->fileGitChanges = $this->buildTreeEdit($data['diffs']);
        } catch (\Throwable $th) {
            $data = '';
        }
    }

    private function buildTreeEdit($files)
    {
        $tree = [];
        foreach ($files as $file) {
            $path = explode('/', $file['new_path']);
            $current = &$tree;
            foreach ($path as $part) {
                if (!isset($current[$part])) {
                    $current[$part] = [];
                }
                $current = &$current[$part];
            }
        }
        return $tree;
    }

    public function reloadComponent($id)
    {
        $this->patchId = $id;
        $this->loadPatchData();
        $this->render();
    }
    public function loadPatchData()
    {
        $patch = ConfSmartUpdate::findOrFail($this->patchId);
       
        if(!isset($patch->user)) {
            $owner = User::where('id', $patch->POWNER)->first();
            $this->POWNERNAME = $owner->username;
        }else{
            $this->POWNERNAME = $patch->user->username;
        }
       
        $this->PATCHNAME = $patch->PATCHNAME;
        $this->PDATE = $patch->PDATE;
        $this->PHP_VERSION = $patch->PHP_VERSION;
        $this->PLEVEL = $patch->PLEVEL;
        $this->PCODE = $patch->PCODE;
        $this->MAJOR_VERSION = $patch->MAJOR_VERSION;
        $this->PDESC = $patch->PDESC;
        $this->Remark = $patch->Remark;
        $this->POWNER = $patch->POWNER;
        
        $this->PAPPROVEDATE = $patch->PAPPROVEDATE;
        $this->PTYPE = $patch->PTYPE;
        $this->PATCHCODE = $patch->PATCHCODE;
        $this->UNINSTALL = $patch->UNINSTALL;
        $this->PATCHCODE_SERVER = $patch->PATCHCODE_SERVER;

        $filePath = TabPatchFile::select('fid','ptid','file_name')->where("ptid", $this->patchId)->get()->toArray();
        $this->filePatch =  $filePath;
        
        $this->filePatchChanges =  $this->buildTree($filePath);
    }
    public function startProcess()
    {
        $this->progress = 0;
        $this->isProcessing = true;
        $this->dispatchBrowserEvent('process-started');
    }
    public function getProgress()
    {
        return $this->progress;
    }

    public function processStep()
    {
        $confSmartUpdate = ConfSmartUpdate::findOrFail($this->patchId);
        $confSmartUpdate->PATCHNAME = $this->PATCHNAME;
        $confSmartUpdate->PDATE = $this->PDATE;
        $confSmartUpdate->PHP_VERSION_ID = $this->PHP_VERSION;
        $confSmartUpdate->PLEVEL = $this->PLEVEL;
        $confSmartUpdate->PCODE = $this->PCODE;
        $confSmartUpdate->MAJOR_VERSION = $this->MAJOR_VERSION;
        $confSmartUpdate->PDESC = $this->PDESC;
        $confSmartUpdate->Remark = $this->Remark;
        $confSmartUpdate->POWNER = $this->POWNER;
        $confSmartUpdate->PAPPROVEDATE = $this->PAPPROVEDATE;
        $confSmartUpdate->PTYPE = $this->PTYPE;
        $confSmartUpdate->PATCHCODE = $this->PATCHCODE;
        $confSmartUpdate->UNINSTALL = $this->UNINSTALL;
        $confSmartUpdate->PATCHCODE_SERVER = $this->PATCHCODE_SERVER;
        $confSmartUpdate->save();

        if ($this->isProcessing && count($this->fileChangesTemp) > 0) {
            $totalFiles = count($this->fileChangesTemp);
            $this->currentStep =  $totalFiles - ($totalFiles - $this->currentStep);
            if ($this->currentStep < $totalFiles) {
                $file = $this->fileChangesTemp[$this->currentStep];
                $filedata = $this->getFileContentFromGit($file, $this->endCommit);

                $filePath = $this->formatFilePath($file , 'SPN');

                $existingFile = TabPatchFile::where('file_name', $filePath)
                    ->where('ptid', $confSmartUpdate->PID)
                    ->first();

                if (!$existingFile) {
                    $filepath = new TabPatchFile;
                    $filepath->ptid = $confSmartUpdate->PID;
                    $filepath->file_name = $filePath;
                    $filepath->file_data = base64_encode($filedata);
                    $filepath->save();
                }

                $this->currentStep += 1;
                $this->progress = round(($this->currentStep / $totalFiles) * 100, 2);
                if ($this->progress == 100) {
                    $this->message = 'Patch details and file changes updated successfully.';
                    $this->emit('reloadComponent', $this->patchId);
                    $this->reset(['fileChangesTemp', 'fileGitChanges', 'currentStep']);
                }
            }
        }

        return $this->progress;
    }



    private function formatFilePath($file ,$project)
    {
        $lowerProject = strtolower($project);
        $upperProject = strtoupper($project);
        if (strpos($file, $upperProject.'/') !== false) {
            return str_replace("$upperProject/", "../$upperProject/", $file);
        } elseif (strpos($file, '$lowerProject/') !== false) {
            return str_replace("$lowerProject/", "../$lowerProject/", $file);
        } else {
            return str_replace("IE5DEV.shippingnet", ".", $file);
        }
    }

    private function getFileContentFromGit($filePath, $commit)
    {
        $token = env('GITLAB_API_TOKEN');

        $client = new Client([
            'base_uri' => 'https://idemo.netbay.co.th/gitlab/api/v4/',
            'headers' => [
                'Authorization' => "Bearer $token",
                'Accept' => $this->contentType,
            ],
            'verify' => false,
        ]);

        try {
            $response = $client->get("projects/$this->selectedProject/repository/files/" . urlencode($filePath) . "/raw", [
                'query' => ['ref' => $commit]
            ]);
            $statusCode = $response->getStatusCode();
            $content = $response->getBody()->getContents();

            if ($statusCode == 200) {
                return $content;
            } else {
                throw new \Exception("Failed to fetch file. Status code: $statusCode");
            }
        } catch (RequestException $e) {
            throw $e;
        } catch (\Exception $e) {
            throw $e;
        }
    }

    private function buildTree($files)
    {
        $tree = [];
        
        foreach ($files as $file) {
            $filePath = $this->generateFilePath($file['file_name']);
            $pathParts = explode('/', $filePath);
            $current = &$tree;

            foreach ($pathParts as $part) {
                if (!isset($current[$part])) {
                    $current[$part] = [];
                }
                $current = &$current[$part];
            }

            $current['id'] = $file['fid'] ?? 0;
        }

        return $tree;
    }

    private function generateFilePath($fileName)
    {
        if (strpos($fileName, "IE5DEV.shippingnet") !== false) {
            return $this->processShippingNetPath($fileName);
        } elseif (strpos($fileName, "IE5DEV.seamanifest") !== false) {
            return $this->processSeaManifestPath($fileName);
        }
        return $fileName;
    }

    private function processShippingNetPath($fileName)
    {
        if (strpos($fileName, "../SPN/") !== false) {
            return str_replace("../SPN/", "SPN/", $fileName);
        } elseif (strpos($fileName, "../spn/") !== false) {
            return str_replace("../spn/", "spn/", $fileName);
        }
        return "IE5DEV.shippingnet" . substr($fileName, 1);
    }
    private function processSeaManifestPath($fileName)
    {
        if (strpos($fileName, "../seamanifest/") !== false) {
            return str_replace("../seamanifest/", "seamanifest/", $fileName);
        } elseif (strpos($fileName, "../SEAMANIFEST/") !== false) {
            return str_replace("../SEAMANIFEST/", "SEAMANIFEST/", $fileName);
        }
        return "IE5DEV.seamanifest" . substr($fileName, 1);
    }

    public function loadPage($page)
    {
        $this->emit('menuChanged', $page);
    }


    public function deletePatchFile($patchFileId)
    {
        $pathFile = TabPatchFile::where("fid", $patchFileId)->first();
        $patchId = $pathFile->ptid;

        TabPatchFile::where("fid", $patchFileId)->delete();
        $message = "Deleted File ID : " . json_encode($patchFileId) . " Successfully";

        $this->message = $message;
        $this->emit('reloadComponent', $patchId);
    }
    public function deleteSelectedPatchFiles($selectedFiles)
    {
        foreach ($selectedFiles as $patchFileId) {
            $pathFile = TabPatchFile::where("fid", $patchFileId)->first();
            $patchId = $pathFile->ptid;

            TabPatchFile::where("fid", $patchFileId)->delete();
        }

        $this->emit('reloadComponent', $patchId);
    }

    public function render()
    {
        return view('livewire.pages.patch.patch-edit');
    }
}
