<?php
/**
* Class for intializing and displaying all admin settings
*
* @package LBRYPress
*/
defined('ABSPATH') || die(); // Exit if accessed directly

class LBRY_Admin
{
    private $options;

    /**
    * LBRY_Admin Constructor
    */
    public function __construct()
    {
        add_action('admin_menu', array($this, 'create_options_page'));
        add_action('admin_init', array($this, 'page_init'));
        add_action('admin_init', array($this, 'wallet_balance_warning'));
        add_action('admin_post_lbry_add_channel', array($this, 'add_channel'));
        add_action('admin_post_lbry_add_supports', array($this, 'add_supports'));
        add_action('admin_post_lbry_edit_channel', array($this, 'edit_channel'));
    }

    /**
    * Creates the options page in the WP admin interface
    */
    public function create_options_page()
    {

        add_menu_page(
            __( 'LBRYPress Settings', 'lbrypress' ),
            __( 'LBRYPress', 'lbrypress' ),
            'manage_options',
            LBRY_ADMIN_PAGE,
            array( $this, 'options_page_html' ),
            plugin_dir_url( LBRY_PLUGIN_FILE ) . '/admin/images/lbry-icon.png'
        );

        // Admin stylesheet enqueue
        function load_admin_stylesheet( $hook ) {

            if ( ( $hook == 'post.php' ) || ( $hook == 'post-new.php' ) || ( $_GET['page'] == 'lbrypress' ) ) {
                    wp_enqueue_style(
                        'lbry-admin',
                        plugins_url( '/admin/css/lbry-admin.css', LBRY_PLUGIN_FILE ),
                        array(),
                        LBRY_VERSION,
                        'all'
                    );
                }
        }
        add_action( 'admin_enqueue_scripts', 'load_admin_stylesheet' );

        // Admin channel sort JS enqueue
        function load_channel_sort_script() {
            if ( ( $_GET['page'] == 'lbrypress') && ( $_GET['tab'] == 'channels' ) ) {
                wp_enqueue_script(
                    'lbry-table-sort',
                    plugins_url( '/admin/js/table-sort.js', LBRY_PLUGIN_FILE ),
                    array('jquery'),
                    LBRY_VERSION,
                    true
                );
            }
        }
        add_action( 'admin_enqueue_scripts', 'load_channel_sort_script' );

        // Admin Media Upload on Edit Channel tab
        function load_channel_edit_media_scripts() {
            if ( ( $_GET['page'] == 'lbrypress' ) && ( $_GET['tab'] == 'channel-edit' ) ) {
                wp_enqueue_media();
                wp_enqueue_script(
                    'lbry-media-upload',
                    plugins_url( '/admin/js/admin-image-uploader.js', LBRY_PLUGIN_FILE ),
                    array( 'jquery' ),
                    LBRY_VERSION,
                    true
                );
            }
        }
        add_action( 'admin_enqueue_scripts', 'load_channel_edit_media_scripts' );
        
        // Admin Error Notices
        function lbry_plugin_not_configured_notice() {
          	echo "<div id='notice' class='updated fade'><p>LBRYPress plugin is not configured yet. Please do it now.</p></div>\n";
        }
        $lbry_wallet = get_option('lbry_wallet');
        if ( ! isset($lbry_wallet) ) {
            add_action( 'admin_notices', 'lbry_plugin_not_configured_notice' );
        }
        function admin_permission_check() {
          	if ( ! current_user_can( 'manage_options' ) )  {
          		wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
          	}
        }
    }

    /**
    * Returns the Options Page HTML for the plugin
    */
    public function options_page_html()
    {
          // Set class properties to be referenced in callbacks
          $this->options = get_option( LBRY_SETTINGS );
          $this->options_speech = get_option( LBRY_SPEECH_SETTINGS );
          require_once( LBRY_ABSPATH . 'templates/options-page.php' );
    }

    /**
    * Registers all settings for the plugin
    */
    public function page_init()
    {
        // Register the LBRY Setting array
        register_setting(
            'lbry_general_settings',
            LBRY_SETTINGS,
            array( $this, 'sanitize_general_settings' )
        );

        // Add Required Settings Sections
        add_settings_section(
            LBRY_SETTINGS_SECTION_GENERAL, // ID
            'General Settings', // Title
            array( $this, 'general_section_callback' ), // Callback
            LBRY_ADMIN_PAGE // Page
        );

        // Add all settings fields
        add_settings_field(
            LBRY_WALLET,
            'LBRY Wallet Address',
            array( $this, 'wallet_callback' ),
            LBRY_ADMIN_PAGE,
            LBRY_SETTINGS_SECTION_GENERAL
        );

        add_settings_field(
            'lbry_default_publish_setting',
            'Always Publish to LBRY',
            array( $this, 'lbry_always_pub_callback' ),
            LBRY_ADMIN_PAGE,
            LBRY_SETTINGS_SECTION_GENERAL
        );

        add_settings_field(
            'default_lbry_channel',
            'Default Publish Channel',
            array( $this, 'default_channel_callback' ),
            LBRY_ADMIN_PAGE,
            LBRY_SETTINGS_SECTION_GENERAL
        );

        add_settings_field(
            LBRY_LICENSE,
            'LBRY Publishing License',
            array( $this, 'license_callback' ),
            LBRY_ADMIN_PAGE,
            LBRY_SETTINGS_SECTION_GENERAL
        );

        add_settings_field(
            LBRY_LBC_PUBLISH,
            'LBC Per Publish',
            array( $this, 'lbc_per_publish_callback' ),
            LBRY_ADMIN_PAGE,
            LBRY_SETTINGS_SECTION_GENERAL
        );

        /**
         * Channel Page Settings
         * We are using a custom page so that we can use the admin-post action and retrieve the $_POST 
         * global variable to populate the cURL request to create_channel, not saving the inputs to 
         * our database.
         */
        
         
        /**
         * Speech Admin Page settings
         */

        register_setting(
            LBRY_SPEECH_SETTINGS,
            LBRY_SPEECH_SETTINGS,
            array( $this, 'sanitize_speech_settings' )
        );

        add_settings_section(
            'lbry_settings_section_speech', // ID
            'Spee.ch Channel Settings', // Title
            array( $this, 'speech_section_callback' ), // Callback
            'lbrypress-speech' // Page
        );

        add_settings_field(
            LBRY_SPEECH,
            'Spee.ch URL',
            array( $this, 'speech_callback' ),
            'lbrypress-speech',
            'lbry_settings_section_speech'
        );

        add_settings_field(
            LBRY_SPEECH_CHANNEL,
            'Spee.ch Channel',
            array( $this, 'speech_channel_callback' ),
            'lbrypress-speech',
            'lbry_settings_section_speech'
        );

        add_settings_field(
            LBRY_SPEECH_PW,
            'Spee.ch Password',
            array( $this, 'speech_pw_callback' ),
            'lbrypress-speech',
            'lbry_settings_section_speech'
        );
    }

    /**
    * Sanitizes setting input
    * // COMBAK Potentially sanitize more
    */

    public function sanitize_general_settings( $input )
    {
        $new_input = get_option( LBRY_SETTINGS ); // get saved data

        if ( isset( $input[LBRY_WALLET] ) ) {
            $new_input[LBRY_WALLET] = sanitize_text_field( $input[LBRY_WALLET] );
        }
        $new_input['lbry_default_publish_setting'] = $input['lbry_default_publish_setting'];

        if ( isset( $input['default_lbry_channel'] ) ) {
            $new_input['default_lbry_channel'] = sanitize_text_field( $input['default_lbry_channel'] );
        }
        $license_array = LBRY()->licenses;
        if ( isset( $input[LBRY_LICENSE] ) && ( in_array( $input[LBRY_LICENSE], $license_array ) ) ) {
            $new_input[LBRY_LICENSE] = sanitize_text_field( $input[LBRY_LICENSE] );
        }
        if ( isset( $input[LBRY_LBC_PUBLISH] ) ) {
            $new_input[LBRY_LBC_PUBLISH] = number_format( floatval( $input[LBRY_LBC_PUBLISH] ), 3, '.', '' );
        }
        return $new_input;
    }

      public function sanitize_speech_settings( $input )
    {
        $new_input = get_option( LBRY_SPEECH_SETTINGS );
        if ( isset( $input[LBRY_SPEECH] ) ) {
            $new_input[LBRY_SPEECH] = sanitize_text_field( $input[LBRY_SPEECH] );
        }
        if ( isset( $input[LBRY_SPEECH_CHANNEL] ) ) {
            $channel = $input[LBRY_SPEECH_CHANNEL];
            $channel = str_replace( '@', '', $channel );
            $new_input[LBRY_SPEECH_CHANNEL] = sanitize_user( $channel );
        }
        if ( isset( $input[LBRY_SPEECH_PW] ) ) {
            $input[LBRY_SPEECH_PW] = sanitize_text_field( $input[LBRY_SPEECH_PW] );
            $encrypted = $this->encrypt( $input[LBRY_SPEECH_PW] );
            $new_input[LBRY_SPEECH_PW] = $encrypted;
        } else { 
            // If we have a password and it's empty, keep original password
            if ( empty( $input[LBRY_SPEECH_PW] ) )
                $new_input[LBRY_SPEECH_PW] = get_option( LBRY_SPEECH_SETTINGS[LBRY_SPEECH_PW] );
        }
        return $new_input;
        update_option( LBRY_SPEECH_SETTINGS, $new_input );
    }

    /**
    * Section info for the General Section
    */
    public function general_section_callback()
    {
        print 'This is where you can configure how LBRYPress will distribute your content:';
    }

    /**
    * Section info for the Available Channel(s) Section
    */
    public function available_channels_callback()
    {
        // Moved to channels-page.php
    }

    /**
    * Section info for the Speech Channel Section
    */
    public function speech_section_callback()
    {
      print 'If you have a Spee.ch account, you can enter your account details here, if you don\'t already have a Spee.ch account, no need to enter anything here.';
    }

    /**
    * Prints Wallet input
    */
    public function wallet_callback()
    {
        // Get first available account address from Daemon
        $address = LBRY()->daemon->address_list();
        $address = is_array( $address ) && ! empty( $address ) ? $address[0]->address : '';
        printf(
            '<input type="text" id="'. esc_attr('%1$s') .'" name="'. esc_attr('%2$s[%1$s]') .'" value="' . esc_attr('%3$s') . '" readonly />',
            LBRY_WALLET,
            LBRY_SETTINGS,
            $address
        );
    }

    /**
     * Checkbox to default to always allow publish on LBRY
     */
    public function lbry_always_pub_callback()
    {
        $options = get_option( LBRY_SETTINGS )['lbry_default_publish_setting'];
        if ( ! isset( $options ) ) {
            $options = 0;
        }
        $checked = checked( $options, 1, false );
        printf(
        '<input type="checkbox" id="lbry_default_publish_setting" name="' . esc_attr('%2$s[%1$s]') . '" value="1" ' . esc_attr( $checked ) . '><p>Set Default to always Publish to <strong>LBRY</strong>, this can be adjusted when publishing a New Post.</p>',
        'lbry_default_publish_setting',
        LBRY_SETTINGS,
        );
    }

    /**
     * Prints select to choose a default to publish to channel
     */
    public function default_channel_callback()
    {
        $options = '';
        $channel_list = LBRY()->daemon->channel_list();

        if ( $channel_list ) {
                foreach ( $channel_list as $channel ) {
                    $selected = $this->options['default_lbry_channel'] === $channel->claim_id;

                    $options .= '<option value="' . esc_attr( $channel->claim_id ) . '"';
                    if ( $selected ) {
                        $options .= ' selected';
                    }
                    $options .= '>' . esc_html( $channel->name ) . '</option>';
                }

                printf(
                    '<select id="' . esc_attr('%1$s') . '" name="' . esc_attr('%2$s[%1$s]') . '">' . esc_html('%3$s') . '</select>',
                    'default_lbry_channel',
                    LBRY_SETTINGS,
                    $options
                );
        } else { ?>
                <p>Looks like you haven't added any channels yet, you can do that now on the <a href="<?php echo esc_url( admin_url( add_query_arg( array( 'page' => 'lbrypress', 'tab' => 'channels' ), 'options.php' ) ) ); ?>" class="">Channels Tab</a></p>
        <?php }
    }

    /**
    * Prints License input
    */
    public function license_callback()
    {
        // TODO: Maybe make this more elegant?
        $options = '';
        // Create options list, select current license
        //
        foreach ( LBRY()->licenses as $value => $name ) {
            $selected = $this->options[LBRY_LICENSE] === $value;

            $options .= '<option value="' . $value . '"';
            if ( $selected ) {
                $options .= ' selected';
            }
            $options .= '>'. $name . '</option>';
        }

        printf(
            '<select id="'.esc_attr('%1$s').'" name="'. esc_attr('%2$s[%1$s]') .'">' . esc_html('%3$s') . '</select>',
            LBRY_LICENSE,
            LBRY_SETTINGS,
            $options
        );
    }
    
    /**
    * Prints LBC per publish input
    */
    public function lbc_per_publish_callback()
    {
        printf(
            '<input type="number" id="' . esc_attr('%1$s') . '" name="' . esc_attr('%2$s[%1$s]') . '" value="' . esc_attr('%3$.3f') . '" min="0.001" step="0.001"><p>Current minimum bid <img src="' . esc_attr('%4$s ') . '" class="icon icon-lbc bid-icon-lbc"> 0.001</p>',
            LBRY_LBC_PUBLISH,
            LBRY_SETTINGS,
            $this->options[LBRY_LBC_PUBLISH],
            plugin_dir_url( LBRY_PLUGIN_FILE ) . 'admin/images/lbc.png'
        );

    }

    /**
     * Channels Page
     * Channels page uses admin.php so we are able to use the admin-post action instead of options.php
     */

    /**
    * Prints Spee.ch input
    */
    public function speech_callback()
    {
        $options = get_option( LBRY_SPEECH_SETTINGS );
        printf(
            '<input type="text" id="' . esc_attr('%1$s') . '" name="' . esc_attr('%2$s[%1$s]') . '" value="' . esc_attr('%3$s') . '" placeholder="https://your-speech-address.com">',
            LBRY_SPEECH,
            LBRY_SPEECH_SETTINGS,
            isset( $options[LBRY_SPEECH] ) ? $options[LBRY_SPEECH] : '',
        );
    }

    /**
    * Prints Spee.ch channel input
    */
    public function speech_channel_callback()
    {
        $options = get_option( LBRY_SPEECH_SETTINGS );
        printf(
            '<input type="text" id="' . esc_attr('%1$s') . '" name="' . esc_attr('%2$s[%1$s]') . '" value="@' . esc_attr('%3$s') . '" placeholder="your-speech-channel">',
            LBRY_SPEECH_CHANNEL,
            LBRY_SPEECH_SETTINGS,
            isset( $options[LBRY_SPEECH_CHANNEL] ) ? $options[LBRY_SPEECH_CHANNEL] : '',
        );
    }

    /**
    * Prints Spee.ch password input
    */
    public function speech_pw_callback()
    {
        printf(
            '<input type="password" id="' . esc_attr('%1$s') . '" name="' . esc_attr('%2$s[%1$s]') . '" placeholder="Leave empty for same password">',
            LBRY_SPEECH_PW,
            LBRY_SPEECH_SETTINGS,
        );       
    }
  
    /**
    * Handles new channel form submission
    */
    public function add_channel()
    {

        $redirect_url = admin_url( add_query_arg( array( 'page' => 'lbrypress', 'tab' => 'channels' ), 'options.php' ) );
        
        // Check that nonce
        if ( isset( $_POST['_lbrynonce'] ) && wp_verify_nonce( $_POST['_lbrynonce'], 'add_channel_nonce' ) ) {
            if ( empty( $_POST['lbry_new_channel'] ) || empty( $_POST['lbry_channel_bid_amount'] ) ) {
                LBRY()->notice->set_notice( 'error', 'Must supply both channel name and bid amount' );
            } elseif ( isset( $_POST['lbry_new_channel'] ) && isset( $_POST['lbry_channel_bid_amount'] ) ) {
                $channel = $_POST['lbry_new_channel']; // TODO: sanitize key() only allows for lowercase chars, dashes, and underscores. maybe remove to allow more characters? and use something else for better control?
                $channel = trim( $channel );
                $channel = str_replace( '@', '', $channel );
                $channel = str_replace( ' ', '-', $channel );
                $channel = str_replace( '_', '-', $channel );
                $channel_name = sanitize_user( $channel );

                $bid = $_POST['lbry_channel_bid_amount'];
                $channel_bid = number_format( floatval( $bid ), 3, '.', '' );

                // Try to add the new channel
                try { 
                    $result = LBRY()->daemon->channel_new( $channel_name, $channel_bid );
                    // Tell the user it takes some time to go through
                    LBRY()->notice->set_notice(
                        'success', 'Successfully added a new channel: @' . esc_html( $channel_name ) . '! Please allow a few minutes for the bid to process.', true );
                    
                } catch ( \Exception $e ) {
                    LBRY()->notice->set_notice( 'error', $e->getMessage(), false );
                }
            }
        } else {
            LBRY()->notice->set_notice('error', 'Security check failed' );
            die( __( 'Security check failed', 'lbrypress' ) );
        }

        wp_safe_redirect( $redirect_url );
        exit();
    }

    /**
     * Handles adding supports form submission
     */
    public function add_supports()
    {
        if ( ( $_POST['post_id'] ) && ( absint( $_POST['post_id'] ) ) ) {
            $redirect_url = admin_url( add_query_arg( array( 'post' => $_POST['post_id'], 'action' => 'edit' ), 'post.php') );
        } else {
            $redirect_url = admin_url( add_query_arg( array( 'page' => 'lbrypress', 'tab' => 'channels' ), 'options.php' ) );
        }
        if ( ( $_POST['lbry_url'] ) ? $lbry_url = urldecode($_POST['lbry_url']) : $lbry_url = $_POST['lbry_supports_add_claim_id']);
        if ( ( $_POST['supporting_channel'] ) ? $supporting_channel = $_POST['supporting_channel'] : $supporting_channel = null );
        // Check that nonce
        if ( isset( $_POST['_lbrynonce'] ) && wp_verify_nonce( $_POST['_lbrynonce'], 'add_supports_nonce' ) ) {
            if ( isset( $_POST['lbry_supports_add_claim_id'] ) && isset( $_POST['lbry_supports_add_bid_amount'] ) ) {
                $claim_id = $_POST['lbry_supports_add_claim_id']; // TODO: sanitize key() only allows for lowercase chars, dashes, and underscores. maybe remove to allow more characters? and use something else for better control?

                $claim_id = sanitize_text_field( $claim_id );
                $bid = $_POST['lbry_supports_add_bid_amount'];
                $supports_bid = number_format( floatval( $bid ), 3, '.', '' );

                // Try to add support to the claim
                try { 
                    $result = LBRY()->daemon->supports_add( $claim_id, $supports_bid, $supporting_channel, $lbry_url );
                    
                } catch ( \Exception $e ) {
                    LBRY()->notice->set_notice( 'error', $e->getMessage(), false );
                }
            }
        } else {
            LBRY()->notice->set_notice('error', 'Security check failed' );
            die( __( 'Security check failed', 'lbrypress' ) );
        }

        wp_safe_redirect( $redirect_url );
        exit();
    }

    /**
     * Handles editing an existing channel form submission
     */
    public function edit_channel()
    {
        $redirect_url = admin_url( add_query_arg( array( 'page' => 'lbrypress', 'tab' => 'channels' ), 'options.php' ) );

        $claim = $_POST['claim_id'];
        $claim_id = sanitize_text_field( $claim );
        $bid = $_POST['lbry_supports_add_bid_amount'];
        $channel_bid = number_format( floatval( $bid ), 3, '.', '' );
        $title = $_POST['lbry_edit_channel_title'];
        $channel_title = sanitize_text_field( $title );
        $description = $_POST['lbry_edit_channel_description'];
        $channel_description = sanitize_text_field( $description );
        $tags = $_POST['lbry_edit_channel_tags'];
        $channel_tags = sanitize_text_field( $tags );
        $website = $_POST['lbry_new_channel_website'];
        $channel_website = sanitize_text_field( $website );
        $email = $_POST['lbry_new_channel_email'];
        $channel_email = sanitize_text_field( $email );
        // $language_array = LBRY()->languages;
        // $primlang = $_POST['lbry_new_channel_prim_lang'];
        // $primary_language = ( ($primlang) && in_array( $primlang, $language_array ) );
        // $seclang = $_POST['lbry_new_channel_sec_lang'];
        // $secondary_language = ( ($seclang) && in_array( $seclang, $language_array ) );
        // $thumbnail = $_POST[''];
        // $thumbnail_url = wp_get_attachment_url( get_option( 'lbry_media_selector_thumbnail_id' ) )
        // $header = $_POST[''];
        // $header_url = wp_get_attachment_url( get_option( 'lbry_media_selector_header_id' ) );

        // Check that nonce
        if ( isset( $_POST['_lbrynonce'] ) && wp_verify_nonce( $_POST['_lbrynonce'], 'edit_channel_nonce' ) ) {
            $args = array(
                'claim_id'    => $claim_id,
                'bid'         => $channel_bid,
                'title'       => $channel_title,
                'description' => $channel_description,
                'tags'        => $channel_tags,
                'website_url' => $channel_website,
                'email'       => $channel_email,
                //'languages'   => array( $primary_language, $secondary_language ),
                //'thumbnail_url' => $thumbnail_url,
                //'cover_url'   => $header_url,
            );
                // Try to add support to the claim
                try { 
                    $result = LBRY()->daemon->channel_edit( $args );
                    
                } catch ( \Exception $e ) {
                    LBRY()->notice->set_notice( 'error', $e->getMessage(), false );
                }
        } else {
            LBRY()->notice->set_notice('error', 'Security check failed' );
            die( __( 'Security check failed', 'lbrypress' ) );
        }

        wp_safe_redirect( $redirect_url );
        exit();
    }

    /**
     * Checks at most once an hour to see if the wallet balance is too low
     */
    // IDEA: Check user permissions possibly
    public static function wallet_balance_warning()
    {
        // See if we've checked in the past two hours
        if ( ! get_transient( 'lbry_wallet_check' ) ) {
            $result = LBRY()->daemon->wallet_balance();
            $balance = $result->result->available;
            $site_url = get_site_url();
            if ( $balance < get_option( LBRY_SETTINGS )[LBRY_LBC_PUBLISH] * 20 ) {
                // If LBRY Balance is low, send email, but only once per day
                if ( ! get_transient( 'lbry_wallet_warning_email' ) ) {
                    $email = get_option( 'admin_email' );
                    $subject = 'Your LBRYPress Wallet Balance is Low!';
                    $message = 'Your LBRY Wallet for your WordPress installation at ' . esc_html_e( $site_url ) . ' is running very low.\r\n\r\nYou currently have ' . esc_html_e( $balance ) . ' LBC left in your wallet. In order to keep publishing to the LBRY network, please add some LBC to your account.';
                    wp_mail( $email, $subject, $message );
                    set_transient( 'lbry_wallet_warning_email', true, DAY_IN_SECONDS );
                }
            }
            set_transient( 'lbry_wallet_check', true, 2 * HOUR_IN_SECONDS );
        }
    }

    private function encrypt($plaintext)
    {
        $ivlen = openssl_cipher_iv_length($cipher="AES-256-CTR");
        $iv = openssl_random_pseudo_bytes($ivlen);
        $ciphertext_raw = openssl_encrypt($plaintext, $cipher, wp_salt(), $options=OPENSSL_RAW_DATA, $iv);
        $hmac = hash_hmac('sha256', $ciphertext_raw, wp_salt(), $as_binary=true);
        return base64_encode($iv.$hmac.$ciphertext_raw);
    }

    private function decrypt($ciphertext)
    {
        $c = base64_decode($ciphertext);
        $ivlen = openssl_cipher_iv_length($cipher="AES-256-CTR");
        $iv = substr($c, 0, $ivlen);
        $hmac = substr($c, $ivlen, $sha2len=32);
        $ciphertext_raw = substr($c, $ivlen+$sha2len);
        $original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, wp_salt(), $options=OPENSSL_RAW_DATA, $iv);
        $calcmac = hash_hmac('sha256', $ciphertext_raw, wp_salt(), $as_binary=true);
        if (hash_equals($hmac, $calcmac)) {//PHP 5.6+ timing attack safe comparison
            return $original_plaintext;
        }

        return false;
    }

    public function get_speech_pw()
    {
        $ciphertext = get_option(LBRY_SETTINGS)[LBRY_SPEECH_PW];
        if (empty($ciphertext)) {
            return false;
        }

        return $this->decrypt($ciphertext);
    }
}