<?php

namespace WP_Statistics\Models;

use WP_Statistics\Abstracts\BaseModel;
use WP_Statistics\Components\DateTime;
use WP_Statistics\Decorators\VisitorDecorator;
use WP_Statistics\Traits\WpCacheTrait;
use WP_Statistics\Utils\Query;

class OnlineModel extends BaseModel
{
    use WpCacheTrait;

    protected $timeframe;

    /**
     * @param array $args
     *      - timeframe: int Timeframe in minutes. Default: 5 minutes.
     */
    public function __construct($args = [])
    {
        $this->setArgs($args);
    }

    /**
     * Set the arguments for the OnlineModel.
     *
     * @return void
     */
    public function setArgs($args = [])
    {
        $args = wp_parse_args($args, [
            /**
             * How many minutes a visitor counts as "online" for.
             *
             * The window was fixed at 5 minutes, so sites that wanted a different value had
             * to edit the plugin on every update. This filter lets it be set once, from a
             * small snippet, and it holds through updates.
             *
             * @example add_filter('wp_statistics_online_visitors_timeframe', function () { return 240; });
             */
            'timeframe' => apply_filters('wp_statistics_online_visitors_timeframe', 5)
        ]);

        $this->timeframe = [
            'from' => DateTime::get('-' . $args['timeframe'] . ' min', 'Y-m-d H:i:s'),
            'to'   => DateTime::get('now', 'Y-m-d H:i:s')
        ];
    }


    /**
     * Get the number of online visitors.
     *
     * @return int
     */
    public function countOnlines($args = [])
    {
        $args = $this->parseArgs($args, [
            'resource_type' => '',
            'resource_id'   => '',
            'page_id'       => '',
            'agent'         => '',
            'platform'      => '',
            'country'       => '',
            'logged_in'     => false
        ]);

        $query = Query::select('COUNT(*)')
            ->from('visitor')
            ->where('location', '=', $args['country'])
            ->where('platform', '=', $args['platform'])
            ->where('agent', '=', $args['agent'])
            ->where('last_page', '=', $args['page_id'])
            ->where('last_counter', '=', DateTime::get())
            ->whereDate('last_view', $this->timeframe);

        if ($args['logged_in'] === true) {
            $query->where('visitor.user_id', '!=', 0);
        }

        if (!empty($args['resource_type']) || !empty($args['resource_id'])) {
            $query
                ->join('pages', ['visitor.last_page', 'pages.page_id'])
                ->where('pages.type', 'IN', $args['resource_type'])
                ->where('pages.id', '=', $args['resource_id']);
        }

        $cacheKey = 'count_online_' . md5(json_encode(array_filter($args)));

        $result = $this->getCachedData($cacheKey, function () use ($query) {
            return $query->getVar();
        }, 3);

        return $result ? $result : 0;
    }

    /**
     * Returns online visitors.
     *
     * @param array $args Arguments to include in query (e.g. `date`, etc.).
     * @return array
     */
    public function getOnlineVisitors($args = [])
    {
        $args = $this->parseArgs($args, [
            'ip'            => '',
            'resource_type' => '',
            'resource_id'   => '',
            'page_id'       => '',
            'agent'         => '',
            'platform'      => '',
            'country'       => '',
            'logged_in'     => false,
            'page'          => 1,
            'per_page'      => '',
            'order_by'      => 'last_view',
            'order'         => 'DESC',
            'decorate'      => true
        ]);

        $query = Query::select('*')
            ->from('visitor')
            ->where('ip', '=', $args['ip'])
            ->where('location', '=', $args['country'])
            ->where('platform', '=', $args['platform'])
            ->where('agent', '=', $args['agent'])
            ->where('last_page', '=', $args['page_id'])
            ->where('last_counter', '=', DateTime::get())
            ->whereDate('last_view', $this->timeframe)
            ->perPage($args['page'], $args['per_page'])
            ->orderBy($args['order_by'], $args['order']);

        if ($args['decorate'] === true) {
            $query->decorate(VisitorDecorator::class);
        }

        if ($args['logged_in'] === true) {
            $query->where('visitor.user_id', '!=', 0);
        }

        if (!empty($args['resource_type']) || !empty($args['resource_id'])) {
            $query
                ->join('pages', ['visitor.last_page', 'pages.page_id'])
                ->where('pages.type', '=', $args['resource_type'])
                ->where('pages.id', '=', $args['resource_id']);
        }

        $result = $query->getAll();

        return $result ? $result : [];
    }

    /**
     * Returns a list of active pages with their respective visitors and views.
     *
     * @param array $args Arguments to include in query (e.g. `date`, etc.).
     *
     * @return array
     */
    public function getActivePages($args = [])
    {
        $args = $this->parseArgs($args, [
            'order_by' => 'visitors',
            'order'    => 'DESC'
        ]);

        $result = Query::select(['pages.page_id', 'COUNT(DISTINCT visitor_id) as visitors', 'COUNT(*) as views'])
            ->from('pages')
            ->join('visitor_relationships', ['visitor_relationships.page_id', 'pages.page_id'])
            ->whereDate('pages.date', 'today')
            ->whereDate('visitor_relationships.date', $this->timeframe)
            ->groupBy('pages.page_id')
            ->orderBy($args['order_by'], $args['order'])
            ->getAll();

        return $result ?? [];
    }

    /**
     * Returns the number of visitors per minute
     *
     * @param array $args Arguments to include in query (e.g. `date`, etc.).
     *
     * @return array
     */
    public function getVisitorsPerMinute($args = [])
    {
        $args = $this->parseArgs($args, [
            // ...
        ]);

        $result = Query::select([
                "DATE_FORMAT(visitor_relationships.date, '%%H:%%i') AS time",
                'COUNT(DISTINCT visitor_id) as visitors'
            ])
            ->from('pages')
            ->join('visitor_relationships', ['visitor_relationships.page_id', 'pages.page_id'])
            ->whereDate('pages.date', 'today')
            ->whereDate('visitor_relationships.date', $this->timeframe)
            ->groupBy('time')
            ->getAll();

        return $result;
    }
}