<?php

class SystemStatisticsCronJob extends AbstractCronJob
{

    const LIFETIME_IN_SEC = 172800; // 60*60*24*2 = 48H
    const FULL_TIME = '00';
    const QUARTER_TIME = '15';
    const HALF_TIME = '30';
    const THREE_QUARTER_TIME = '45';
    const ALERT_MEM_PERCENT_WARNING = 90;
    const ALERT_MEM_PERCENT_ERROR = 95;
    const ALERT_MEM_PERCENT_CRITICAL = 99;
    const ALERT_LOAD_PROCCESS_WARNING = 2;
    const ALERT_LOAD_PROCCESS_ERROR = 5;
    const ALERT_LOAD_PROCCESS_CRITICAL = 10;
    const ALERT_LOAD_PROCCESS_FATAL = 13;

    public $currentServerTime = '';
    public $currentServerTimestamp = '';

    public function run()
    {
        $ctrld = $this->getCtrldInfo();

        if (!$ctrld) {
            Log::e("Can't save system statistic!", 'SystemStatisticCronJob');
            return;
        }
        $model = $this->getStatisticModel();
        $this->currentServerTime = PhonalisaDate::newServerDate();
        $this->currentServerTimestamp = PhonalisaDate::newServerTimestamp();
        $currentTime = PhonalisaDate::convertServerDateTo('i', $this->currentServerTime);
        $result = false;

        if ($currentTime === SystemStatisticsCronJob::FULL_TIME) {
            $result = $this->writeStatisticToModel($model, $ctrld, SystemStatisticsCronJob::FULL_TIME);
        } else if ($currentTime === SystemStatisticsCronJob::QUARTER_TIME) {
            $result = $this->writeStatisticToModel($model, $ctrld, SystemStatisticsCronJob::QUARTER_TIME);
        } else if ($currentTime === SystemStatisticsCronJob::HALF_TIME) {
            $result = $this->writeStatisticToModel($model, $ctrld, SystemStatisticsCronJob::HALF_TIME);
        } else if ($currentTime === SystemStatisticsCronJob::THREE_QUARTER_TIME) {
            $result = $this->writeStatisticToModel($model, $ctrld, SystemStatisticsCronJob::THREE_QUARTER_TIME);
        }

        if (!$result) {
            Log::e("Can't save system statistic!", 'SystemStatisticCronJob');
            return;
        }
        $this->deleteModelsAfterLifetimeEnds();
    }

    public function getCronRule()
    {
        return "*/15 * * * *";
    }

    private function getCtrldInfo()
    {
        $ctrld = PhonalisaCtrldConnector::getSystemInfo();
        $result = false;

        if ($ctrld->status == 'ok' && isset($ctrld->meminfo) && isset($ctrld->systemloadinfo) && isset($ctrld->thermalinfo)) {
            $result = $ctrld;
        }
        return $result;
    }

    private function getStatisticModel()
    {
        $searchYear = PhonalisaDate::convertServerDateTo('Y', $this->currentServerTime);
        $searchMonth = PhonalisaDate::convertServerDateTo('m', $this->currentServerTime);
        $searchDay = PhonalisaDate::convertServerDateTo('d', $this->currentServerTime);
        $searchHour = PhonalisaDate::convertServerDateTo('H', $this->currentServerTime);

        $models = Statistic::model()->findAllByAttributes(
                array(
                    'target_type' => Statistic::StatisticTargetTypeSystemOverview,
                    'year' => $searchYear,
                    'month' => $searchMonth,
                    'day' => $searchDay
                )
        );

        $model = false;

        foreach ($models as $statisticModel) {
            if ($statisticModel->valueForKey('hour') === $searchHour) {
                $model = $statisticModel;
                break;
            }
        }
        if (!$model) {
            $model = new Statistic();
            $model->target_type = Statistic::StatisticTargetTypeSystemOverview;
            $model->setValueForKey('hour', $searchHour);
        }

        return $model;
    }

    private function writeStatisticToModel($model, $ctrld, $minutes)
    {
        $currentDateTime = PhonalisaDate::convertServerDateTo('Y-m-d_H-', $this->currentServerTime);
        $dateTimeName = $currentDateTime . $minutes;

        $model->year = PhonalisaDate::convertServerDateTo('Y', $this->currentServerTime);
        $model->month = PhonalisaDate::convertServerDateTo('m', $this->currentServerTime);
        $model->day = PhonalisaDate::convertServerDateTo('d', $this->currentServerTime);

        $model = $this->writeMemInfo($model, $ctrld, $dateTimeName);
        $model = $this->writeSystemLoadInfo($model, $ctrld, $dateTimeName);
        $model = $this->writeThermalInfo($model, $ctrld, $dateTimeName);

        if (!$model->save()) {
            return false;
        }
        return true;
    }

    private function writeMemInfo($model, $ctrld, $dateTimeName)
    {
        foreach ($ctrld->meminfo as $deviceType => $deviveInfo) {
            foreach ($deviveInfo as $attributeName => $atributeValue) {
                foreach ($atributeValue as $valueName => $value) {
                    $key = $dateTimeName . "_meminfo_" . $deviceType . "_" . $attributeName . "_" . $valueName;
                    $model->setValueForKey($key, $value);
                }
                $this->informAdminAboutMemStatus(@$atributeValue->device, @$atributeValue->usedPercent);
            }
        }
        return $model;
    }

    private function writeSystemLoadInfo($model, $ctrld, $dateTimeName)
    {
        foreach ($ctrld->systemloadinfo as $key => $value) {
            $key = $dateTimeName . "_systemloadinfo_" . $key;
            $model->setValueForKey($key, $value);
            $this->informAdminAboutLoadStatus($key, $value);
        }
        return $model;
    }

    private function writeThermalInfo($model, $ctrld, $dateTimeName)
    {
        $value = '';
        if (isset($ctrld->thermalinfo->cpu)) {
            $value = $ctrld->thermalinfo->cpu;
        }
        $key = $dateTimeName . "_thermalinfo_cpu";
        $model->setValueForKey($key, $value);

        return $model;
    }

    private function deleteModelsAfterLifetimeEnds()
    {
        $dateTimstampMinusLifetimeInSec = $this->currentServerTimestamp - self::LIFETIME_IN_SEC;
        $searchDateTime = PhonalisaDate::newServerDate($dateTimstampMinusLifetimeInSec, 'U');
        $searchYear = PhonalisaDate::convertServerDateTo('Y', $searchDateTime);
        $searchMonth = PhonalisaDate::convertServerDateTo('m', $searchDateTime);
        $searchDay = PhonalisaDate::convertServerDateTo('d', $searchDateTime);

        $currentYear = PhonalisaDate::convertServerDateTo('Y', $this->currentServerTime);
        $currentMonth = PhonalisaDate::convertServerDateTo('m', $this->currentServerTime);
        $currentDay = PhonalisaDate::convertServerDateTo('d', $this->currentServerTime);

        $modelsToDelete = Statistic::model()->findAllByAttributes(
                array(
                    'target_type' => Statistic::StatisticTargetTypeSystemOverview,
                )
        );
        foreach ($modelsToDelete as $modelToDelete) {
            if ($modelToDelete->year === $currentYear && $modelToDelete->month === $currentMonth && $modelToDelete->day === $currentDay) {
                continue;
            }

            if ($modelToDelete->year < $searchYear) {
                $modelToDelete->delete();
            }

            if ($modelToDelete->month < $searchMonth && $modelToDelete->year == $searchYear) {
                $modelToDelete->delete();
            }

            if ($modelToDelete->day <= $searchDay && $modelToDelete->month == $searchMonth && $modelToDelete->year == $searchYear) {
                $modelToDelete->delete();
            }
        }
    }

    protected function informAdminAboutMemStatus($device = '', $usedPercent = '')
    {
        if (!empty($device) && !empty($usedPercent)) {
            $alertDevices = array(
                MemoryInfo::MOUNT_POINT_DB,
                MemoryInfo::MOUNT_POINT_ROOT,
                MemoryInfo::MOUNT_POINT_SD_CARD
            );

            if (!in_array($device, $alertDevices)) {
                return;
            }

            $level = '';
            $shortDescrText = 'SYSTEM_STATISTIC_CRON_JOB_MEM_ALERT_SHORT_DESCR';
            $descrText = 'SYSTEM_STATISTIC_CRON_JOB_MEM_ALERT_DESCR';
            $alertPercent = '';
            $usedPercent = (int) str_replace('%', '', $usedPercent);

            if ($usedPercent < self::ALERT_MEM_PERCENT_WARNING) {
                return;
            } else if ($usedPercent >= self::ALERT_MEM_PERCENT_WARNING && $usedPercent < self::ALERT_MEM_PERCENT_ERROR) {
                $level = Alert::LEVEL_WARNING;
                $alertPercent = self::ALERT_MEM_PERCENT_WARNING;
            } else if ($usedPercent >= self::ALERT_MEM_PERCENT_ERROR && $usedPercent < self::ALERT_MEM_PERCENT_CRITICAL) {
                $level = Alert::LEVEL_ERROR;
                $alertPercent = self::ALERT_MEM_PERCENT_ERROR;
            } else if ($usedPercent >= self::ALERT_MEM_PERCENT_CRITICAL) {
                $level = Alert::LEVEL_CRITICAL;
                $alertPercent = self::ALERT_MEM_PERCENT_CRITICAL;
            }
            $shortDescr = __($shortDescrText, true, 'messages', array("{percent}" => $alertPercent));
            $descr = __($descrText, true, 'messages', array("{device}" => $device, "{percent}" => $usedPercent));

            $this->createAlert($level, $shortDescr, $descr);
        }
    }

    protected function informAdminAboutLoadStatus($load, $loadValue)
    {
        if ($load !== '' && $loadValue !== '' && $loadValue !== '?') {
            $level = '';
            $shortDescrText = 'SYSTEM_STATISTIC_CRON_JOB_LOAD_ALERT_SHORT_DESCR';
            $descrText = 'SYSTEM_STATISTIC_CRON_JOB_LOAD_ALERT_DESCR';
            $explLoadValue = explode('.', $loadValue);
            $proccessPerMin = (int) $explLoadValue[0];

            if ($proccessPerMin < self::ALERT_LOAD_PROCCESS_WARNING) {
                return;
            } else if ($proccessPerMin >= self::ALERT_LOAD_PROCCESS_WARNING && $proccessPerMin < self::ALERT_LOAD_PROCCESS_ERROR) {
                $level = Alert::LEVEL_WARNING;
            } else if ($proccessPerMin >= self::ALERT_LOAD_PROCCESS_ERROR && $proccessPerMin < self::ALERT_LOAD_PROCCESS_CRITICAL) {
                $level = Alert::LEVEL_ERROR;
            } else if ($proccessPerMin >= self::ALERT_LOAD_PROCCESS_CRITICAL && $proccessPerMin < self::ALERT_LOAD_PROCCESS_FATAL) {
                $level = Alert::LEVEL_CRITICAL;
            } else if ($proccessPerMin >= self::ALERT_LOAD_PROCCESS_FATAL) {
                $level = Alert::LEVEL_FATAL;
            }
            $shortDescr = __($shortDescrText, true, 'messages', array("{perMin}" => $proccessPerMin, "{proccess}" => $load));
            $descr = __($descrText, true, 'messages', array("{perMin}" => $proccessPerMin, "{proccess}" => $load));
            $this->createAlert($level, $shortDescr, $descr);
        }
    }

    protected function createAlert($level, $shortDescr, $descr)
    {
        if ($level) {

            $oldAlert = Alert::model()->findByAttributes(array('level' => $level, 'shortDescr' => $shortDescr));
            if ($oldAlert) {
                return;
            }
            $alert = new Alert();
            $alert->created = PhonalisaDate::newServerDate();
            $alert->level = $level;
            $alert->component = Alert::COMPONENT_SYSTEM;
            $alert->shortDescr = $shortDescr;
            $alert->descr = $descr;
            if (!$alert->save()) {
                Log::e('Could not save Alert-Model!', 'SystemStatisticCronJob');
            }
        }
    }
}
