<?php

defined( 'ABSPATH' ) || exit;

class Play_Notification {

    protected static $_instance = null;
    private $db_table = 'play_block_notifications';

    public static function instance() {
        if ( is_null( self::$_instance ) ) {
            self::$_instance = new self();
        }

        return self::$_instance;
    }

    /**
     * Initialize the actions.
     */
    public function __construct() {
        if( apply_filters('play_notification', true) ){
            $this->init();
        }
    }

    public function init() {
        register_activation_hook( plugin_dir_path( dirname( __FILE__ ) ).'play-block.php', array( $this, 'on_activate') );

        add_action( 'play_block_like_after_save', array($this, 'like_notification'), 10, 2 );
        add_action( 'play_block_follow_after_save', array($this, 'follow_notification'), 10, 2 );
        add_action( 'play_block_upload_after_insert', array($this, 'upload_notification'), 10, 2 );
        add_action( 'play_block_download_after_save', array($this, 'download_notification'), 10, 2 );
        add_action( 'play_block_playlist_after_insert', array($this, 'playlist_notification'), 10, 2 );
        add_action( 'play_block_playlist_after_save', array($this, 'playlist_notification'), 10, 2 );
        add_action( 'comment_post', array($this, 'comment_notification'), 10, 3 );

        add_filter( 'play_notification_actions', array($this, 'get_notification_actions') );

        add_filter( 'loop_block_sql_query', array($this, 'loop_block_sql_query'), 10, 1 );
        add_filter( 'loop_locate_template', array($this, 'loop_template'), 10, 3 );
        add_action( 'loop_block_after_render', array($this, 'loop_block_after_render'), 10, 2 );

        do_action( 'play_block_notification_init', $this );
    }

    public function like_notification( $user_id, $post_id ) {
        if( apply_filters('play_block_like_notification', true) === false ) return;
        $author_id = get_post_field( 'post_author', $post_id );
        $data = array(
            'user_id' => (int) $author_id,
            'item_id' => (int) $post_id,
            'notifier_id' => (int) $user_id,
            'action' => 'like',
        );
        $data = apply_filters('play_block_like_notification_data', $data);
        $this->save( $data );
    }

    public function follow_notification( $user_id, $notifier_id ) {
        if( apply_filters('play_block_follow_notification', true) === false ) return;
        $data = array(
            'user_id' => (int) $user_id,
            'notifier_id' => (int) $notifier_id,
            'action' => 'follow',
        );
        $data = apply_filters('play_block_follow_notification_data', $data);
        $this->save( $data );
    }

    public function upload_notification( $user_id, $post_id ) {
        if( apply_filters('play_block_upload_notification', false) === false ) return;
        if( get_post_status($post_id) !== 'publish' ) return;
        $data = array(
            'item_id' => (int) $post_id,
            'notifier_id' => (int) $user_id,
            'action' => 'upload',
        );
        // get followers
        $follower = apply_filters( 'user_follow', $user_id );
        foreach ( $follower as $k => $v ) {
            $data['user_id'] = (int) $v;
            $data = apply_filters('play_block_upload_notification_data', $data);
            $this->save( $data );
        }
    }

    public function playlist_notification( $user_id, $post_id ) {
        if( apply_filters('play_block_playlist_notification', true) === false ) return;
        if( get_post_status($post_id) !== 'publish' ) return;
        $data = array(
            'item_related_id' => (int) $post_id,
            'notifier_id' => (int) $user_id,
            'action' => 'playlist',
        );
        
        $posts = get_post_meta( $post_id, 'post', true);
        $posts = explode(',', $posts);
        foreach ( $posts as $k => $v ) {
            $data['user_id'] = (int) get_post_field( 'post_author', $v );
            $data['item_id'] = (int) $v;
            $data = apply_filters('play_block_playlist_notification_data', $data);
            $this->save( $data );
        }
    }

    public function download_notification( $user_id, $post_id ) {
        if( apply_filters('play_block_download_notification', true) === false ) return;
        $author_id = get_post_field( 'post_author', $post_id );
        $data = array(
            'user_id' => (int) $author_id,
            'item_id' => (int) $post_id,
            'notifier_id' => (int) $user_id,
            'action' => 'download',
        );
        $data = apply_filters('play_block_download_notification_data', $data);
        $this->save( $data );
    }

    public function comment_notification( $comment_id, $comment_approved, $commentdata ) {
        if( apply_filters('play_block_comment_notification', true) === false ) return;

        if($comment_approved == 0 || !isset($commentdata['user_id']) ){
            return;
        }

        $data = array(
            'user_id' => (int) get_post_field( 'post_author', $commentdata['comment_post_ID'] ),
            'item_id' => (int) $commentdata['comment_post_ID'],
            'notifier_id' => (int) $commentdata['user_id'],
            'action' => 'comment',
            'description' => $commentdata['comment_content']
        );

        $data = apply_filters('play_block_comment_notification_data', $data);
        $this->save( $data, true );
    }
    
    public function save( $args = array(), $repeat = false ) {
        $retval = false;

        do_action_ref_array( 'play_block_notification_before_save', array( &$this ) );

        $data = $this->parse_args( $args, array(
            'user_id'           => 0,
            'notifier_id'       => 0,
            'item_id'           => 0,
            'item_related_id'   => 0,
            'action'            => '',
            'description'       => '',
            'date_notified'     => $this->current_time(),
            'status'            => 0,
        ));

        $data_format = array( '%d', '%d', '%d', '%d', '%s', '%s', '%s', '%d' );

        if((int)$data['user_id'] == (int)$data['notifier_id']){
            return;
        }

        // Update.
        if ( ! empty( $args['id'] ) ) {
            $result = $this->_update( $data, array( 'ID' => $args['id'] ), $data_format, array( '%d' ) );
        // Insert.
        } else {
            $d = $data;
            array_splice($d, count($d) - 3, 3);
            $existing = $this->get($d);
            if ( ! empty( $existing->results ) && $repeat === false ) {
                return false;
            }
            $result = $this->_insert( $data, $data_format );
        }

        if ( ! empty( $result ) && ! is_wp_error( $result ) ) {
            global $wpdb;
            $retval   = $wpdb->insert_id;
        }

        do_action_ref_array( 'play_block_notification_after_save', array( &$this ) );

        return $retval;
    }

    public function update( $update_args = array(), $where_args = array() ) {
        $update = $this->get_query_clauses( $update_args );
        $where  = $this->get_query_clauses( $where_args  );
        do_action( 'play_block_notification_before_update', $update_args, $where_args );

        return $this->_update(
            $update['data'],
            $where['data'],
            $update['format'],
            $where['format']
        );
    }

    public function delete( $args = array() ) {
        $where = $this->get_query_clauses( $args );
        do_action( 'play_block_notification_before_delete', $args );

        return $this->_delete( $where['data'], $where['format'] );
    }

    public function loop_block_sql_query( $args = array() ) {
        if( is_string( $args['type'] ) && strpos( strtolower($args['type']), 'notification' ) ){
            return $this->get( $args );
        }
    }

    public function loop_template( $template, $template_name, $template_path ){
        if( $template_name == 'notification' ){
            $tpl = plugin_dir_path( dirname( __FILE__ ) ) . 'templates/user/notification.php';
            if ( file_exists( $tpl ) ) {
                $template = $tpl;
            }
        }
        return $template;
    }

    public function loop_block_after_render( $args, $query ) {
        // update to status 1
        if( is_string( $args['type'] ) && strpos( strtolower($args['type']), 'notification' ) ){
            if( $args['pager'] !== '' && $query->results ){
                $ids = array_map(function($item){
                    return $item->id;
                }, array_filter($query->results, 
                    function($item) {
                        return $item->status == 0;
                    }
                ));
                if( !empty($ids) ){
                    $this->_update_in(
                        array(
                            'status' => 1
                        ),
                        array(
                            'id' => $ids
                        ),
                        array('%d')
                    );
                }
            }
        }
    }

    public function get( $args = array() ) {
        global $wpdb;
        do_action( 'play_block_notification_before_get', $args );

        if( isset( $args['action'] ) && in_array( $args['action'], array('ajax_loop_more', 'notification') ) ){
            unset( $args['action'] );
        }

        $arr = wp_parse_args( $args, array(
            'id'                => 0,
            'user_id'           => 0,
            'item_id'           => 0,
            'item_related_id'   => 0,
            'action'            => '',
            'status'            => 0,
            'search_terms'      => '',
            'orderby'           => 'date_notified',
            'order'             => 'DESC',
            'paged'             => 1,
            'pages'             => 20
        ) );

        // SELECT.
        $select_sql = "SELECT *";

        // SELECT count.
        $select_count_sql = "SELECT COUNT(*)";

        // FROM.
        $from_sql   = "FROM {$wpdb->prefix}{$this->db_table}";

        // WHERE.
        $where_sql  = $this->get_where_sql( $arr );

        // ORDER BY.
        $order_sql  = $this->get_order_by_sql( array(
            'orderby'   => $arr['orderby'],
            'order'     => $arr['order']
        ) );

        // LIMIT %d, %d.
        $pag_sql    = $this->get_paged_sql( array(
            'paged'  => $arr['paged'],
            'pages'  => $arr['pages']
        ) );

        $sql = "{$select_count_sql} {$from_sql} {$where_sql} {$order_sql}";
        $num = (int) $wpdb->get_var( $sql );
        
        $sql = "{$select_sql} {$from_sql} {$where_sql} {$order_sql} {$pag_sql}";
        $results = $wpdb->get_results( $sql );

        $query = new stdClass();
        $query->max_num_pages = ceil( $num / $arr['pages'] ); $max_num_pages  ;
        $query->results = $results;

        return $query;
    }

    public function get_notification_actions( ) {
        $actions = array(
            'like'     => play_get_text( 'notification-like' ),
            'follow'   => play_get_text( 'notification-follow' ),
            'comment'  => play_get_text( 'notification-comment' ),
            'upload'   => play_get_text( 'notification-upload' ),
            'download' => play_get_text( 'notification-download' ),
            'playlist' => play_get_text( 'notification-playlist' ),
        );
        return apply_filters('play_notification_actions_filter', $actions);
    }

    private function _insert( $data = array(), $data_format = array() ) {
        global $wpdb;
        return $wpdb->insert( $wpdb->prefix . $this->db_table, $data, $data_format );
    }

    private function _update( $data = array(), $where = array(), $data_format = array(), $where_format = array() ) {
        global $wpdb;
        return $wpdb->update( $wpdb->prefix . $this->db_table, $data, $where, $data_format, $where_format );
    }

    private function _delete( $where = array(), $where_format = array() ) {
        global $wpdb;
        return $wpdb->delete( $wpdb->prefix . $this->db_table, $where, $where_format );
    }

    private function _update_in( $data, $where, $format = NULL ) {
        global $wpdb;

        $update_sql = "UPDATE {$wpdb->prefix}{$this->db_table} SET";

        $format     = array_values( (array) $format );
        $fields     = array();
        $i          = 0;
        foreach( (array) $data as $field => $value ) {
            $f         = isset( $format[$i] ) && in_array( $format[$i], array( '%s', '%d' ), TRUE ) ? $format[$i] : '%s';
            $fields[]  = esc_sql( $field ) . " = " . $wpdb->prepare( $f, $value );
            $i++;
        }

        $fields_sql = implode( ', ', $fields );
        $where_sql  = $this->get_where_sql( $where );

        $sql = "{$update_sql} {$fields_sql} {$where_sql}";

        return $wpdb->query( $sql );
    }

    private function get_query_clauses( $args = array() ) {
        $where_clauses = array(
            'data'   => array(),
            'format' => array(),
        );

        if ( ! empty( $args['id'] ) ) {
            $where_clauses['data']['id'] = absint( $args['id'] );
            $where_clauses['format'][] = '%d';
        }

        if ( ! empty( $args['user_id'] ) ) {
            $where_clauses['data']['user_id'] = absint( $args['user_id'] );
            $where_clauses['format'][] = '%d';
        }

        if ( ! empty( $args['notifier_id'] ) ) {
            $where_clauses['data']['notifier_id'] = absint( $args['notifier_id'] );
            $where_clauses['format'][] = '%d';
        }

        if ( ! empty( $args['item_id'] ) ) {
            $where_clauses['data']['item_id'] = absint( $args['item_id'] );
            $where_clauses['format'][] = '%d';
        }

        if ( ! empty( $args['item_related_id'] ) ) {
            $where_clauses['data']['item_related_id'] = absint( $args['item_related_id'] );
            $where_clauses['format'][] = '%d';
        }

        if ( ! empty( $args['action'] ) ) {
            $where_clauses['data']['action'] = $args['action'];
            $where_clauses['format'][] = '%s';
        }

        if ( isset( $args['status'] ) ) {
            $where_clauses['data']['status'] = ! empty( $args['status'] ) ? $args['status'] : 0;
            $where_clauses['format'][] = '%d';
        }

        return $where_clauses;
    }

    public function get_where_sql( $args = array() ) {
        global $wpdb;

        $wheres = array();
        $where  = '';

        if ( ! empty( $args['id'] ) ) {
            $id_in = implode( ',', wp_parse_id_list( $args['id'] ) );
            $wheres['id'] = "id IN ({$id_in})";
        }

        if ( ! empty( $args['user_id'] ) ) {
            $user_id_in = implode( ',', wp_parse_id_list( $args['user_id'] ) );
            $wheres['user_id'] = "user_id IN ({$user_id_in})";
        }

        if ( ! empty( $args['notifier_id'] ) ) {
            $notifier_id_in = implode( ',', wp_parse_id_list( $args['notifier_id'] ) );
            $wheres['notifier_id'] = "notifier_id IN ({$notifier_id_in})";
        }

        if ( ! empty( $args['item_id'] ) ) {
            $item_id_in = implode( ',', wp_parse_id_list( $args['item_id'] ) );
            $wheres['item_id'] = "item_id IN ({$item_id_in})";
        }

        if ( ! empty( $args['item_related_id'] ) ) {
            $item_related_id_in = implode( ',', wp_parse_id_list( $args['item_related_id'] ) );
            $wheres['item_related_id'] = "item_related_id IN ({$item_related_id_in})";
        }

        if ( ! empty( $args['action'] ) ) {
            if ( ! is_array( $args['action'] ) ) {
                $action = explode( ',', $args['action'] );
            } else {
                $action = $args['action'];
            }

            $t_clean = array();
            foreach ( $action as $t ) {
                $t_clean[] = $wpdb->prepare( '%s', $t );
            }

            $t_in = implode( ',', $t_clean );
            $wheres['action'] = "action IN ({$t_in})";
        }

        if ( ! empty( $args['status'] ) ) {
            $status_in = implode( ',', wp_parse_id_list( $args['status'] ) );
            $wheres['status'] = "status IN ({$status_in})";
        }

        if ( ! empty( $wheres ) ) {
            $where = 'WHERE ' . implode( ' AND ', $wheres );
        }

        return $where;
    }

    public function get_order_by_sql( $args = array() ) {
        $conditions = array();
        $retval     = '';
        if ( ! empty( $args['orderby'] ) ) {
            $orderby               = implode( ', ', (array) $args['orderby'] );
            $conditions['orderby'] = "{$orderby}";
        }
        if ( ! empty( $args['order'] ) && in_array( $args['order'], array( 'ASC', 'DESC' ) ) ) {
            $order               = $args['order'];
            $conditions['order'] = "{$order}";
        }
        if ( ! empty( $conditions ) ) {
            $retval = 'ORDER BY ' . implode( ' ', $conditions );
        }
        return $retval;
    }

    public function get_paged_sql( $args = array() ) {
        global $wpdb;
        $retval = '';
        if ( ! empty( $args['paged'] ) && ! empty( $args['pages'] ) ) {
            $paged  = absint( $args['paged'] );
            $pages  = absint( $args['pages'] );
            $offset = $pages * ( $paged - 1 );
            $retval = $wpdb->prepare( "LIMIT %d, %d", $offset, $pages );
        }
        return $retval;
    }

    public function current_time( $gmt = true, $type = 'mysql' ) {
        return apply_filters( 'play_block_current_time', current_time( $type, $gmt ) );
    }

    public function parse_args( $args, $defaults = array() ) {
        $result = $defaults;
        foreach ( $result as $k => $v ) {
            if(isset( $args[$k] )){
                $result[ $k ] = $args[$k];
            }
        }
        return $result;
    }

    public function on_activate( $network_wide ) {
        global $wpdb;

        if ( is_multisite() && $network_wide ) {
            // Get all blogs in the network and activate plugin on each one
            $blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
            foreach ( $blog_ids as $blog_id ) {
                switch_to_blog( $blog_id );
                $this->create_table();
                restore_current_blog();
            }
        } else {
            $this->create_table();
        }
    }
    
    public function create_table() {
        global $wpdb;
        require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );

        $table_name = $wpdb->prefix . $this->db_table;
        $charset_collate = $wpdb->get_charset_collate();

        $sql = "CREATE TABLE IF NOT EXISTS $table_name (
        `id` int(11) NOT NULL AUTO_INCREMENT,
        `user_id` int(11) NOT NULL,
        `notifier_id` int(11) NOT NULL,
        `item_id` int(11) NOT NULL,
        `item_related_id` int(11) NOT NULL,
        `action` varchar(255) NOT NULL,
        `description` longtext DEFAULT NULL,
        `date_notified` datetime NOT NULL,
        `status` tinyint(1) NOT NULL,
        PRIMARY KEY (`id`)) $charset_collate;";
        dbDelta( $sql );
    }
}

Play_Notification::instance();
