<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use App\Traits\AddonTranslationTrait;

class Addons extends MY_Controller
{
	use AddonTranslationTrait;

	public function __construct()
	{
		parent::__construct();
		is_login();
	}

	public function index()
	{
		$data = array();
		$data['page_title'] = "Add Ons";
		$data['page'] = "Settings";
		$this->load->model('system_model');
		$data['settings'] = $this->admin_m->single_select('settings');
		$addonsList = $this->admin_m->select('addons_list');
		$data['addonsList'] = array_column($addonsList, null, 'slug');
		$data['available_addons'] = system_addons();
		$data['main_content'] = $this->load->view('backend/addons/addons_list', $data, TRUE);
		$this->load->view('backend/index', $data);
	}


	public function uninstall($id)
	{
		$data = [
			'active_code' => '',
			'active_key' => '',
			'purchase_date' => '',
			'status' => 0,
			'is_install' => 0,
		];
		$this->admin_m->update($data, $id, 'addons_list');
		$this->session->set_flashdata('success', !empty(lang('success_text')) ? lang('success_text') : 'Save Change Successful');
		redirect($_SERVER['HTTP_REFERER']);
	}


	public function install_addons()
	{
		is_test();

		if (!class_exists('ZipArchive')) {
			$this->session->set_flashdata('error', 'PHP Zip extension is not enabled on server');
			return redirect($_SERVER['HTTP_REFERER']);
		}

		$this->form_validation->set_rules('purchase_code', 'Purchase Code', 'trim|required');
		$this->form_validation->set_rules('script_purchase_code', 'Script Purchase Code', 'trim|required');

		if (!$this->form_validation->run()) {
			$this->session->set_flashdata('error', validation_errors());
			return redirect($_SERVER['HTTP_REFERER']);
		}

		$purchase_code        = $this->input->post('purchase_code', true);
		$script_purchase_code = $this->input->post('script_purchase_code', true);

		/*
    |---------------------------------------------------
    | 1. Verify License FIRST
    |---------------------------------------------------
    */
		$check_valid = $this->verify_purchase($purchase_code, $script_purchase_code);

		if (empty($check_valid) || !isset($check_valid->st) || $check_valid->st != 1) {
			$msg = !empty($check_valid->msg) ? $check_valid->msg : 'Invalid License or Connection Error';
			$this->session->set_flashdata('error', $msg);
			return redirect($_SERVER['HTTP_REFERER']);
		}

		/*
    |---------------------------------------------------
    | 2. Upload Zip
    |---------------------------------------------------
    */
		if (empty($_FILES['addon_zip']['name'])) {
			$this->session->set_flashdata('error', 'Addon zip file is required');
			return redirect($_SERVER['HTTP_REFERER']);
		}

		if ($_FILES['addon_zip']['size'] > 5242880) {
			$this->session->set_flashdata('error', 'Zip file size must be less than 5MB');
			return redirect($_SERVER['HTTP_REFERER']);
		}

		$addons_dir = FCPATH . 'addons/';

		if (!is_dir($addons_dir)) {
			mkdir($addons_dir, 0755, true);
			copy(APPPATH . 'index.html', $addons_dir . 'index.html');
		}

		$config = [
			'upload_path'   => $addons_dir,
			'allowed_types' => 'zip',
			'max_size'      => 5120,
			'overwrite'     => true,
			'encrypt_name'  => true
		];

		$this->load->library('upload', $config);

		if (!$this->upload->do_upload('addon_zip')) {
			$this->session->set_flashdata('error', $this->upload->display_errors());
			return redirect($_SERVER['HTTP_REFERER']);
		}

		$file_data = $this->upload->data();
		$zip_file  = $file_data['full_path'];

		/*
    |---------------------------------------------------
    | 3. Extract Zip (Secure)
    |---------------------------------------------------
    */
		$extract = $this->extract_zip($zip_file, $addons_dir);
		unlink($zip_file);

		if (!$extract['status']) {
			$this->session->set_flashdata('error', $extract['error']);
			return redirect($_SERVER['HTTP_REFERER']);
		}

		/*
    |---------------------------------------------------
    | 4. Save to Database + Move Files
    |---------------------------------------------------
    */
		$this->load->model('system_model');
		$this->load->model('Updated_queries');

		$check = $this->admin_m->check_using_purchase_code($check_valid->purchase_code);

		$data = [
			'active_code'          => $check_valid->active_code,
			'purchase_code'        => $check_valid->purchase_code,
			'slug'                 => $check_valid->slug,
			'script_purchase_code' => $check_valid->script_purchase_code,
			'item_id'              => $check_valid->item_id,
			'script_name'          => $check_valid->script_name,
			'site_url'             => SITE_URL,
			'active_key'           => $check_valid->active_key,
			'license_name'         => $check_valid->license_name,
			'license_code'         => $check_valid->license_code,
			'purchase_date'        => $check_valid->sold_at,
			'activated_date'       => add_date('1', 'month'),
			'is_install'           => 1,
			'is_active'            => 1,
			'status'               => 1,
			'active_date'          => d_time(),
			'created_at'           => d_time(),
		];

		$this->db->trans_begin();

		try {

			$move_controller = $this->system_model->get_controller_files($check_valid->slug);
			$move_model      = $this->system_model->get_model_files($check_valid->slug);

			if ($move_controller !== true || $move_model !== true) {
				throw new Exception('Addon file movement failed');
			}

			if (!empty($check)) {
				$insert = $this->admin_m->update($data, $check->id, 'addons_list');
			} else {
				$insert = $this->admin_m->insert($data, 'addons_list');
			}

			if (!$insert) {
				throw new Exception('Database operation failed');
			}

			$this->Updated_queries->add_permissions($check_valid->slug);
			$this->Updated_queries->add_features($check_valid->slug);
			$this->add_language($check_valid->slug);

			if ($this->db->trans_status() === FALSE) {
				throw new Exception('Transaction failed');
			}

			$this->db->trans_commit();

			$this->session->set_flashdata('success', 'Addon Installed Successfully');
		} catch (Exception $e) {

			$this->db->trans_rollback();
			$this->session->set_flashdata('error', $e->getMessage());
		}

		return redirect(base_url('admin/addons'));
	}


	private function extract_zip($zip_file, $destination)
	{
		$zip = new ZipArchive;

		if ($zip->open($zip_file) !== TRUE) {
			return ['status' => false, 'error' => 'Failed to open zip file'];
		}

		// Basic protection against Zip Slip
		for ($i = 0; $i < $zip->numFiles; $i++) {
			$entry = $zip->getNameIndex($i);

			if (strpos($entry, '../') !== false || strpos($entry, '..\\') !== false) {
				$zip->close();
				return ['status' => false, 'error' => 'Invalid zip structure detected'];
			}
		}

		$zip->extractTo($destination);
		$zip->close();

		return ['status' => true];
	}




	protected function verify_purchase($purchase_code, $script_purchase_code)
	{
		try {

			$client = new Client([
				'base_uri' => URL,
				'timeout'  => 15,
			]);

			$response = $client->post('install-addons', [
				'json' => [
					'purchase_code'        => $purchase_code,
					'script_purchase_code' => $script_purchase_code,
					'site_url'             => SITE_URL,
					'is_addons'            => 1,
					'author'               => 'codetrick',
					'ip'                   => $this->version_changes_m->getIpAddress(),
				]
			]);


			if ($response->getStatusCode() !== 200) {
				return (object)[
					'st'  => 0,
					'msg' => 'License server returned invalid response'
				];
			}

			$body = json_decode($response->getBody()->getContents());

			if (json_last_error() !== JSON_ERROR_NONE) {
				return (object)[
					'st'  => 0,
					'msg' => 'Invalid JSON response from license server'
				];
			}

			return $body;
		} catch (RequestException $e) {

			return (object)[
				'st'  => 0,
				'msg' => $e->getMessage()
			];
		} catch (Exception $e) {

			return (object)[
				'st'  => 0,
				'msg' => $e->getMessage()
			];
		}
	}


	public function update_addons($slug = 'qpos')
	{
		is_test();

		$this->load->model(['admin_m', 'system_model']);

		/*
    |---------------------------------------------------
    | 1. Validate Addon
    |---------------------------------------------------
    */
		$addon = $this->db->get_where('addons_list', ['slug' => $slug, 'is_install' => 1])->row();

		if (empty($addon)) {
			$this->session->set_flashdata('error', 'Addon not found.');
			return redirect(base_url('admin/addons'));
		}

		if (empty($addon->purchase_code) || empty($addon->script_purchase_code)) {
			$this->session->set_flashdata('error', 'Purchase code not found in database.');
			return redirect(base_url('admin/addons'));
		}

		/*
    |---------------------------------------------------
    | 2. Verify License
    |---------------------------------------------------
    */
		$verification = $this->verify_purchase(
			$addon->purchase_code,
			$addon->script_purchase_code
		);

		if (empty($verification) || !isset($verification->st)) {
			$this->session->set_flashdata('error', 'License server error.');
			return redirect(base_url('admin/addons'));
		}

		if ((int)$verification->st !== 1) {

			// Only deactivate if explicitly invalid
			$this->admin_m->update([
				'active_code' => null,
				'is_active'   => 0
			], $addon->id, 'addons_list');

			$this->session->set_flashdata(
				'error',
				$verification->msg ?? 'Verification failed.'
			);

			return redirect(base_url('admin/addons'));
		}

		/*
    |---------------------------------------------------
    | 3. Update Files + DB (Transaction Safe)
    |---------------------------------------------------
    */
		$this->db->trans_begin();

		try {

			$controllerMoved = $this->system_model->get_controller_files($addon->slug);
			$modelMoved      = $this->system_model->get_model_files($addon->slug);

			if ($controllerMoved !== true) {
				$error = is_array($controllerMoved) && isset($controllerMoved['error'])
					? $controllerMoved['error']
					: 'Controller file move failed.';
				throw new Exception($error);
			}

			if ($modelMoved !== true) {
				$error = is_array($modelMoved) && isset($modelMoved['error'])
					? $modelMoved['error']
					: 'Model file move failed.';
				throw new Exception($error);
			}

			$this->admin_m->update([
				'active_date' => d_time(),
				'is_active'   => 1,
			], $addon->id, 'addons_list');

			if ($this->db->trans_status() === false) {
				throw new Exception('Database update failed.');
			}

			$this->db->trans_commit();

			$this->session->set_flashdata(
				'success',
				__('Addon verified and updated successfully.')
			);
		} catch (Exception $e) {

			$this->db->trans_rollback();

			$this->session->set_flashdata(
				'error',
				$e->getMessage()
			);
		}

		return redirect(base_url('admin/addons'));
	}

	public function install_local_addon($slug)
	{
		is_test();
		if ($slug == 'clone-restaurant' && check() == 0) {
			$this->session->set_flashdata('error', 'Clone Restaurant Addon is not available for this version');
			return redirect(base_url('admin/addons'));
		}

		$check = $this->db->get_where('addons_list', ['slug' => $slug])->row();
		$data = array(
			'slug' => $slug,
			'script_name' => ucwords(str_replace('-', ' ', $slug)),
			'is_install' => 1,
			'is_active' => 1,
			'status' => 1,
			'active_date' => d_time(),
			'created_at' => d_time(),
		);

		if ($this->verifyAddon($slug)->st != 1) {
			$this->session->set_flashdata('error', 'Addon verification failed');
			return redirect(base_url('admin/addons'));
		}

		if (!empty($check)) {
			$this->admin_m->update($data, $check->id, 'addons_list');
		} else {
			$this->admin_m->insert($data, 'addons_list');
		}

		$this->add_language($slug);
		$this->addPermission($slug);

		$this->session->set_flashdata('success', 'Addon Installed Successfully');
		redirect(base_url('admin/addons'));
	}

	protected function verifyAddon($slug)
	{


		try {

			$client = new Client([
				'base_uri' => URL,
				'timeout'  => 15,
			]);


			$response = $client->post('addon', [
				'headers' => [
					'Accept' => 'application/json'
				],
				'json' => [
					'name'     => $slug,
					'site_url' => SITE_URL,
					'id'       => st()->site_id ?? 0,
					'author'   => 'codetrick',
				]
			]);


			if ($response->getStatusCode() !== 200) {
				return (object)[
					'st'  => 0,
					'msg' => 'License server returned invalid response'
				];
			}

			$body = json_decode($response->getBody()->getContents());

			if (json_last_error() !== JSON_ERROR_NONE) {
				return (object)[
					'st'  => 0,
					'msg' => 'Invalid JSON response from license server'
				];
			}

			return $body;
		} catch (\GuzzleHttp\Exception\RequestException $e) {

			return (object)[
				'st'  => 0,
				'msg' => 'Unable to connect to license server'
			];
		} catch (\Exception $e) {

			return (object)[
				'st'  => 0,
				'msg' => 'License verification failed'
			];
		}
	}


	private function add_language($slug)
	{
		$data = [];
		if ($slug == 'clone-restaurant') {
			$data = $this->clone_translation();
		}

		if ($slug == 'inventory') {
			$data = $this->inventory_translation();
		}

		#insert into language_data table, if key exists then ignore

		if (isset($data) && !empty($data)):
			foreach ($data as $key => $value) {
				if (!empty($value['keyword'])) {
					$check_exists = $this->db->get_where('language_data', ['keyword' => $value['keyword']])->row();
					if (!$check_exists) {
						$this->db->insert('language_data', $value);
					}
				}
			}
		endif;
	}

	private function addPermission($slug)
	{
		$data = [];
		if ($slug == 'inventory') {
			$data = $this->inventory_permission();
		}


		if (isset($data) && !empty($data)):
			foreach ($data as $key => $value) :
				$check_slug = $this->check_slug($value['slug'], 'permission_list');
				if ($check_slug == 0) :
					$data = [
						'title' => $value['title'],
						'slug' => $value['slug'],
						'status' => 1,
						'role' => $value['role'],
					];
					$this->db->insert('permission_list', $data);
				endif;
			endforeach;
		endif;
	}

	public function check_slug($slug, $table)
	{
		$this->db->select();
		$this->db->from($table);
		$this->db->where('slug', $slug);
		$query = $this->db->get();
		if ($query->num_rows() > 0) {
			return 1;
		} else {
			return 0;
		}
	}
}
