<?php

namespace CleantalkSP\SpbctWP\FSWatcher\Analyzer;

use CleantalkSP\SpbctWP\FSWatcher\Controller;
use CleantalkSP\SpbctWP\FSWatcher\Logger;
use CleantalkSP\SpbctWP\FSWatcher\Storage\FileStorage;

class Analyzer
{
    /**
     * @return array|false
     */
    public static function getCompareResult()
    {
        $first = filter_var($_POST['fswatcher__first_date'], FILTER_VALIDATE_INT);
        $second = filter_var($_POST['fswatcher__second_date'], FILTER_VALIDATE_INT);

        if ($first > $second) {
            $tmp = $first;
            $first = $second;
            $second = $tmp;
        }

        $storage = Controller::$storage;

        $first_journal = $storage::getJournal($first);
        $second_journal = $storage::getJournal($second);

        if (!$first_journal || !$second_journal) {
            return false;
        }

        if (Controller::$debug) {
            Logger::log('first journal ' . $first_journal);
            Logger::log('second journal ' . $second_journal);
        }

        return self::compare($first_journal, $second_journal);
    }

    /**
     * @return string|false
     */
    public static function getViewFile()
    {
        $path = isset($_POST['fswatcher_file_path']) ? $_POST['fswatcher_file_path'] : false;

        $journals_first = isset($_POST['fswatcher__first_date']) ? $_POST['fswatcher__first_date'] : false;
        $journals_second = isset($_POST['fswatcher__second_date']) ? $_POST['fswatcher__second_date'] : false;
        $journals = array($journals_first, $journals_second);

        if (!$path || !is_file($path)) {
            throw new \Exception('File path is incorrect.');
        }

        if (!$journals[0] || !$journals[1]) {
            throw new \Exception('Provided journals paths are incorrect.');
        }

        $path_found_in_journal = false;

        foreach ($journals as $journal_id) {
            if (
                self::isFileOfFSWJournal($path, $journal_id) &&
                self::isFileOfFSWJournalAfterCompareJournals($path)
            ) {
                $path_found_in_journal = true;
                break;
            }
        }

        if (!$path_found_in_journal) {
            throw new \Exception('The file is out of FSWatcher journals.');
        }

        return esc_html__(file_get_contents($path));
    }

    /**
     * @param $path
     * @param $journal_id
     * @return bool
     */
    private static function isFileOfFSWJournal($path, $journal_id)
    {
        $storage = new FileStorage();
        $journal_parsed = $storage->getJournal($journal_id);
        $analyzer = new Analyzer();
        $journal_parsed = $analyzer->uncompress($journal_parsed, true);
        if (
                strpos($journal_parsed, $path) !== false ||
                strpos($journal_parsed, stripslashes($path)) !== false
        ) {
            return true;
        }
        return false;
    }

    /**
     * @param $path
     * @return bool
     */
    private static function isFileOfFSWJournalAfterCompareJournals($path)
    {
        $journal_result_compare = self::getCompareResult();
        $journal_result_string = '';
        foreach ($journal_result_compare as $journal_result) {
            if (count($journal_result) > 0) {
                foreach ($journal_result as $value) {
                    $journal_result_string .= implode(' , ', $value);
                }
            }
        }

        if (
            strpos($journal_result_string, $path) !== false ||
            strpos($journal_result_string, stripslashes($path)) !== false
        ) {
            return true;
        }

        return false;
    }

    /**
     * @param $first_journal
     * @param $second_journal
     * @return array|false
     */
    private static function compare($first_journal, $second_journal)
    {
        $result = array(
            'added' => array(),
            'deleted' => array(),
            'changed' => array(),
        );

        //return no diff if csv names is equal
        if ( $first_journal === $second_journal) {
            return $result;
        }

        //return no diff if md5 sums is equal
        if (md5(@file_get_contents($first_journal)) === md5(@file_get_contents($second_journal))) {
            return $result;
        }

        $first_journal = self::uncompress($first_journal);
        $second_journal = self::uncompress($second_journal);

        if ( !$first_journal || !$second_journal) {
            return false;
        }

        $first_array = [];
        $second_array = [];

        $esc_char = version_compare(PHP_VERSION, '8.4', '>=') ? '' : '\\';

        try {
            $fp_first = fopen($first_journal, 'r');
            while ($first = fgetcsv($fp_first, 0, ',', '"', $esc_char)) {
                $first_array[$first[0]] = $first[1];
            }
            fclose($fp_first);
            @unlink($first_journal);

            $fp_second = fopen($second_journal, 'r');
            while ($second = fgetcsv($fp_second, 0, ',', '"', $esc_char)) {
                $second_array[$second[0]] = $second[1];
            }
            fclose($fp_second);
            @unlink($second_journal);

            foreach ($first_array as $path => $time) {
                if ((isset($second_array[$path]) && $time !== $second_array[$path])) {
                    $result['changed'][] = [$path, $second_array[$path]];
                }
            }

            $keys_differ = array_merge(array_diff_key($first_array, $second_array), array_diff_key($second_array, $first_array));

            foreach ($keys_differ as $path => $time) {
                if ( in_array($path, array_keys($first_array)) && !in_array($path, array_keys($second_array))) {
                    $result['deleted'][] = [$path,$time];
                }

                if ( !in_array($path, array_keys($first_array)) && in_array($path, array_keys($second_array))) {
                    $result['added'][] = [$path,$time];
                }
            }

            return $result;
        } catch (\Exception $e) {
            return false;
        }
    }

    /**
     * @param $file
     * @param bool $do_return_uncompressed_content
     *
     * @return false|string
     */
    private static function uncompress($file, $do_return_uncompressed_content = false)
    {
        if ( substr($file, -3) === '.gz' ) {
            $content = @gzopen($file, 'r');
            if ( false === $content ) {
                return false;
            }
            $gz_result = @gzread($content, 1024 * 1024 * 10);
            if ( !is_string($gz_result) ) {
                @gzclose($content);
                return false;
            }
            if ($do_return_uncompressed_content) {
                gzclose($content);
                return $gz_result;
            }
            $write_result = @file_put_contents(substr($file, 0, -3), $gz_result);
            gzclose($content);
            if ( false === $write_result ) {
                return false;
            }
            $file = substr($file, 0, -3);
        }

        return $file;
    }
}
