<?php

namespace Sowmedia\Repo\Updaters;

use Sowmedia\Repo\API\v1\Endpoint;
use Sowmedia\Repo\API\v1\Exception;
use Sowmedia\Repo\Frontend\LicensePage;
use Sowmedia\Repo\Frontend\AdminNotices;

/**
 * A modernized updater, basically a forked LegacyUpdater to work with the new repo API
 * @author Sowmedia.nl (Jaco)
 */
class GenesisUpdater {
    private $package_type;

    /**
     * Path Slug (directory/file.ext)
     * @var string
     */
    private $index_slug;

    /**
     * Plugin or Theme slug (directory)
     * @var string
     */
    private $slug;

    private $name;
    private $version;

    private $is_free;

    function __construct($pass_through, $is_free = false){
        $this->parse_pass_through($pass_through);
        $this->is_free = $is_free;

        $this->add_hooks();
    }

    /**
     * Parse the payload
     */
    private function parse_pass_through($pass_through){
        if(is_string($pass_through)){
            $this->package_type = "plugin";
            $this->index_slug = $pass_through;
            list($t1, $t2) = explode("/", $pass_through);
            $this->slug = $t1;
            if(is_admin()){
                $data = get_file_data(trailingslashit(wp_normalize_path(WP_PLUGIN_DIR)) . $this->index_slug, array("Name" => "Plugin Name", "Version" => "Version"));
                $this->name = $data["Name"];
                $this->version = $data["Version"];
            }
        }elseif(is_object($pass_through)){
            $this->package_type = "theme";
            $this->name = $pass_through->get("Name");
            // Provide support for the old buggy situation
            if(strpos($pass_through->get("Name"), "/")) {
                list($t1, $t2) = explode("/", $pass_through->get("Name"));
                $this->slug = $this->index_slug = explode("/", $pass_through->get("Name"));
                $this->version = $pass_through->get("Version");
            } else $this->slug = $this->index_slug = $pass_through->stylesheet;
        }
    }

    private function add_hooks(){
        // Add hooks based on package type
        if($this->package_type == "plugin"){
            add_filter("pre_set_site_transient_update_plugins", array($this, "update_check"), pow(10, 10), 1);
            add_filter("plugins_api", array($this, "get_info"), pow(10, 10), 3);
        }elseif($this->package_type == "theme"){
            add_filter("pre_set_site_transient_update_themes", array($this, "update_check"), pow(10, 10), 1);
            add_filter("themes_api", array($this, "get_info"), pow(10, 10), 3);
        }

        if(is_admin()){
            add_action("admin_init", array($this, "add_license_input"));
            add_action("admin_init", array($this, "show_license_errors"));
        }

        AdminNotices::setup();
        LicensePage::setup();
    }

    private function get_license_key(){
        return get_option($this->slug . "-license-key");
    }

    function save_license_key($key){
        // Update license check data
        $this->set_license_check_data(true);
        update_option($this->slug . "-license-key", $key);
    }

    function check_license_key($key){
        if($this->is_free)
            return array(true, null);

        $repo = new Endpoint($key);
        try{
            $info = $repo->get_license_info();
        }catch(Exception $e){
            return array(false, $e->getMessage());
        }catch(\Exception $e){
            return array(false, "Cannot contact the Sowmedia Repo");
        }
        if($info["package"] != "all" && $info["package"] != $this->slug)
            return array(false, "This license is not valid for this package");
        return array(true, null);
    }

    function update_check($transient){
        if(!isset($transient->response) || !is_array($transient->response))
            $transient->response = array();
        if(!isset($transient->no_update) || !is_array($transient->no_update))
            $transient->no_update = array();

        // Comment this when debugging
        if(isset($transient->response[$this->index_slug]) || isset($transient->no_update[$this->index_slug]))
            return $transient;

        if(isset($transient->checked[$this->index_slug]))
            $this->version = $transient->checked[$this->index_slug];

        $repo = new Endpoint($this->get_license_key());
        try{
            $info = $repo->get_package_info($this->slug);
        }catch(\Exception $e){
            return $transient;
        }

        $new_version = $info["latest_version"]["version"];

        $wp_format = array(
            "slug"          => $this->slug,
            "new_version"   => $new_version,
            "package"       => $repo->get_download_url($this->slug, $new_version, true)
        );
        // Plugins expect a std object
        $wp_format = $this->package_type == "plugin" ? (object)$wp_format : $wp_format;

        if(version_compare($this->version, $new_version, "<"))
            $transient->response[$this->index_slug] = $wp_format;
        else
            $transient->no_update[$this->index_slug] = $wp_format;

        return $transient;
    }

    function get_info($result, $action, $args){
        if(!isset($args->slug) || $args->slug != $this->slug || $action != "plugin_information")
            return $result;

        $repo = new Endpoint($this->get_license_key());
        try{
            $package = $repo->get_package_info($this->slug);
            $latest = $repo->get_version_info($this->slug, "latest");
        }catch(\Exception $e){
            return $result;
        }

        $versions = array();
        foreach($package["versions"] as $version){
            $versions[$version["version"]] = $repo->get_download_url($package["slug"], $version["version"], true);
        }

        return (object)array(
            "slug"              => $package["slug"],
            "name"              => $package["name"],
            "version"           => $latest["version"],
            "author"            => $package["author"],
            "author_profile"    => "http://www.sowmedia.nl",
            "contributors"      => array(),
            "last_updated"      => $latest["released_at"],
            "homepage"          => "http://www.sowmedia.nl",
            "sections"      => array(
                "description"   => $package["description"],
                "changelog"     => $latest["changelog"]
            ),
            "download_link"     => $repo->get_download_url($package["slug"], $latest["version"], true),
            "screenshots"       => array(),
            "tags"              => array(),
            "versions"          => $versions
        );
    }

    function add_license_input(){
        LicensePage::get()->add_key_input(array(
            "name"      => $this->name,
            "key"       => $this->get_license_key(),
            "do_check"  => array($this, "check_license_key"),
            "do_save"   => array($this, "save_license_key"),
            "enabled"   => !$this->is_free
        ));
    }

    function show_license_errors(){
        if(LicensePage::get()->shown())
            return;

        list($ok, $message) = $this->check_license();
        if($ok)
            return;

        if($this->get_license_key() != null && strlen(trim($this->get_license_key())) > 0){
            AdminNotices::get()->add_notice(sprintf(
                "Your license for <b>%s</b> is invalid: <i>%s</i>. Please <a href='%s'>update your license key</a>.",
                $this->name, htmlspecialchars($message), LicensePage::get()->get_url()
            ), "error");
        } elseif(!$this->is_free) {
            AdminNotices::get()->add_notice(sprintf("Please <a href='%s'>enter a license key</a> for <b>%s</b>.",
                LicensePage::get()->get_url(), $this->name), "error");
        }
    }

    private function check_license($force = false){
        list($ok, $last_check, $message) = $this->get_license_check_data();
        if(!$force && (time() - $last_check <= 7200))
            return array($ok, $message);

        list($ok, $message) = $this->check_license_key($this->get_license_key());
        $this->set_license_check_data($ok, null, $message);
        return array($ok, $message);
    }

    private function set_license_check_data($ok, $time = null, $message = null){
        if($time == null)
            $time = time();
        update_option($this->slug . "-license-check-ok", $ok);
        update_option($this->slug . "-license-check-timestamp", $time);
        update_option($this->slug . "-license-check-message", $message);
    }

    private function get_license_check_data(){
        return array(
            get_option($this->slug . "-license-check-ok"),
            get_option($this->slug . "-license-check-timestamp"),
            get_option($this->slug . "-license-check-message")
        );
    }
}
