<?php
/**
 * The upload limits class
 *
 * This is used to define upload limits functions
 *
 * @since      1.6.0
 * @package    WCVendors_Pro
 * @subpackage WCVendors_Pro/includes
 * @author     Lindeni Mahlalela, WC Vendors
 */
class WCVendors_Pro_Upload_Limits {

    /**
     * Holds the instance of the class
     *
     * @var     object
     * @since   1.6.0
     * @version 1.6.0
     */
    private static $instance = null;

    /**
     * The user id
     *
     * @var     int
     * @since   1.6.0
     * @version 1.6.0
     */
    protected static $user_id = 0;

    /**
     * Initialize the class and set its properties.
     *
     * @since    1.6.0
     * @version  1.6.0
     * @param int $user_id User ID.
     */
    public function __construct( $user_id ) {
        self::$user_id = $user_id;
    }

    /**
     * Get instance of this class
     *
     * @param int $user_id User ID.
     * @since   1.6.0
     * @version 1.6.0
     * @return object
     */
    public static function get_instance( $user_id ) {
        if ( null === self::$instance ) {
            self::$instance = new self( $user_id );
        } else {
            self::set_user_id( $user_id );
        }

        return self::$instance;
    }

    /**
     * Get the user id
     *
     * @return  int $user_id
     * @since   1.6.0
     * @version 1.6.0
     */
    public function get_user_id() {
        if ( 0 === (int) self::$user_id && is_user_logged_in() ) {
            self::$user_id = get_current_user_id();
        }

        return self::$user_id;
    }

    /**
     * Set the user id
     *
     * @param   int $user_id The user ID.
     * @return  void
     * @since   1.6.0
     * @version 1.6.0
     */
    public static function set_user_id( $user_id ) {
        self::$user_id = $user_id;
    }

    /**
     * Limit vendor upload based on settings
     *
     * @param   array $file The details of the file to be uploaded.
     * @return  array $file The details of the file to be uploaded.
     * @since   1.6.0
     * @version 1.6.0
     * @version 2.0.1 - Add max file upload size limit
     */
    public function check_upload_limits( $file ) {

        $user_id = $this->get_user_id();

        if ( ! WCV_Vendors::is_vendor( $user_id ) ) {
            return $file;
        }

        $message = '';
        if ( $this->disk_limit_reached() ) {
            $message = apply_filters(
                'wcv_global_disk_usage_limit_message',
                __( 'You have reached the total disk usage allowed in your account.', 'wcvendors-pro' )
            );
        }

        if ( $this->files_limit_reached() ) {
            $message .= apply_filters(
                'wcv_global_file_count_limit_message',
                __( 'You have reached the total number of files you are allowed to upload.', 'wcvendors-pro' )
            );
        }

        if ( $this->file_size_limit_exceeded( $file ) ) {
            $max_size_formatted = $this->format_disk_size( $this->get_max_file_size_limit( $user_id ) );
            $message           .= apply_filters(
                'wcv_max_file_size_limit_message',
                sprintf(
                    // translators: %s is the maximum allowed file size (e.g. "10 MB").
                    __( 'The file size exceeds the maximum allowed size of %s.', 'wcvendors-pro' ),
                    $max_size_formatted
                )
            );
        }

        if ( ! empty( $message ) ) {
            $file['error'] = $message;
        }

        return $file;
    }

    /**
     * Get a user's disk usage
     *
     * @param   integer $user_id The user ID.
     * @return  int
     * @since   1.6.0
     * @version 1.6.0
     */
    public function get_disk_usage( $user_id = 0 ) {

        $user_id = $this->get_user_id();

        if ( 0 === $user_id ) {
            return 0;
        }

        $disk_usage = get_transient( 'wcv_user_disk_usage_' . $user_id );

        if ( $disk_usage ) {
            return $disk_usage;
        }

        $include_vendor_thumbnails = $this->include_vendor_thumbnails( $user_id );
        $include_global_thumbnails = $this->include_global_thumbnails();

        if ( $include_vendor_thumbnails || $include_global_thumbnails ) {
            $user_media = self::get_media( $user_id );

            $disk_usage = 0;
            if ( ! empty( $user_media ) ) {
                foreach ( $user_media as $media ) {
                    $disk_usage += $media['file_size'];

                    if ( ! empty( $media['thumbnails'] ) ) {
                        foreach ( $media['thumbnails'] as $thumbnail ) {
                            $disk_usage += $thumbnail['file_size'];
                        }
                    }
                }
            }
        }

        set_transient( 'wcv_user_disk_usage_' . $user_id, $disk_usage, 12 * HOUR_IN_SECONDS );

        return $disk_usage;
    }

    /**
     * Check if user has reached disk usage limit
     *
     * @return  bool
     * @since   1.6.0
     * @version 1.6.0
     */
    public function disk_limit_reached() {
        $user_id = $this->get_user_id();

        if ( 0 === $user_id ) {
            return true;
        }

        $vendor_disk_limited = get_user_meta( $user_id, '_wcv_vendor_disk_usage_limit_reached', true );
        $current_disk_usage  = self::get_disk_usage();
        $limit_disk_usage    = $this->get_disk_usage_limit( $user_id );

        if ( ( $current_disk_usage >= $limit_disk_usage && $limit_disk_usage > 0 ) || $vendor_disk_limited ) {
            return true;
        } else {
            delete_user_meta( $user_id, '_wcv_vendor_disk_usage_limit_reached' );
        }

        return false;
    }

    /**
     * Check if user has reached files count limit
     *
     * @return  bool
     * @since   1.6.0
     * @version 1.7.6
     */
    public function files_limit_reached() {

        $user_id = $this->get_user_id();

        if ( 0 === $user_id ) {
            return false;
        }

        $vendor_files_limited = get_user_meta( $user_id, '_wcv_vendor_file_count_limit_reached', true );
        $user_media           = self::get_media();
        $current_number_files = ! empty( $user_media ) ? count( $user_media ) : 0;
        $limit_files_count    = $this->get_files_count_limit( $user_id );

        $user_meta  = get_userdata( $user_id );
        $user_roles = $user_meta->roles;
        if ( in_array( 'vendor', $user_roles, true ) ) {
            if ( ( $current_number_files >= $limit_files_count && $limit_files_count > 0 ) || $vendor_files_limited ) {
                return true;
            } else {
                delete_user_meta( $user_id, '_wcv_vendor_file_count_limit_reached' );
            }
        }

        return false;
    }

    /**
     * Get the file count limit based on vendor overrides or global settings
     *
     * @param int $user_id The user ID.
     *
     * @return  int $user_id The user ID.
     * @since   1.6.0
     * @version 1.6.0
     */
    public function get_files_count_limit( $user_id = 0 ) {
        $user_id = 0 === $user_id ? $this->get_user_id() : $user_id;

        $user_file_limit = apply_filters(
            'wcv_global_file_count_limit_' . $user_id,
            get_user_meta( $user_id, '_wcv_vendor_file_count_limit', true ),
            $user_id
        );

        $global_file_limit = apply_filters(
            'wcv_global_file_count_limit',
            get_option( 'wcvendors_global_files_count_limit', 0 )
        );

        // Prioritize user/vendor level limits.
        $limit_files_count = ! empty( $user_file_limit ) && is_numeric( $user_file_limit )
            ? $user_file_limit
            : $global_file_limit;

        return (int) $limit_files_count;
    }

    /**
     * Get the disk usage limit based on vendor overrides or global settings
     *
     * @param int $user_id The user ID.
     *
     * @return  int $user_id The user ID.
     * @since   1.6.0
     * @version 1.6.0
     */
    public function get_disk_usage_limit( $user_id = 0 ) {

        $user_id = 0 === $user_id ? $this->get_user_id() : $user_id;

        $vendor_disk_limit = get_user_meta( $user_id, '_wcv_vendor_disk_usage_limit', true );

        $user_disk_limit         = apply_filters(
            'wcv_global_disk_usage_limit_' . $user_id,
            is_numeric( $vendor_disk_limit ) ? $vendor_disk_limit * MB_IN_BYTES : 0,
            $user_id
        );
        $global_disk_limit_value = get_option( 'wcvendors_global_disk_usage_limit', 0 );
        $global_disk_limit       = apply_filters(
            'wcv_global_disk_usage_limit',
            is_numeric( $global_disk_limit_value ) ? $global_disk_limit_value * MB_IN_BYTES : 0
        );

        $limit_disk_usage = ! empty( $user_disk_limit ) && is_numeric( $user_disk_limit )
            ? $user_disk_limit
            : $global_disk_limit;

        return (int) $limit_disk_usage;
    }

    /**
     * Get the max file size limit based on vendor overrides or global settings
     *
     * @param int $user_id The user ID.
     *
     * @return int Maximum file size in bytes.
     * @since   2.0.1
     * @version 2.0.1
     */
    public function get_max_file_size_limit( $user_id = 0 ) {
        $user_id = 0 === $user_id ? $this->get_user_id() : $user_id;

        $vendor_file_size_limit = get_user_meta( $user_id, '_wcv_vendor_max_upload_file_size', true );

        $user_file_size_limit = apply_filters(
            'wcv_max_upload_file_size_' . $user_id,
            is_numeric( $vendor_file_size_limit ) ? $vendor_file_size_limit * MB_IN_BYTES : 0,
            $user_id
        );

        $global_file_size_limit_value = get_option( 'wcvendors_max_upload_file_size', 0 );
        $global_file_size_limit       = apply_filters(
            'wcv_max_upload_file_size',
            is_numeric( $global_file_size_limit_value ) ? $global_file_size_limit_value * MB_IN_BYTES : 0
        );

        $max_file_size = ! empty( $user_file_size_limit ) && is_numeric( $user_file_size_limit )
            ? $user_file_size_limit
            : $global_file_size_limit;

        return (int) $max_file_size;
    }

    /**
     * Check if file exceeds max file size limit
     *
     * @param array $file File data array.
     * @return bool
     * @since   2.0.1
     * @version 2.0.1
     */
    public function file_size_limit_exceeded( $file ) {
        $user_id = $this->get_user_id();

        if ( 0 === $user_id ) {
            return false;
        }

        $max_file_size = $this->get_max_file_size_limit( $user_id );

        // 0 means unlimited.
        if ( 0 === $max_file_size ) {
            return false;
        }

        $file_size = isset( $file['size'] ) ? $file['size'] : 0;

        return $file_size > $max_file_size;
    }

    /**
     * Get all media that belongs to a user
     *
     * @param   integer $user_id User id.
     * @return  array   List of media file details.
     * @since   1.6.0
     * @version 1.7.7
     */
    public function get_media( $user_id = 0 ) {

        if ( 0 === (int) $user_id ) {
            $user_id = $this->get_user_id();
        }

        if ( 0 === (int) $user_id ) {
            return array();
        }

        $user_media = array();

        $user_attachments = get_transient( 'wcv_user_media_' . $user_id );
        if ( $user_attachments ) {
            return apply_filters( 'wcv_user_media_' . $user_id, $user_attachments, $user_id );
        }

        $user_attachments = get_posts(
            apply_filters(
                'wcv_get_user_media_query_args',
                array(
                    'post_type'   => 'attachment',
                    'post_status' => 'inheret',
                    'author'      => $user_id,
                    'number'      => -1,
                )
            )
        );

        if ( $user_attachments ) {
            $site_url = site_url();

            foreach ( $user_attachments as $attachment ) {
                $file_path = str_replace( $site_url, ABSPATH, $attachment->guid );
                $file_path = str_replace( '//', '/', $file_path );
                $file_size = file_exists( $file_path ) ? filesize( $file_path ) : 0;

                $include_vendor_thumbnails = $this->include_vendor_thumbnails( $user_id );
                $include_global_thumbnails = $this->include_global_thumbnails();

                $media_thumbnails = array();
                if ( $include_vendor_thumbnails || $include_global_thumbnails ) {

                    $media_meta = wp_get_attachment_metadata( $attachment->ID );

                    if ( ! empty( $media_meta['sizes'] ) ) {
                        foreach ( $media_meta['sizes'] as $thumbnail ) {
                            $thumbnail_path     = plugin_dir_path( $file_path ) . $thumbnail['file'];
                            $media_thumbnails[] = array(
                                'file_path' => $thumbnail_path,
                                'file_size' => file_exists( $thumbnail_path ) ? filesize( $thumbnail_path ) : 0,
                            );
                        }
                    }
                }

                $user_media[] = array(
                    'media_id'    => $attachment->ID,
                    'url'         => $attachment->guid,
                    'file_path'   => $file_path,
                    'file_size'   => $file_size,
                    'post_author' => $attachment->post_author,
                    'thumbnails'  => $media_thumbnails,
                );
            }
        }

        $user_media = apply_filters( 'wcv_user_media_' . $user_id, $user_media, $user_id );

        set_transient(
            'wcv_user_media_' . $user_id,
            $user_media,
            apply_filters( 'wcv_user_media_transient', 2 * HOUR_IN_SECONDS )
        );

        return $user_media;
    }

    /**
     * Check if thumbnails should contribute to limits for this user
     *
     * @param   int $user_id User ID.
     * @return  bool
     * @since   1.6.0
     * @version 1.6.0
     */
    public function include_vendor_thumbnails( $user_id = 0 ) {

        $user_id = $this->get_user_id();

        return wc_string_to_bool(
            apply_filters(
                'wcv_vendor_upload_limits_include_thumbnails',
                get_user_meta( $user_id, '_wcv_vendor_upload_limits_include_thumbnails', true )
            )
        );
    }

    /**
     * Check if admin has set media thumbnails to contribute to limits
     *
     * @return  bool
     * @since   1.6.0
     * @version 1.6.0
     */
    public function include_global_thumbnails() {
        return wc_string_to_bool(
            apply_filters(
                'wcvendors_upload_limits_include_thumbnails',
                get_option( 'wcvendors_upload_limits_include_thumbnails', 'yes' )
            )
        );
    }

    /**
     * Format disk usage size.
     * Return 0 instead of Unlimited if vendor hasn't uploaded  any file.
     *
     * @param int  $size The size to format.
     * @param bool $require_units Whether to require units or not.
     *
     * @return int|string
     */
    public function format_disk_usage_size( $size, $require_units = true ) {
        if ( 0 === (int) $size ) {
            return 0;
        }

        return $this->format_disk_size( $size, $require_units );
    }

    /**
     * Format size into units
     *
     * @param int  $size The size to format.
     * @param bool $require_units Whether to require units or not.
     *
     * @return  string The size of the file with units.
     * @since   1.6.0
     * @version 1.6.0
     */
    public function format_disk_size( $size, $require_units = true ) {
        if ( 0 === $size ) {
            return __( 'Unlimited', 'wcvendors-pro' );
        }

        $units = explode( ' ', 'B KB MB GB TB PB' );

        $mod = 1024;

        for ( $i = 0; $size > $mod; $i++ ) {
            $size /= $mod;
        }

        $dot_pos   = strpos( $size, '.' );
        $end_index = false !== $dot_pos ? $dot_pos + 3 : strlen( $size );

        return $require_units ? substr( $size, 0, $end_index ) . ' ' . $units[ $i ] : substr( $size, 0, $end_index );
    }
}
