<?php

/* --------------------------------------------------------------------

  This file is part of Chevereto Free.
  https://chevereto.com/free

  (c) Rodolfo Berrios <rodolfo@chevereto.com>

  For the full copyright and license information, please view the LICENSE
  file that was distributed with this source code.

  --------------------------------------------------------------------- */

namespace CHV;

use G;
use Exception;

class Stat
{
    public static function getTotals()
    {
        $totals = DB::queryFetchSingle('SELECT * FROM '.DB::getTable('stats').' WHERE stat_type = "total"');
        unset($totals['stat_id'], $totals['stat_type'], $totals['date_gmt']);
        return DB::formatRow($totals);
    }
    
    public static function track($args=[])
    {
        if (!in_array($args['action'], ['insert', 'update', 'delete'])) {
            throw new StatException(sprintf('Invalid stat action "%s" in ', $args['action']) . __METHOD__, 100);
        }
        $tables = DB::getTables();
        // Restrict unknown tables
        if (!array_key_exists($args['table'], $tables)) {
            throw new StatException(sprintf('Unknown table "%s" in ' . __METHOD__, $args['table']), 101);
        }
        // Restrict tables by stat action
        switch ($args['action']) {
            case 'insert':
                if (!in_array($args['table'], ['albums', 'images', 'likes', 'users'])) {
                    throw new StatException(sprintf('Table "%s" does not bind an stat procedure in ' . __METHOD__, $args['table']), 101);
                }
            break;
        }
        // Check missing values
        if ($args['table'] == 'images' and in_array($args['action'], ['insert', 'delete'])) {
            if (!isset($args['disk_sum'])) {
                $disk_sum_value = 0;
            } else {
                if (preg_match('/^([\+\-]{1})?\s*([\d]+)$/', $args['disk_sum'], $matches)) {
                    $disk_sum_value = $matches[2];
                } else {
                    throw new StatException(sprintf('Invalid disk_sum value "%s" in ' . __METHOD__, $args['disk_sum']), 104);
                }
            }
        }
        // Fix values
        if (!isset($args['value'])) {
            $value = 1;
        } else {
            if (preg_match('/^([\+\-]{1})?\s*([\d]+)$/', $args['value'], $matches)) {
                $value = $matches[2];
            } else {
                throw new StatException(sprintf('Invalid value "%s" in ' . __METHOD__, $args['value']), 102);
            }
        }
        if (!isset($args['date_gmt'])) {
            switch ($args['action']) {
                case 'insert':
                case 'update':
                    $args['date_gmt'] = G\datetimegmt();
                break;
                case 'delete':
                    throw new StatException('Missing date_gmt value in ' . __METHOD__, 105);
                break;
            }
        } else {
            try {
                $date = new \DateTime($args['date_gmt']);
                $args['date_gmt'] = $date->format('Y-m-d');
            } catch (Exception $e) {
                //throw new StatException($e);
                throw new StatException('Invalid date_gmt value in ' . __METHOD__, 106);
            }
        }
        
        // Get to the choppa
        switch ($args['action']) {
            
            case 'insert':
                switch ($args['table']) {
                    case 'images':
                        if (!isset($args['disk_sum'])) {
                            throw new StatException('Missing disk_sum value in ' . __METHOD__, 103);
                        }
                        $sql_tpl =
                            'UPDATE `%table_stats` SET stat_images = stat_images + %value, stat_disk_used = stat_disk_used + %disk_sum WHERE stat_type = "total";' . "\n" .
                            'INSERT INTO `%table_stats` (stat_type, stat_date_gmt, stat_images, stat_disk_used) VALUES ("date",DATE("%date_gmt"),"%value", "%disk_sum") ON DUPLICATE KEY UPDATE stat_images = stat_images + %value, stat_disk_used = stat_disk_used + %disk_sum;';
                    break;
                    default: // albums, likes, users
                        $sql_tpl =
                            'UPDATE `%table_stats` SET stat_%related_table = stat_%related_table + %value WHERE stat_type = "total";' . "\n" .
                            'INSERT `%table_stats` (stat_type, stat_date_gmt, stat_%related_table) VALUES ("date",DATE("%date_gmt"),"%value") ON DUPLICATE KEY UPDATE stat_%related_table = stat_%related_table + %value;';
                    break;
                }
            break;
            
            case 'update':
                switch ($args['table']) {
                    case 'images':
                    case 'albums':
                        // Track image | album | user views
                        $sql_tpl =
                            'UPDATE `%table_stats` SET stat_%aux_views = stat_%aux_views + %value WHERE stat_type = "total";' . "\n" .
                            'INSERT INTO `%table_stats` (stat_type, stat_date_gmt, stat_%aux_views) VALUES ("date",DATE("%date_gmt"),"%value") ON DUPLICATE KEY UPDATE stat_%aux_views = stat_%aux_views + %value;';
                        if (isset($args['user_id'])) {
                            $sql_tpl .= "\n" . 'UPDATE `%table_users` SET user_content_views = user_content_views + %value WHERE user_id = %user_id;';
                        }
                        $sql_tpl = strtr($sql_tpl, ['%aux' => DB::getFieldPrefix($args['table'])]);
                    break;
                }
            break;
            
            case 'delete':
                switch ($args['table']) {
                    case 'images':
                        $sql_tpl =
                            'UPDATE `%table_stats` SET stat_images = GREATEST(stat_images - %value, 0) WHERE stat_type = "total";' . "\n" .
                            'UPDATE `%table_stats` SET stat_images = GREATEST(stat_images - %value, 0) WHERE stat_type = "date" AND stat_date_gmt = DATE("%date_gmt");' . "\n" .
                            'UPDATE `%table_stats` SET stat_image_likes = GREATEST(stat_image_likes - %likes, 0) WHERE stat_type = "total";' . "\n" .
                            'UPDATE `%table_stats` SET stat_image_likes = GREATEST(stat_image_likes - %likes, 0) WHERE stat_type = "date" AND stat_date_gmt = DATE("%date_gmt");' . "\n" .
                            'UPDATE `%table_stats` SET stat_disk_used = GREATEST(stat_disk_used - %disk_sum, 0) WHERE stat_type = "total";' . "\n" .
                            'UPDATE `%table_stats` SET stat_disk_used = GREATEST(stat_disk_used - %disk_sum, 0) WHERE stat_type = "date" AND stat_date_gmt = DATE("%date_gmt");';
                    break;
                    default:  // albums, likes, users
                        $sql_tpl =
                            'UPDATE `%table_stats` SET stat_%related_table = GREATEST(stat_%related_table - %value, 0) WHERE stat_type = "total";' . "\n" .
                            'UPDATE `%table_stats` SET stat_%related_table = GREATEST(stat_%related_table - %value, 0) WHERE stat_type = "date" AND stat_date_gmt = DATE("%date_gmt");';
                        if ($args['table'] == 'users') {
                            $sql_tpl .=
                                // Update likes stats related to this deleted user
                                'UPDATE IGNORE `%table_stats` AS S
									INNER JOIN (
										SELECT DATE(like_date_gmt) AS like_date_gmt, COUNT(*) AS cnt
										FROM `%table_likes`
											WHERE like_user_id = %user_id
										GROUP BY DATE(like_date_gmt)
								  ) AS L ON S.stat_date_gmt = L.like_date_gmt
								SET S.stat_image_likes = GREATEST(S.stat_image_likes - COALESCE(L.cnt, "0"), 0) WHERE stat_type = "date";
								UPDATE IGNORE `%table_stats` SET stat_image_likes = GREATEST(stat_image_likes - COALESCE((SELECT COUNT(*) FROM `%table_likes` WHERE like_user_id = %user_id), "0"), 0) WHERE stat_type = "total";' . "\n" .
                                // Update album stats related to this deleted user
                                'UPDATE IGNORE `%table_stats` AS S
									INNER JOIN (
										SELECT DATE(album_date_gmt) AS album_date_gmt, COUNT(*) AS cnt
										FROM `%table_albums`
											WHERE album_user_id = %user_id
										GROUP BY DATE(album_date_gmt)
								  ) AS A ON S.stat_date_gmt = A.album_date_gmt
								SET S.stat_albums = GREATEST(S.stat_albums - COALESCE(A.cnt, "0"), 0) WHERE stat_type = "date";
								UPDATE IGNORE `%table_stats` SET stat_albums = GREATEST(stat_albums - COALESCE((SELECT COUNT(*) FROM `%table_albums` WHERE album_user_id = %user_id), "0"), 0) WHERE stat_type = "total";';
                        }
                    break;
                }
            break;
        }
        
        $sql = strtr($sql_tpl, [
            '%table_stats'		=> $tables['stats'],
            '%table_users'		=> $tables['users'],
            '%table_likes'		=> $tables['likes'],
            '%table_albums'		=> $tables['albums'],
            '%related_table'	=> (isset($args['content_type']) ? ($args['content_type'] . '_') : null) . $args['table'],
            '%value'			=> $value,
            '%date_gmt'			=> $args['date_gmt'],
            '%user_id'			=> $args['user_id'],
            '%disk_sum'			=> $disk_sum_value,
            '%likes'			=> $args['likes'],
        ]);
        
        try {
            DB::queryExec($sql);
        } catch (Exception $e) {
            throw new StatException($e->getMessage(), 400);
        }
    }
    
    public static function rebuildTotals()
    {
        $query = 'TRUNCATE TABLE `%table_prefix%stats`;

        INSERT INTO `%table_prefix%stats` (stat_id, stat_date_gmt, stat_type) VALUES ("1", NULL, "total") ON DUPLICATE KEY UPDATE stat_type=stat_type;
        
        UPDATE `%table_prefix%stats` SET
        stat_images = (SELECT IFNULL(COUNT(*),0) FROM `%table_prefix%images`),
        stat_albums = (SELECT IFNULL(COUNT(*),0) FROM `%table_prefix%albums`),
        stat_users = (SELECT IFNULL(COUNT(*),0) FROM `%table_prefix%users`),
        stat_image_views = (SELECT IFNULL(SUM(image_views),0) FROM `%table_prefix%images`),
        stat_disk_used = (SELECT IFNULL(SUM(image_size) + SUM(image_thumb_size) + SUM(image_medium_size),0) FROM `%table_prefix%images`)
        WHERE stat_type = "total";';

        $sql = strtr($query, [
            '%table_prefix%' => G\get_app_setting('db_table_prefix'),
        ]);

        $db = DB::getInstance();
        $db->query($sql);
        $db->exec();
    }
}

class StatException extends Exception
{
}
