<?php

defined( 'ABSPATH' ) || exit;

class Play_Like {

    private $site_id;
    private $meta_key;
    private $meta_key_like = 'like';
    private $meta_key_dislike = 'dislike';
    private $type = 'posts';

    protected static $_instance = null;

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

        return self::$_instance;
    }

    public function __construct() {
        $this->site_id  = get_current_blog_id();
        $this->meta_key = $this->meta_key_like;

        add_action( 'the_like_button', array( $this, 'the_like_button' ), 10, 2 );
        add_action( 'the_dislike_button', array( $this, 'the_dislike_button' ), 10, 2 );

        add_filter( 'user_likes', array( $this, 'get_user_likes_array' ) );

        add_action( 'play_like', array( $this, 'like' ) );
        add_action( 'play_dislike', array( $this, 'like' ) );

        add_filter( 'comment_reply_link', array( $this, 'comment_like_dislike' ), 10, 4 );
        add_action( 'woocommerce_review_after_comment_text', array( $this, 'comment_woo_like_dislike' ) );

        do_action( 'play_block_like_init', $this );
    }

    /**
     * like
     */
    public function like( $request ) {
        $id             = (int) $request[ 'id' ];
        $this->type     = isset( $request[ 'type' ] ) ? sanitize_text_field( $request[ 'type' ] ) : 'posts';
        $this->meta_key = isset( $request[ 'action' ] ) ? sanitize_text_field( $request[ 'action' ] ) : 'like';
        
        if ( ! is_user_logged_in() ) {
            Play_Utils::instance()->response( array(
                'error' => 'You need to login',
                'url'   => wp_login_url( get_permalink( $id ) )
            ) );
        }
        if ( Play_Utils::instance()->validate_nonce() ) {
            $user_id = get_current_user_id();
            if ( $this->is_user_like( $user_id, $id ) ) {
                $this->remove_like( $user_id, $id );
                $status = 0;
                do_action( 'play_block_dislike_after_save', $user_id, $id );
            } else {
                $this->add_like( $user_id, $id );
                $status = 1;
                do_action( 'play_block_like_after_save', $user_id, $id );
            }
            Play_Utils::instance()->response( array(
                'status' => $status,
                'type'   => $this->type,
                'count'  => Play_Utils::instance()->format_count( $this->get_like_count( $id ) )
            ) );
        }
    }

    /**
     * Display like button
     */
    public function the_like_button( $id, $type = 'posts', $echo = true ) {
        $this->type     = $type;
        $this->meta_key = $this->meta_key_like;
        $button         = $this->get_button( $id );
        if ( $echo ) {
            echo $button;

            return;
        }

        return $button;
    }

    /**
     * Display dislike button
     */
    public function the_dislike_button( $id, $type = 'posts', $echo = true ) {
        $this->type     = $type;
        $this->meta_key = $this->meta_key_dislike;
        $button         = $this->get_button( $id );
        if ( $echo ) {
            echo $button;

            return;
        }

        return $button;
    }

    public function comment_like_dislike( $link, $args, $comment, $post ) {
        $el = sprintf( '<div class="comment-toolbar">%s</div>', $this->the_like_button( $comment->comment_ID, 'comments', false ) . $this->the_dislike_button( $comment->comment_ID, 'comments', false ) . $link );

        return apply_filters( 'play_comment_reply_link', $el, $link, $comment, $post );
    }

    public function comment_woo_like_dislike( $comment ) {
        $el = sprintf( '<div class="comment-toolbar">%s</div>', $this->the_like_button( $comment->comment_ID, 'comments', false ) . $this->the_dislike_button( $comment->comment_ID, 'comments', false ) );
        echo apply_filters( 'play_comment_woo_reply_link', $el, $comment );
    }

    /**
     * Get like button
     */
    public function get_button( $id ) {
        $count = Play_Utils::instance()->format_count( $this->get_like_count( $id ) );
        $liked = $this->is_user_like( get_current_user_id(), $id );

        $ret = sprintf( '<button data-id="%1$s" data-action="%2$s" data-type="%3$s" class="btn-like %4$s">%5$s<span class="count">%6$s</span></button>', esc_attr( $id ), esc_attr( $this->meta_key ), esc_attr( $this->type ), $liked ? 'active' : '', $this->get_button_svg(), esc_html( $count ) );
        
        return apply_filters( 'play_block_get_like_button', $ret, $id, $count, $liked, $this );
    }

    public function get_button_svg() {
        $icon = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon"><path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"></path></svg>';
        if ( 'posts' === $this->type ) {
            $icon = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path></svg>';
        }
        if ( 'dislike' === $this->meta_key ) {
            $icon = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon"><path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"></path></svg>';
        }

        return apply_filters( 'like_button_svg', $icon, $this->type, $this->meta_key );
    }

    /**
     * Add a like
     */
    private function add_like( $user_id, $id ) {
        $likes = $this->get_user_likes( $user_id );

        // add and update to user meta
        if ( ! $this->site_exists( $this->site_id, $likes ) ) {
            $likes[] = array(
                'site_id'  => $this->site_id,
                'posts'    => array(),
                'comments' => array()
            );
        }

        // add and update the user meta
        foreach ( $likes as $key => $site_likes ) {
            if ( $site_likes[ 'site_id' ] !== $this->site_id ) {
                continue;
            }
            if ( ! in_array( $id, $likes[ $key ][ $this->type ] ) ) {
                $likes[ $key ][ $this->type ][] = (int) $id;
                update_user_meta( $user_id, $this->meta_key, $likes );
            }
        }
        // add and update to meta
        $like_users = $this->get_like_users( $id );

        if ( ! in_array( $user_id, $like_users ) ) {
            // update count
            $like_count = $this->get_like_count( $id );

            $this->update_meta( $id, $this->meta_key . '_count', ++ $like_count );

            // update user
            $like_users[] = (int) $user_id;

            $this->update_meta( $id, $this->meta_key . '_user', $like_users );

        }

        return apply_filters( 'play_add_like', $likes, $user_id, $id, $this );
    }

    /**
     * Remove a like
     */
    private function remove_like( $user_id, $id ) {
        $likes = $this->get_user_likes( $user_id );

        foreach ( $likes as $key => $site_likes ) {
            if ( $site_likes[ 'site_id' ] !== $this->site_id ) {
                continue;
            }
            foreach ( $site_likes[ $this->type ] as $k => $p ) {
                if ( $p == $id ) {
                    unset( $likes[ $key ][ $this->type ][ $k ] );
                }
            }
        }
        update_user_meta( $user_id, $this->meta_key, $likes );

        // remove and update to meta
        $like_users = $this->get_like_users( $id );
        if ( in_array( $user_id, $like_users ) ) {
            // update count
            $like_count = $this->get_like_count( $id );

            $this->update_meta( $id, $this->meta_key . '_count', max( -- $like_count, 0 ) );

            // update user
            foreach ( $like_users as $k => $u ) {
                if ( $u == $user_id ) {
                    unset( $like_users[ $k ] );
                }
            }

            $this->update_meta( $id, $this->meta_key . '_user', $like_users );

        }

        return apply_filters( 'play_remove_like', $likes, $user_id, $id, $this );
    }

    /**
     * update meta
     */
    public function update_meta( $id, $key, $value ) {
        if ( 'comments' === $this->type ) {
            update_comment_meta( $id, $key, $value );
        } else {
            update_post_meta( $id, $key, $value );
        }
    }

    /**
     * Check if a user like a post
     */
    public function is_user_like( $user_id, $id ) {
        $likes = $this->get_user_likes( $user_id, $this->site_id );
        if ( in_array( $id, $likes ) ) {
            return true;
        }

        return false;
    }

    /**
     * Get post like count
     */
    public function get_like_count( $id ) {
        if ( $this->type === 'comments' ) {
            $count = get_comment_meta( $id, $this->meta_key . '_count', true );
        } else {
            $count = get_post_meta( $id, $this->meta_key . '_count', true );
        }
        if ( $count == '' ) {
            $count = 0;
        }

        return apply_filters( 'play_like_count', $count, $id, $this );
    }

    /**
     * Get users who like a post
     */
    public function get_like_users( $id ) {
        if ( $this->type === 'comments' ) {
            $users = get_comment_meta( $id, $this->meta_key . '_user', true );
        } else {
            $users = get_post_meta( $id, $this->meta_key . '_user', true );
        }
        if ( $users == '' ) {
            $users = array();
        }

        return apply_filters( 'play_like_users', $users, $id, $this );
    }

    /**
     * Get likes for a user
     */
    public function get_user_likes( $user_id, $site_id = null ) {
        $likes = get_user_meta( $user_id, $this->meta_key, true );
        if ( empty( $likes ) ) {
            return array(
                array(
                    'site_id'  => $this->site_id,
                    'posts'    => array(),
                    'comments' => array()
                )
            );
        }

        return ( ! is_null( $site_id ) ) ? $this->pluck_site_likes( $site_id, $likes ) : $likes;
    }

    /**
     * Pluck the site likes
     */
    private function pluck_site_likes( $site_id, $all_likes ) {
        foreach ( $all_likes as $site_likes ) {
            if ( $site_likes[ 'site_id' ] == $site_id && isset( $site_likes[ $this->type ] ) ) {
                return $site_likes[ $this->type ];
            }
        }

        return array();
    }

    /**
     * Get likes for a user as array data
     */
    public function get_user_likes_array( $user_id ) {
        $likes = $this->get_user_likes( $user_id, get_current_blog_id() );

        return $this->remove_invalid_like( $likes );
    }

    /**
     * Remove invalid like
     */
    private function remove_invalid_like( $likes ) {
        foreach ( $likes as $key => $like ) {
            if ( ! $this->exists( $like ) ) {
                unset( $likes[ $key ] );
            }
        }

        return $likes;
    }

    /**
     * Post exists
     */
    private function exists( $id ) {
        if ( $this->type == 'comments' ) {
            $status = wp_get_comment_status( $id );
        } else {
            $status = get_post_status( $id );
        }

        return ( $status && ( 'publish' === $status || 'approved' === $status ) ) ? true : false;
    }

    private function site_exists( $site_id, $arr ) {
        foreach ( $arr as $key => $site ) {
            if ( $site[ 'site_id' ] == $site_id ) {
                return true;
            }
        }

        return false;
    }

}

Play_Like::instance();
