<?php
if (!defined('ABSPATH')) {
    exit;
}

use YusufKandemir\MicrodataParser\Microdata;
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints as Assert;
use Topwebstudio\Parsers\JsonLdParser;
use Topwebstudio\Parsers\MicrodataParser;
use Topwebstudio\Parsers\SchemaConverter;
use JasonGrimes\Paginator;

class SchematicBuilder {

    public function __construct() {
        add_action('init', array($this, 'init'));

        add_action('wp_ajax_schematic_load_schema', array($this, 'loadSchema'));
        add_action('wp_ajax_schematic_save_schema', array($this, 'saveSchema'));
        add_action('wp_ajax_schematic_save_options', array($this, 'saveOptions'));
        add_action('wp_ajax_schematic_api_register', array($this, 'apiRegister'));
        add_action('wp_ajax_schematic_api_login', array($this, 'apiLogin'));
        add_action('wp_ajax_schematic_api_save_profile', array($this, 'apiSaveProfile'));
        add_action('wp_ajax_schematic_save_options_catalog', array($this, 'saveOptionsCatalog'));
        add_action('wp_ajax_schematic_save_template_catalog', array($this, 'saveTemplateCatalog'));
        add_action('wp_ajax_schematic_save_template', array($this, 'saveTemplate'));
        add_action('wp_ajax_schematic_delete_schema', array($this, 'deleteSchema'));
        add_action('wp_ajax_schematic_load_data', array($this, 'loadData'));
        add_action('wp_ajax_schematic_load_schema_feeds', array($this, 'loadSchemaFeeds'));
        add_action('wp_ajax_schematic_load_schema_templates', array($this, 'loadSchemaTemplates'));
        add_action('wp_ajax_schematic_parser', array($this, 'parser'));
        add_action('wp_ajax_schematic_parse_html', array($this, 'parseHtml'));

        foreach (getSchematicChosenPages() as $taxonomy) {
            if ($taxonomy['groupId'] === 'list' && isSchematicPageChosen($taxonomy['objectType'])) {
                add_action($taxonomy['objectType'] . '_edit_form_fields', array($this, 'taxonomyFields'), 10, 2);
            }
        }


        if (isSchematicPageChosen('attachment')) {
            add_action('attachment_submitbox_misc_actions', array($this, 'attachmentMeta'));
        }

        add_action('add_meta_boxes', array($this, 'postMeta'));
    }

    public function attachmentMeta($post) {
        ?>

        <div class="misc-pub-section>
        <?php getSchematicEditButtonHtml($post); ?>
             </div>

             <?php
         }

         public function postMeta() {
             global $post;

             if ($this->isGutenbergEnabled($post) && shouldSchemaPluginLoad()) {

                 $pages = getSchematicPagesIdsByType('single');

                 add_meta_box('page_shortcode_examples', 'Schema', array($this, 'pageButton'), $pages, 'side', 'high');
             }
         }

         public function pageButton($post) {
             if ($this->isGutenbergEnabled($post)) {
                 ?> <div class="misc-pub-section">
                 <?php getSchematicEditButtonHtml($post); ?>  
            </div>   <?php
        }
    }

    public function taxonomyFields($term) {
        ?>
        <tr class="form-field">
            <th scope="row" valign="top"> Schematic </label></th>
        <td>
            <?php getSchematicEditButtonHtml(null, $term); ?>
        </td>
        </tr>
        <?php
    }

    public function init() {
        if (current_user_can('administrator')) {

            if (isset($_GET['schematic-export-private-schemas']) && wp_verify_nonce($_GET['nonce'], 'schematic-nonce')) {
                $this->exportPrivateSchemas();
                exit();
            }

            if (isset($_GET['schematic-page-schemas']) && wp_verify_nonce($_GET['nonce'], 'schematic-nonce')) {
                $this->importPrivateSchemas();
                exit();
            }
        }
    }

    public function importPrivateSchemas() {
        if (isset($_FILES['files']['tmp_name'][0])) {
            $contents = file_get_contents($_FILES['files']['tmp_name'][0]);

            if ($contents) {
                $array = $this->getSchemaResponse($contents);

                if (is_array($array) && !empty($array)) {
                    $this->save_schematic_templates($array);
                    wp_send_json_success();
                }
            }
        }
    }

    public function exportPrivateSchemas() {
        $data = $this->get_schematic_templates();

        $filename = "wp-schema-plugin-json-schemas-" . date("Y-m-d") . ".json";

        $now = gmdate("D, d M Y H:i:s");
        header("Expires: Tue, 03 Jul 2001 06:00:00 GMT");
        header("Cache-Control: max-age=0, no-cache, must-revalidate, proxy-revalidate");
        header("Last-Modified: {$now} GMT");


        header("Content-Type: application/force-download");
        header("Content-Type: application/octet-stream");
        header("Content-Type: application/download");

        header("Content-Disposition: attachment;filename={$filename}");
        header("Content-Transfer-Encoding: binary");

        $fp = fopen("php://output", 'w');
        fwrite($fp, json_encode($data));
        fclose($fp);
        die();
    }

    public function converter($schema) {
        global $schematicCompatability;

        $functions = array(
            'invokeSchematicPhpFunctions' => array(),
            'schematicShortcodes' => array(),
        );

        if ($schematicCompatability->getMppInstalled()) {
            $functions['magicpageShortcodes'] = array();
        }


        $converter = new SchemaConverter($schema);
        $converter->setFunctions($functions);

        return $converter->getOutput();
    }

    public function loadSchema() {
        if (current_user_can('administrator')) {
            check_ajax_referer('schematic-nonce');

            $objectType = isset($_POST['objectType']) && $_POST['objectType'] ? strip_tags($_POST['objectType']) : null;
            $groupId = isset($_POST['groupId']) && $_POST['groupId'] ? strip_tags($_POST['groupId']) : null;
            $objectId = isset($_POST['objectId']) && $_POST['objectId'] ? intval($_POST['objectId']) : null;
            $postPageGroup = isset($_POST['postPageGroup']) && $_POST['postPageGroup'] ? strip_tags($_POST['postPageGroup']) : null;


            $templates = array();

            if ($objectId) {
                if ($groupId === 'single') {
                    $templates = getSchematicPostSchema($objectId, $postPageGroup, true);
                } elseif ($groupId === 'list') {
                    $templates = getSchematicListSchema($objectId, $objectType);
                }
            } else {
                if ($objectType === $groupId) {
                    $templates = getSchematicDataByType($objectType);
                } else {
                    $templates = get_option('_' . $objectType . '_schematic_schema', array());
                }
            }


            header('Content-Type: application/json');
            echo json_encode($templates);
        }
        exit();
    }

    protected function getSchemaResponse($res = null) {
        if ($res) {
            $response = stripslashes_deep($res);

            if (gettype($response) === 'string') {
                return json_decode($response, true);
            }

            if (gettype($response) === 'array') {
                return $response;
            }
        }
    }

    public function saveSchema() {


        if (current_user_can('administrator')) {
            check_ajax_referer('schematic-nonce');

            $schema = $this->getSchemaResponse($_POST['schemaElementsArray']);
            $objectType = $_POST['objectType'];
            $groupId = $_POST['groupId'];

            $objectId = isset($_POST['objectId']) ? intval($_POST['objectId']) : null;
            $saveall = isset($_POST['save-all']) ? boolval($_POST['save-all']) : false;
            $saveOption = isset($_POST['saveOption']) ? boolval($_POST['saveOption']) : false;
            $postPageGroup = isset($_POST['postPageGroup']) && $_POST['postPageGroup'] ? sanitize_title($_POST['postPageGroup']) : null;


            // homepage and alike    
            if ($objectType == $groupId) {
                update_option('_' . $objectType . '_schematic_schema', $schema);
                exit('OK');
            }

            global $wpdb;

            if (!$postPageGroup) {
                delete_option('_group_schema_' . $postPageGroup . '_schematic_schema');
            } else {
                update_option('_group_schema_' . $postPageGroup . '_schematic_schema', $schema);
            }

            if ($groupId === 'single') {

                if ($saveall || $saveOption) {
                    updateSchematicSchemaOption($objectType, $schema);
                }

                if ($saveall) {
                    $wpdb->query($wpdb->prepare("DELETE pm FROM $wpdb->postmeta pm LEFT JOIN $wpdb->posts wp ON wp.ID = pm.post_id WHERE pm.meta_key = '_schematic_schema' AND wp.post_type = %s", $objectType));
                }

                if (!$schema) {
                    delete_post_meta($objectId, '_schematic_schema');
                } else {
                    update_post_meta($objectId, '_schematic_schema', $schema);
                }
            } else if ($groupId === 'list') {
                if ($saveall || $saveOption) {
                    updateSchematicSchemaOption($objectType, $schema);
                }

                if ($saveall) {
                    $wpdb->query($wpdb->prepare("DELETE tm FROM $wpdb->termmeta tm LEFT JOIN $wpdb->term_taxonomy tt ON tt.term_id = tm.term_id WHERE tm.meta_key = '_schematic_schema' AND tt.taxonomy = %s", $objectType));
                }

                if (!$schema) {
                    delete_term_meta($objectId, '_schematic_schema');
                } else {
                    update_term_meta($objectId, '_schematic_schema', $schema);
                }
            }
        }

        exit('OK');
    }

    public function saveOptions() {
        if (current_user_can('administrator')) {
            check_ajax_referer('schematic-nonce');

            if ($_POST['options']) {
                $options = getSchematicOptions();
                foreach ($_POST['options'] as $option => $value) {
                    $options[$option] = $value;
                }
            }

            $this->setSchematicOptions($options);
        }

        exit('OK');
    }

    public function saveOptionsCatalog() {
        if (current_user_can('administrator') && wp_verify_nonce($_POST['_ajax_nonce'], 'schematic-nonce')) {

            $_options = $_POST['options'];

            $options = getSchematicCatalogOptions();

            if ($_options) {
                foreach ($_options as $option => $value) {
                    $options[$option] = $value;
                }
            }

            setSchematicCatalogOptions($options);

            header('Content-Type: application/json');
            echo json_encode($options);
        }
        exit();
    }

    public function saveTemplateCatalog() {
        if (current_user_can('administrator')) {
            check_ajax_referer('schematic-nonce');

            $data = array(
                'title' => $_POST['title'],
                'description' => $_POST['description'],
                'tags' => $_POST['tags'],
                'schema' => $_POST['schema'],
                'email' => $_POST['email'],
                'uid' => $_POST['uid'],
            );

            global $schematicApi;

            $response = $schematicApi->schema_submit($data);

            echo json_encode($response);
        }
        exit();
    }

    public function saveTemplate() {
        if (current_user_can('administrator')) {
            check_ajax_referer('schematic-nonce');

            $title = $_POST['title'];
            $id = strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $title))); // sluggify function
            $description = $_POST['description'];
            $tags = $_POST['tags'];

            $schema = $this->getSchemaResponse($_POST['schema']);


            $datetime = new \DateTime();

            $templates = $this->get_schematic_templates();

            if (!$title || !$description) {
                header('Content-Type: application/json');
                echo json_encode(array('error' => 'true', 'message' => 'Title and Description are mandatory fields'));
                exit();
            }

            if ($templates && isset($templates[$id])) {
                header('Content-Type: application/json');
                echo json_encode(array('error' => 'true', 'message' => 'Schema with the same title already exists'));
                exit();
            }

            $templates = array_reverse($templates);

            $templates[$id] = array(
                'id' => $id,
                'title' => $title,
                'description' => $description,
                'tags' => $tags,
                'schema' => $schema,
                'date' => $datetime->format('Y-m-d')
            );

            $templates = array_reverse($templates);

            $this->save_schematic_templates($templates);
        }

        exit('OK');
    }

    public function deleteSchema() {
        if (current_user_can('administrator')) {
            check_ajax_referer('schematic-nonce');

            $id = $_POST['id'];

            $templates = $this->get_schematic_templates();

            if ($templates && isset($templates[$id])) {
                unset($templates[$id]);

                $this->save_schematic_templates($templates);

                exit('OK');
            }
        }
    }

    public function loadData() {
        if (current_user_can('administrator')) {
            check_ajax_referer('schematic-nonce');

            $output = array();

            $schemaData = $this->getSchemaResponse($_POST['schemaElementsArray']);

            if ($schemaData) {
                if (!isset($_POST['flag'])) {
                    foreach ($schemaData as $id => $schema) {
                        if ($schema) {

                            $converter = new SchemaConverter($schema);
                            $output[$id] = $converter->getOutput();
                        }
                    }
                } else {

                    $converter = new SchemaConverter($schemaData);
                    $output = $converter->getOutput();
                }
            }

            header('Content-Type: application/json');

            echo json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        }
        exit();
    }

    public function loadSchemaFeeds() {
        if (current_user_can('administrator')) {
            check_ajax_referer('schematic-nonce');

            header('Content-Type: application/json');

            global $schematicApi;

            $response = $schematicApi->schema_browse_database();
            echo(json_encode($response));
        }
        exit();
    }

    public function parseHtml() {
        if (current_user_can('administrator')) {
            check_ajax_referer('schematic-nonce');

            $options = getSchematicOptions();
            $content = stripslashes($_POST['html']);

            if (!$content) {
                header('Content-Type: application/json');
                echo(json_encode(array('message' => 'Please post some valid HTML', 'error' => 'true')));
                exit();
            }
            $result = array('microdata' => array(), 'jsonld' => array());

            if ($options['microdata_format'] == 'true') {
                $microdata = Microdata::fromHTML($content)->toArray();

                $microdataParser = new MicrodataParser($microdata);
                $result['microdata'] = $microdataParser->getParsedData();
            }

            if ($options['json_format'] == 'true') {
                $jsonld = new JsonLdParser($content);
                $result['jsonld'] = $jsonld->getParsedData();
            }

            header('Content-Type: application/json');
            echo(json_encode(['output' => $result, 'error' => 'false'], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
        }

        exit();
    }

    public function parser() {
        if (current_user_can('administrator')) {
            check_ajax_referer('schematic-nonce');

            $urls = preg_split('/\r\n|\r|\n/', trim($_POST['url']));
            $urls = array_map('trim', $urls);
            $urls = array_unique($urls);


            if (count($urls) <= 1) {
                $this->parseSingleUrl($urls[0]);
            } else {
                $this->parseMultipleUrls($urls);
            }
        }

        exit();
    }

    protected function parseMultipleUrls($urls) {
        $options = getSchematicOptions();
        $output = array();

        foreach ($urls as $id => $url) {
            $output[$id] = array(
                'url' => $url
            );

            $validator = Validation::createValidator();
            $violations = $validator->validate($url, array(
                new NotBlank(),
                new Assert\Url(array('checkDNS' => Assert\Url::CHECK_DNS_TYPE_ANY))
            ));

            if (0 !== count($violations)) {
                $output[$id]['error'] = 'true';
                $output[$id]['message'] = '';

                foreach ($violations as $violation) {
                    $output[$id]['message'] .= $violation->getMessage() . ' ';
                }
                continue;
            }
            ///
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
            $content = curl_exec($ch);

            if (curl_errno($ch)) {
                $output[$id]['error'] = 'true';
                $output[$id]['message'] = curl_error($ch);
            }

            curl_close($ch);

            if (!$content) {
                $output[$id]['error'] = 'true';
                $output[$id]['message'] = 'This page is not responding or has no content';
            }
            ///

            if ($options['microdata_format'] == 'true') {
                $microdata = Microdata::fromHTML($content)->toArray();

                $microdataParser = new MicrodataParser($microdata);
                $output[$id]['microdata'] = $microdataParser->getParsedData();
            }

            if ($options['json_format'] == 'true') {
                $jsonld = new JsonLdParser($content);
                $output[$id]['jsonld'] = $jsonld->getParsedData();
            }
        }

        header('Content-Type: application/json');
        echo(json_encode(['output' => $output], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
    }

    protected function parseSingleUrl($url) {
        $options = getSchematicOptions();

        $validator = Validation::createValidator();
        $violations = $validator->validate($url, array(
            new NotBlank(),
            new Assert\Url(array('checkDNS' => Assert\Url::CHECK_DNS_TYPE_ANY))
        ));

        if (0 !== count($violations)) {
            $output = array('error' => 'true', 'message' => '');
            foreach ($violations as $violation) {
                $output['message'] .= $violation->getMessage() . ' ';
            }

            header('Content-Type: application/json');
            echo json_encode($output);
            exit();
        }

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        $content = curl_exec($ch);

        if (curl_errno($ch)) {
            header('Content-Type: application/json');
            echo(json_encode(array('message' => curl_error($ch), 'error' => 'true')));
            exit();
        }

        curl_close($ch);

        if (!$content) {
            header('Content-Type: application/json');
            echo(json_encode(array('message' => 'This page is not responding or has no content', 'error' => 'true')));
            exit();
        }

        $result = array('microdata' => array(), 'jsonld' => array());

        if ($options['microdata_format'] == 'true') {
            $microdata = Microdata::fromHTML($content)->toArray();

            $microdataParser = new MicrodataParser($microdata);
            $result['microdata'] = $microdataParser->getParsedData();
        }

        if ($options['json_format'] == 'true') {
            $jsonld = new JsonLdParser($content);
            $result['jsonld'] = $jsonld->getParsedData();
        }

        header('Content-Type: application/json');
        echo(json_encode(['output' => $result, 'error' => 'false', 'single' => true], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
    }

    public function loadSchemaTemplates() {
        if (current_user_can('administrator') && wp_verify_nonce($_POST['_ajax_nonce'], 'schematic-nonce')) {

            $itemsPerPage = 5;
            $options = getSchematicOptions();
            $array = $this->get_schematic_templates();

            if (isset($options['search']) && $options['search'] && !empty($array)) {
                $found = array();
                foreach ($array as $id => $items) {
                    if (stripos(serialize($items), $options['search']) !== false) {
                        $found[$id] = $items;
                    }
                }

                $array = $found;
            }

            $currentPage = isset($options['page']) && $options['page'] ? $options['page'] : 1;
            $paginator = new Paginator(count($array), $itemsPerPage, $currentPage);

            $pageRange = array();

            if ($paginator->getPages()) {
                foreach ($paginator->getPages() as $page) {
                    $pageRange[] = $page['num'];
                }
            }

            $pagination = array(
                'totalCount' => count($array),
                'numItemsPerPage' => $itemsPerPage,
                'current' => intval($currentPage),
                'pageRange' => $pageRange
            );

            $start = intval($currentPage) * $itemsPerPage - $itemsPerPage;

            $schemas = array_slice($array, $start, $itemsPerPage);

            header('Content-Type: application/json');
            echo json_encode(array(
                'schemas' => $schemas,
                'pagination' => $pagination,
                'parameters' => $options
            ));
        }
        exit();
    }

    public function apiRegister() {
        $data = $_POST['data'];

        if (current_user_can('administrator') && wp_verify_nonce($data['_ajax_nonce'], 'schematic-nonce')) {
            header('Content-Type: application/json');

            global $schematicApi;
            if (!$data['email'] || !$data['password'] || !$data['nickname']) {
                header('Content-Type: application/json');
                echo json_encode(array('error' => 'true', 'info' => 'You must provide nickname, email and password fields.'));
                exit();
            }

            $data['password'] = md5($data['password']);

            $response = $schematicApi->schema_api_register($data);
            echo(json_encode($response));
        }
        exit();
    }

    public function apiLogin() {
        $data = $_POST['data'];

        if (current_user_can('administrator') && wp_verify_nonce($data['_ajax_nonce'], 'schematic-nonce')) {

            header('Content-Type: application/json');

            global $schematicApi;

            if (!$data['email'] || !$data['password']) {
                header('Content-Type: application/json');
                echo json_encode(array('error' => 'true', 'info' => 'You must provide email and password fields.'));
                exit();
            }

            $data['password'] = md5($data['password']);

            $response = $schematicApi->schema_api_login($data);
            echo(json_encode($response));
        }
        exit();
    }

    public function apiSaveProfile() {
        $data = $_POST['data'];

        if (current_user_can('administrator') && wp_verify_nonce($data['_ajax_nonce'], 'schematic-nonce')) {

            header('Content-Type: application/json');

            global $schematicApi;


            if (!$data['nickname'] || !$data['email']) {
                header('Content-Type: application/json');
                echo json_encode(array('error' => 'true', 'info' => 'You must provide email and nickname'));
                exit();
            }

            if (isset($data['password'])) {
                $data['password'] = md5($data['password']);
            }


            $response = $schematicApi->schema_api_save_profile($data);
            echo(json_encode($response));
        }
        exit();
    }

    private function get_schematic_templates() {
        return get_option('_schematic_templates', array());
    }

    private function save_schematic_templates($templates) {
        update_option('_schematic_templates', stripslashes_deep($templates));
    }

    private function isGutenbergEnabled($post) {
        if ($post && use_block_editor_for_post($post)) {
            return true;
        }
    }

}

$schematicSchemaBuilder = new SchematicBuilder();
