<?php

namespace EstatebudConnect\Core;

if (!defined('WPINC')) {
    die();
}

use EstatebudConnect\Plugin;
use EstatebudConnect\Configs\App;
use EstatebudConnect\Core\Log;
use EstatebudConnect\Core\Cache;
use EstatebudConnect\Models\Settings;
use EstatebudConnect\Traits\Singleton;

class Update
{
    protected $host = 'repo.estatebud.com';
    protected $cache_key = 'update';
    protected $latest_release = null;

    use Singleton;

    public function __construct()
    {
        add_filter('plugins_api', [$this, 'wp_plugin_info'], 20, 3);
        add_filter('site_transient_update_plugins', [$this, 'wp_plugin_update']);
        add_action('upgrader_process_complete', [$this, 'wp_plugin_updated'], 10, 2);
    }

    public function wp_plugin_info($res, $action, $args)
    {
        if ($action !== 'plugin_information') {
            return $res;
        }

        if ($args->slug !== App::SLUG) {
            return $res;
        }

        Log::write('Update: wp_plugin_info() calling get_latest_release()');

        $this->get_latest_release();
        
        if (!$this->latest_release) {
            return $res;
        }

        $data = new \stdClass();
        $data->name = $this->latest_release->name;
        $data->slug = $this->latest_release->slug;
        $data->version = $this->latest_release->version;
        $data->tested = $this->latest_release->tested;
        $data->requires = $this->latest_release->requires;
        $data->author = $this->latest_release->author;
        $data->author_profile = $this->latest_release->author_profile;
        $data->download_link = $this->latest_release->download_url;
        $data->trunk = $this->latest_release->download_url;
        $data->requires_php = $this->latest_release->requires_php;
        $data->last_updated = $this->latest_release->last_updated;
        $data->sections = [
            'description' => $this->latest_release->sections->description,
            'installation' => $this->latest_release->sections->installation,
            'changelog' => $this->latest_release->sections->changelog
        ];

        if (isset($this->latest_release->banners)) {
            $data->banners = [
                'low' => $this->latest_release->banners->low,
                'high' => $this->latest_release->banners->high
            ];
        }

        Log::write('Update: wp_plugin_info() returned plugin data', (array) $data);

        return $data;
    }

    public function wp_plugin_update($data)
    {
        if (empty($data->checked) || Plugin::is_uninstalling()) {
            return $data;
        }

        if ($result = $this->update()) {
            $data->response[$result->plugin] = $result;
        }

        Log::write('Update: wp_plugin_update() returned update data', (array) $data);

        return $data;
    }

    public function wp_plugin_updated($upgrader, $options)
    {
        $action = $options['action'] ?? '';
        $type = $options['type'] ?? '';

        Log::write('Update: wp_plugin_updated()', $options);

        if ($action === 'update' && $type === 'plugin') {
            Cache::instance()->delete($this->cache_key);
            Log::write('Update: wp_plugin_updated() cleared cache');
        }
    }

    public function get_latest_release()
    {
        if (!is_null($this->latest_release)) {
            Log::write('get_latest_release() returning latest release as it exists');
            return $this->latest_release;
        }

        $cache = Cache::instance()->get($this->cache_key);
        
        if ($cache) {
            Log::write('get_latest_release() returning cache as it exists');
            $this->latest_release = $cache;
        } else {
            $settings_data = Settings::instance()->get_data();

            $url = sprintf("https://%s/%s/release.json?min_type=%s", $this->host, App::SLUG, $settings_data['release_tier']);
            $args = [
                'headers' => [
                    'Accept' => 'application/json',
                    'Content-Type' => 'application/json',
                ],
                'user-agent' => sprintf('WordPress/%s; %s', App::NAME, App::VERSION),
                'method' => 'GET',
                'timeout' => 5,
            ];

            Log::write('Update: get_latest_release() downloading release info from ' . $url);

            $response = wp_remote_request($url, $args);
            $responseHttpCode = wp_remote_retrieve_response_code($response);
            $data = wp_remote_retrieve_body($response);

            if ($responseHttpCode == 200) {
                $this->latest_release = $this->parse($data);
            } else {
                $this->latest_release = false;
            }

            Log::write('Update: get_latest_release() obtained:', (array) $this->latest_release);

            Cache::instance()->set($this->cache_key, $this->latest_release, Cache::TTL_5M);
        }

        return $this->latest_release;
    }

    public function update()
    {
        if (is_null($this->latest_release)) {
            $this->get_latest_release();
        }

        if ($this->verify_release() !== true) {
            return false;
        }

        $data = new \stdClass();
        $data->slug = App::SLUG;
        $data->plugin = sprintf("%s/%s.php", App::SLUG, App::SLUG);
        $data->new_version = $this->latest_release->version;
        $data->tested = $this->latest_release->tested;
        $data->package = $this->latest_release->download_url;

        return $data;
    }

    public function verify_release()
    {
        if (Plugin::has_build_tools()) {
            $error = "Development mode active";
        } else if (!$this->latest_release) {
            $error = "Release data not fetched";
        } else if (!$this->is_update_available()) {
            $error = "New release not found";
        } else if (!version_compare($release->requires, get_bloginfo('version'), '<=')) {
            $error = sprintf("Requires minimum WordPress version %s", $release->requires);
        } else if (!version_compare($release->requires_php, PHP_VERSION, '<')) {
            $error = sprintf("Requires minimum PHP version %s", $release->requires_php);
        }

        Log::write('Update: verify_release() release result', [ 'result' => $error ?? 'Release verified' ]);

        return $error ?? true;
    }

    public function is_update_available()
    {
        Log::write('Update: is_update_available()');

        if (is_null($this->latest_release)) {
            Log::write('Update: is_update_available() calling get_latest_release()');
            $this->get_latest_release();
        }

        return version_compare(App::VERSION, $this->latest_release->version, '<');
    }

    private function parse($json)
    {
        return json_decode($json);
    }
}