<?php

namespace App\Http\Controllers\ManualResponse;

use App\Http\Controllers\Controller;
use App\Models\ManualResponse\MasterResponseTemplates;
use App\Models\ManualResponse\DeclarationRunning;
use Illuminate\Support\Facades\DB;
use SimpleXMLElement;
use DOMDocument;
use Carbon\Carbon;

class GenXmlController extends Controller
{
    public function generateXML($templateId, $documentType, $lockData)
    {
        $template = MasterResponseTemplates::join('mr_master_messages as mm', 'mr_master_response_templates.mid', '=', 'mm.id')
                        ->select('mm.name as messageName', 'mr_master_response_templates.id as templateId', 'mr_master_response_templates.template')
                        ->where('mr_master_response_templates.id', $templateId)
                        ->first();

        if (empty($template)) {
            return null;
        }

        $json = json_decode($template['template'], true);
        if($json != null){
            return $this->genXmlFromTemplate($json, $documentType, $lockData);
        }
    }

    private function genXmlFromTemplate($json, $documentType = '0', $lockData = true, $xml = null, &$xmlData = [])
    {
        if ($xml === null) {
            $rootElement = $json['root'];
            $namespace = $json['namespace'];
            unset($json['root'], $json['namespace']);
            if($namespace !== ''){
                $xml = new SimpleXMLElement("<?xml version=\"1.0\" encoding=\"UTF-8\"?><$rootElement xmlns=\"$namespace\"></$rootElement>");
            }else{
                $xml = new SimpleXMLElement("<?xml version=\"1.0\" encoding=\"UTF-8\"?><$rootElement></$rootElement>");
            }
        }

        foreach ($json as $key => $value) {
            if (is_array($value)) {
                if (is_numeric($key)) {
                    $key = 'item' . $key; // dealing with <0/>..<n/> issues
                }
                $new_object = $xml->addChild($key);
                $this->genXmlFromTemplate($value, $documentType, $lockData, $new_object, $xmlData); // Pass by reference
            } else {
                if (is_numeric($key)) {
                    $key = 'item' . $key; // dealing with <0/>..<n/> issues
                }

                if (empty($value)) {
                    if($key == 'DeclarationNumber' && ($documentType !== null && $documentType != '')){
                        $value = $this->generateDeclarationNo($documentType);
                    }else if($key == 'GoodsTransitionNumber'){
                        $value = $this->generateGCLNo();
                    }

                    if(in_array($key, ['AuditDateTime', 'ErrorDate'])){
                        $value = Carbon::now()->setTimezone('GMT+7')->format('Y-m-d\TH:i:s');
                    }
                    $child = $xml->addChild($key);
                    $child[0] = ''; // Ensure closed tag for empty value
                } else {
                    $xml->addChild($key, htmlspecialchars($value));
                }
                $xmlData[$key] = $value; // Collect all tags as editable input html
            }
        }

        //$this->updateXmlValues($this->formatXml($xml->asXML()), $xmlNoData)
        return ['xmlContent' => $this->formatXml($xml->asXML()), 
                'xmlWithData' => $xmlData
        ];
    }

    //Convert short tag in case of value is empty to closed tag
    private function formatXml($xmlString)
    {
        $dom = new DOMDocument('1.0', 'UTF-8');
        $dom->preserveWhiteSpace = false;
        $dom->formatOutput = true;
        $dom->loadXML($xmlString);

        // Ensure empty tags are closed
        $xpath = new \DOMXPath($dom);
        foreach ($xpath->query('//*[not(*) and not(text())]') as $node) {
            $node->nodeValue = ''; // Set node value to empty string to ensure closed tag
        }

        return $dom->saveXML();
    }

    public function updateXmlValues($xmlString, $tagsToUpdate)
    {
        $dom = new DOMDocument('1.0', 'UTF-8');
        $dom->loadXML($xmlString);

        foreach ($tagsToUpdate as $tag => $value) {
            $nodes = $dom->getElementsByTagName($tag);
            foreach ($nodes as $node) {
                $node->nodeValue = htmlspecialchars($value);
            }
        }

        return $dom->saveXML();
    }

    private function generateDeclarationNo($documentType)
    {

        $currentDate = Carbon::now()->toDateString();
        $maxRunningNumber = 999999;

        $running = DB::transaction(function () use ($currentDate, $maxRunningNumber, $documentType) {
            $runningNumberRecord = DeclarationRunning::lockForUpdate()->where('document_type', $documentType)->first();

            if ($runningNumberRecord) {
                if ($runningNumberRecord->last_updated_date !== $currentDate) {
                    // Reset the running number if the date has changed
                    $runningNumberRecord->running_number = 1;
                    $runningNumberRecord->last_updated_date = $currentDate;
                } elseif ($runningNumberRecord->running_number >= $maxRunningNumber) {
                    // Reset the running number if it reaches the max value
                    $runningNumberRecord->running_number = 1;
                } else {
                    // Increment the running number
                    $runningNumberRecord->running_number += 1;
                }
                $runningNumberRecord->save();
            } else {
                // Create a new record if none exists
                $runningNumberRecord = DeclarationRunning::create([
                    'running_number' => 1,
                    'last_updated_date' => $currentDate,
                    'document_type' => $documentType,
                ]);
            }

            return str_pad($runningNumberRecord->running_number, 6, '0', STR_PAD_LEFT);
        });

        $tmp_declarationNo = [
            "A",
            substr($running, 0, 1),
            Carbon::now()->format('d'),
            $documentType,
            substr(Carbon::now()->year + 543, -2),
            Carbon::now()->format('m'),
            substr($running, -5)
        ];

        return implode('', $tmp_declarationNo);
    }

    function generateGCLNo(){
        $currentDate = Carbon::now()->toDateString();
        $maxRunningNumber = 9999999;

        $running = DB::transaction(function () use ($currentDate, $maxRunningNumber) {
            $runningNumberRecord = DeclarationRunning::lockForUpdate()->where('document_type', "GCL")->first();

            if ($runningNumberRecord) {
                if ($runningNumberRecord->last_updated_date !== $currentDate) {
                    // Reset the running number if the date has changed
                    $runningNumberRecord->running_number = 1;
                    $runningNumberRecord->last_updated_date = $currentDate;
                } elseif ($runningNumberRecord->running_number >= $maxRunningNumber) {
                    // Reset the running number if it reaches the max value
                    $runningNumberRecord->running_number = 1;
                } else {
                    // Increment the running number
                    $runningNumberRecord->running_number += 1;
                }
                $runningNumberRecord->save();
            } else {
                // Create a new record if none exists
                $runningNumberRecord = DeclarationRunning::create([
                    'running_number' => 1,
                    'last_updated_date' => $currentDate,
                    'document_type' => "GCL",
                ]);
            }

            return str_pad($runningNumberRecord->running_number, 7, '0', STR_PAD_LEFT);
        });

        $tmp_GCLNo = [
            substr(Carbon::now()->year + 543, -2),
            Carbon::now()->format('m'),
            "A",
            $running
        ];

        return implode('', $tmp_GCLNo);
    }
}
