<?php

/**
 * Vvveb
 *
 * Copyright (C) 2022  Ziadin Givan
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

namespace Vvveb\Controller\Content;

use function Vvveb\__;
use Vvveb\Controller\Base;
use function Vvveb\getSetting;
use function Vvveb\humanReadable;
use function Vvveb\model;
use function Vvveb\sanitizeHTML;
use function Vvveb\setSetting;
use function Vvveb\slugify;
use Vvveb\Sql\CategorySQL;
use Vvveb\Sql\Field_groupSQL;
use Vvveb\Sql\FieldSQL;
use Vvveb\System\Cache;
use Vvveb\System\CacheManager;
use Vvveb\System\Core\View;
use Vvveb\System\Event;
use Vvveb\System\Images;
use Vvveb\System\Sites;
use Vvveb\System\Traits\FieldGroup;
use Vvveb\System\User\Admin;
use Vvveb\System\Validator;

/**
 * Handles post/page editing, including content, images, taxonomies, fields, and revisions.
 */
class Edit extends Base {
	protected string $type = 'post';

	protected string $object = 'post';

	protected bool $revisions = true;

	use TaxonomiesTrait, AutocompleteTrait, SitesTrait, FieldGroup;

	/**
	 * Returns the theme folder path for the current site.
	 *
	 * @return string Theme folder path.
	 */
	protected function getThemeFolder(): string {
		return DIR_THEMES . (Sites::getTheme($this->global['site_id']) ?? 'default');
	}

	/**
	 * Loads custom field groups and renders them for the editor view.
	 *
	 * @return void
	 */
	protected function fields(): void {
		$options        = ['type' => $this->object, 'subtype' => $this->type, 'limit' => 1000];
		$fieldGroup     = new Field_groupSQL();
		$fieldGroups    = $fieldGroup->getAll($options + $this->global)['field_group'] ?? [];

		$langsFields    = [];
		$field_group_id = [];

		foreach ($fieldGroups as $group) {
			$field_group_id[$group['field_group_id']] = $group['field_group_id'];
		}

		if ($field_group_id) {
			$post_id = $this->request->get[$this->object . '_id'] ?? false;

			$field       = new FieldSQL();
			$fields	     = $field->getAll(['field_group_id' => $field_group_id, $this->object . '_id' => $post_id, 'limit' => 1000] + $this->global);
			$langsFields = [];

			if ($fields['count'] > 0) {
				foreach ($fields['field'] as &$field) {
					$input['settings']     = json_decode($field['settings'], true);
					$input['validation']   = json_decode($field['validation'], true);
					$input['presentation'] = json_decode($field['presentation'], true);

					$field['class']        = 'row mb-3';
					$field['label-class']  = 'form-label';
					$field['label']        = $input['settings']['label'] ?? '';
					$field['instructions'] = $input['presentation']['instructions'] ?? '';
					$field['placeholder']  = $input['settings']['placeholder'] ?? '';

					foreach (['value', 'default'] as $key) {
						if (isset($field[$key]) && $field[$key]) {
							if ($field[$key][0] == '{') {
								$field[$key] = json_decode($field[$key], true);
							}

							$field['value'] = $field[$key];

							break;
						}
					}

					foreach ($this->view->languagesList as $code => $lang) {
						$field['name']   = 'field[' . $lang['language_id'] . '][' . $field['field_id'] . ']';
						$field['field']  = $this->renderFields2([$field]);

						$groupName                                       = $fieldGroups[$field['field_group_id']]['name'];
						$langsFields[$lang['language_id']][$groupName][] = $field;
					}
				}
			}
		}
		$this->view->fields = $langsFields;
		$this->view->count  = $fields['count'] ?? 0;
	}

	/**
	 * Main index action - loads post data, templates, revisions, taxonomies, and sites for the editor.
	 *
	 * @return void
	 */
	public function index(): void {
		$view = $this->view;

		$admin_path  = \Vvveb\adminPath();
		$postOptions = [];
		$post        = [];
		$post_id     = $this->request->get[$this->object . '_id'] ?? $this->request->post[$this->object . '_id'] ?? false;

		$controllerPath  = $admin_path . 'index.php?module=media/media';
		$view->scanUrl   = "$controllerPath&action=scan";
		$view->uploadUrl = "$controllerPath&action=upload";
		$view->linkUrl   = $admin_path . 'index.php?module=content/post&action=urlAutocomplete';
		$theme           = Sites::getTheme($this->global['site_id']) ?? 'default';
		$view->themeCss  = '';

		if (file_exists(DIR_THEMES . "$theme/css/admin-post-editor.css")) {
			$view->themeCss .= PUBLIC_PATH . "themes/$theme/css/admin-post-editor.css";
		} else {
			if (file_exists(DIR_THEMES . "$theme/css/style.css")) {
				$view->themeCss .= PUBLIC_PATH . "themes/$theme/css/style.css";
			}
		}

		foreach (['custom.css', 'fonts.css'] as $css) {
			if (file_exists(DIR_THEMES . "$theme/css/$css")) {
				$view->themeCss .= ',' . PUBLIC_PATH . "themes/$theme/css/$css";
			}
		}
		//$view->themeCss        = PUBLIC_PATH . "themes/$theme/css/style.css";

		$viewCapability = 'view_other_posts';

		if ($this->object == 'product') {
			$viewCapability = 'view_other_products';
		}

		if ($post_id) {
			$postOptions[$this->object . '_id'] = (int)$post_id;
		} else {
			if (isset($this->request->get['slug'])) {
				$postOptions['slug'] = $this->request->get['slug'];
			}
		}

		if (isset($this->request->get['type'])) {
			$this->type = $this->request->get['type'];
		}

		if ($postOptions) {
			$posts = model($this->object);

			$postOptions['type'] = $this->type;
			$options             = $postOptions + $this->global;

			if (Admin::hasCapability($viewCapability)) {
				unset($options['admin_id']);
			} else {
				$options['admin_id'] = $this->global['admin_id'];
			}

			//get all languages
			//unset($options['language_id']);
			$post                = $posts->get($options);

			if (! $post) {
				$message = sprintf(__('%s not found!'), humanReadable(__($this->type)));
				$this->notFound(['message' => $message, 'title' => $message]);
			}

			//featured image
			if (isset($post['image'])) {
				$post['image_url'] = Images::image($post['image'], $this->object);
			}

			//gallery
			if (isset($post[$this->object . '_image'])) {
				$post['images'] = Images::images($post[$this->object . '_image'], $this->object);
			}

			//$productImages = $posts->getImages($postOptions);
		} else {
			$post['image_url']  = Images::image('',$this->object);
			$post['updated_at'] = date('Y-m-d H:i:s', time());
		}

		/*
		if (isset($post['updated_at'])) {
			$post['updated_at'] = str_replace(' ', 'T', $post['updated_at']);
		} else {
			$post['updated_at'] = date("Y-m-d\TH:i:s", isset($post['updated_at']) && $post['updated_at'] ? strtotime($post['updated_at']) : time());
		}*/

		$this->type = $post['type'] ?? $this->type;

		if ($this->object == 'product') {
			$route      = "product/{$this->type}/index";
			$altRoute   = "product/{$this->object}/index";
			$controller = 'product';
		} else {
			$route      = "content/{$this->type}/index";
			$altRoute   = "content/{$this->object}/index";
			$controller = 'content';
			$stickyPosts = getSetting('posts', 'sticky') ?? [];
			//$post['sticky'] = in_array($post['post_id'], $stickyPosts);
			$post['sticky'] = isset($post['post_id']) && isset($stickyPosts[$post['post_id']]);
		}

		if ($this->revisions) {
			$revisions = model($this->object . '_content_revision');
		}

		//get site host for current selected site to use for absolute url
		$url = ['host' => $this->global['host'], 'path' => $this->global['path'] ? '/' . $this->global['path'] : null];

		if ($this->type != $this->object) {
			$url['type'] = $this->type;
		}

		$revisionsUrl = \Vvveb\url(['module' => "$controller/revisions", 'object' => $this->object, 'type' => $this->type, $this->object . '_id' => $post_id]);
		$name         = '';

		if (isset($post[$this->object . '_content'])) {
			foreach ($post[$this->object . '_content'] as &$content) {
				if (! isset($post['url'])) {
					$post['url']          = \Vvveb\url($route, ['slug'=> $content['slug'], $this->object . '_id' => $post_id] + $url);
					$post['relative-url'] = \Vvveb\url($route, ['slug'=> $content['slug'], $this->object . '_id' => $post_id]);

					if (! $post['url'] || $post['url'] == '//' . $this->global['site_url']) {
						$post['url']          = \Vvveb\url($altRoute, ['slug'=> $content['slug'], $this->object . '_id' => $post_id] + $url);
						$post['relative-url'] = \Vvveb\url($altRoute, ['slug'=> $content['slug'], $this->object . '_id' => $post_id]);
					}
				}
				$language = [];

				if ($content['language_id'] != $this->global['default_language_id']) {
					$code = 'en_US';

					foreach ($this->view->languagesList as $code => $lang) {
						if ($lang['language_id'] == $content['language_id']) {
							break;
						}
					}
					$language = ['language' => $code];
				} else {
					$name = $content['name'];
				}

				$content['url']             = \Vvveb\url($route, $content + $language + $url);
				$content['revision_count']  = 0;

				if (! $content['url'] || $content['url'] == '//' . $this->global['site_url']) {
					$content['url']         = \Vvveb\url($altRoute, $content + $language + $url);
				}

				if ($revisions) {
					$revision = $revisions->getAll([$this->object . '_id' => $post_id, 'language_id' => $content['language_id']]);

					if ($revision) {
						foreach ($revision[$this->object . '_content_revision'] as &$rev) {
							$rev['preview-url'] = $content['url'] . '?revision=preview&created_at=' . $rev['created_at'] . '&language_id=' . $content['language_id'];
							$rev['compare-url'] = $revisionsUrl . '&created_at=' . $rev['created_at'] . '&language_id=' . $content['language_id'];
						}

						$content['revision_count'] = $revision['count'];
						$content['revision']       = $revision[$this->object . '_content_revision'];
					}
				}
			}
		}

		$type_name  = humanReadable(__($this->type));

		$defaultTemplate = \Vvveb\getCurrentTemplate();
		$template        = isset($post['template']) && $post['template'] ? $post['template'] : $defaultTemplate;
		$themeFolder     = $this->getThemeFolder();

		$design_url = '';

		if (isset($post['url'])) {
			$design_url         = \Vvveb\url(['module' => 'editor/editor', 'name' => urlencode($name),  'url' => $post['relative-url'], 'template' => $template], false);
			$post['design_url'] = $design_url;
		}

		$view->template_missing = '';

		if (! file_exists($themeFolder . DS . $template)) {
			if ($template == $defaultTemplate) {
				$view->template_missing = sprintf(__('Template missing, choose existing template or %screate global template%s for %s.'), '<a href="' . $design_url . '" target="_blank">', '</a>', $type_name);
			} else {
				$view->template_missing = sprintf(__('Template %s missing, %screate template%s for this %s.'), '<b>' . $template . '</b>', '<a href="' . $design_url . '" target="_blank">', '</a>', $type_name);
			}
		}

		$view->taxonomies = $this->taxonomies($post[$this->object . '_id'] ?? false);

		if ($this->type == 'page') {
			//remove menu type
			unset($view->taxonomies[3]);
		}

		$sites = $post[$this->object . '_to_site'] ?? [];

		if ($sites) {
			$sites = array_keys($sites);
		} else {
			if (! $post_id) {
				$sites[] = $this->global['site_id'];
			}
		}

		$view->sitesList = $this->sites($sites);

		list($post, $post_id, $this->type) = Event :: trigger(__CLASS__,__FUNCTION__, $post, $post_id, $this->type);

		$object          = $this->object;
		$view->$object   = $post;
		$view->status    = ['publish' => 'Publish', 'draft' => 'Draft', 'pending' => 'Pending', 'private' => 'Private', 'scheduled' => 'Scheduled'];

		$view->templates = Cache::getInstance()->cache(APP, 'template-list-' . $theme, function () use ($theme) {
			return \Vvveb\getTemplateList($theme, ['email']);
		}, 604800);

		$view->themeFonts = Cache::getInstance()->cache(APP, 'fonts-list-' . $theme, function () use ($theme) {
			$fonts = \Vvveb\System\Media\Font::themeFonts($theme);
			$names = [];

			if ($fonts) {
				foreach ($fonts as $font) {
					if (isset($font['font-family'])) {
						$names[$font['font-family']] = null;
					}
				}
			}

			return $names;
		}, 604800);

		$this->fields();

		//$validator                 = new Validator([$this->object]);
		//$view->validatorJson       = $validator->getJSON();
		$view->type           = __($this->type);
		$view->type_name      = $type_name;
		$view->posts_list_url =  \Vvveb\url(['module' => $this->list, 'type' => $this->type]);
		$view->revisions_url  =  $revisionsUrl;
		$view->oEmbedProxyUrl = $admin_path . 'index.php?module=editor/editor&action=oEmbedProxy';
	}

	/**
	 * Adds a new category taxonomy item.
	 *
	 * @param  int    $taxonomy_id The taxonomy ID.
	 * @param  string $name        The category name.
	 * @return int    The new taxonomy_item ID.
	 */
	private function addCategory(int $taxonomy_id, string $name): int {
		$categories = new CategorySQL();
		$cat        = $categories->addCategory([
			'taxonomy_item' => $this->global + [
				'taxonomy_id' => $taxonomy_id,
			],
			'taxonomy_item_content' => $this->global + ['slug'=> slugify($name), 'name' => $name, 'content' => ''],
		] + $this->global);

		return $category_id = $cat['taxonomy_item'];
	}

	/**
	 * Alias for save().
	 *
	 * @return void
	 */
	public function add(): void {
		$this->save();
	}

	/**
	 * Saves post/page content, handles validation, revisions, images, taxonomies, fields, and cache.
	 *
	 * @return void
	 */
	public function save(): void {
		$view                          = view :: getInstance();
		$post_id                       = $this->request->get[$this->object . '_id'] ?? $this->request->post[$this->object . '_id'] ?? false;
		$this->{$this->object . '_id'} = $post_id;
		$publicPath                    = \Vvveb\publicUrlPath();

		if (isset($this->request->get['type'])) {
			$this->type          = $this->request->get['type'];
		}

		$validator                     = new Validator([$this->object]);
		$validatorContent              = new Validator([$this->object . '_content']);

		$postContent                   = $this->request->post[$this->object . '_content'] ?? [];
		if (! is_array($postContent)) {
			$postContent = [];
		}

		if (
			(($errors = $validator->validate($this->request->post)) === true) &&
			(($errors = $validatorContent->validate(current($postContent))) === true)
		) {
			$posts = model($this->object);

			$post = $this->request->post;

			foreach ($post[$this->object . '_content'] as &$content) {
				$content['name']    = strip_tags($content['name']);
				$content['content'] = sanitizeHTML($content['content']);

				if (isset($content['excerpt'])) {
					$content['excerpt'] = sanitizeHTML($content['excerpt']);
				}
			}

			/*
			if (isset($post['updated_at'])) {
				$post['updated_at'] = str_replace(' ', 'T', $post['updated_at']);
			} else {
				$post['updated_at'] = date("Y-m-d\TH:i:s", time());
			}*/

			//process tags
			if (isset($post['tag'])) {
				foreach ($post['tag'] as $listId => $tags) {
					foreach ($tags as $tagId => $tag) {
						//existing tag add to post taxonomy_item list
						if (is_numeric($tagId)) {
							//$post['taxonomy_item_id'][] = $tagId;
						} else {
							//add new taxonomy_item
							$tagId = $this->addCategory($listId, $tag);
						}
						$post['taxonomy_item_id'][] = $tagId;
					}
				}
			}

			//process fields
			$post_field_value = [];

			if (isset($post['field'])) {
				foreach ($post['field'] as $language_id => $fields) {
					foreach ($fields as $field_id => $value) {
						if (is_array($value)) {
							$value = json_encode($value);
						}

						$post_field_value[] = ['field_id' => $field_id, 'language_id' => $language_id, 'value' => $value];
					}
				}
			}

			$site_id = $this->request->post['site'] ?? []; //[$this->global['site_id']];

			$post = $post + $this->global;
			$new  = false;

			if ($post_id) {
				$editCapability = 'edit_other_posts';

				if ($this->object == 'product') {
					$editCapability = 'edit_other_products';
				}

				if (Admin::hasCapability($editCapability)) {
					//unset($data['admin_id']);
				} else {
					//$data['admin_id'] = $this->global['admin_id'];
					$view->errors[] = __('Permission denied!');
					return;
				}

				$post[$this->object . '_id'] = (int)$post_id;
				$data                        = [
					$this->object              => $post,
					$this->object . '_id'      => $post_id,
					$this->object . '_content' => $post[$this->object . '_content'],
					'taxonomy_item_id'         => $post['taxonomy_item_id'] ?? [],
					'post_field_value'         => $post_field_value,
					'site_id'                  => $site_id,
				] + $this->global;

				$result = $posts->edit($data);

				if ($result >= 0) {
					$this->view->success['get'] = ucfirst($this->type) . ' ' . __('saved') . '!';

					$stickyPosts = getSetting('posts', 'sticky') ?? [];

					if (isset($post['sticky']) && ! isset($stickyPosts[$post_id])) {
						$stickyPosts[$post_id] = $post_id;
						setSetting('posts', 'sticky', $stickyPosts);
					}

					if (isset($stickyPosts[$post_id]) && ! isset($post['sticky'])) {
						unset($stickyPosts[$post_id]);
						setSetting('posts', 'sticky', $stickyPosts);
					}



					if ($this->revisions) {
						$revisions = model($this->object . '_content_revision');

						//foreach ($post[$this->object . '_content'] as &$content) {
						foreach ($this->request->post[$this->object . '_content'] as &$content) {
							if (isset($content['has_changes']) && $content['has_changes'] == 1) {
								$revisions->add(['revision' => [
									$this->object . '_id' => $post_id,
									'language_id'         => $content['language_id'],
									'content'             => $content['content'],
									'created_at'          => date("Y-m-d\TH:i:s", time()),
								] + $this->global]);
							}
						}
					}
				} else {
					$this->view->errors = [$posts->error];
				}
			} else {
				$post['type']                      = $this->type;
				//$post['created_at']                = $post['updated_at'];
				if (isset($post['updated_at']) && ! $post['updated_at']) {
					unset($post['updated_at'], $post[$this->object . '_id']);
				}

				$add = [
					$this->object              => $post,
					$this->object . '_content' => $post[$this->object . '_content'],
					'taxonomy_item_id'         => $post['taxonomy_item_id'] ?? [],
					'post_field_value'         => $post_field_value,
					'site_id'                  => $site_id,
				] + $this->global;

				$return = $posts->add($add);
				$id     = $return[$this->object] ?? false;

				if (! $id) {
					$view->errors = [$posts->error];
				} else {
					$this->request->get[$this->object . '_id'] = $id;

					$post_id = $id;
					$new     = true;

					$message              = ucfirst($this->type) . ' ' . __('saved') . '!';
					$view->success['get'] = $message;

					if (isset($post['sticky'])) {
						$stickyPosts = getSetting('posts', 'sticky') ?? [];
						$stickyPosts[$post_id] = $post_id;
						setSetting('posts', 'sticky', $stickyPosts);
					}
				}
			}

			if (isset($post[$this->object . '_image'])) {
				$productImage = $this->object . 'Image';

				foreach ($post[$this->object . '_image'] as &$image) {
					$image = str_replace($publicPath . 'media/','', $image);
				}

				$posts->$productImage([$this->object . '_id' => $post_id ? $post_id : 0, $this->object . '_image' => $post[$this->object . '_image']]);
			}

			list($post, $post_id, $this->type) = Event :: trigger(__CLASS__,__FUNCTION__, $post, $post_id, $this->type);

			CacheManager::clearObjectCache('component', $this->object);
			CacheManager::clearObjectCache('component', $this->object . 's');
			CacheManager::clearPageCache();

			if ($new) {
				$this->redirect(['module'=>$this->module, $this->object . '_id' => $id, 'type' => $this->type, 'success' => $message], [], false);
			}
		} else {
			$view->errors = $errors;
		}
	}
}
