<?php

namespace Velis\User;

use User\User;
use Velis\App;
use Velis\User\Absence\Type;
use Velis\Model\DataObject;
use Velis\Arrays;
use Velis\Lang;
use Velis\Filter;
use DateTime;

/**
 * Registered user absence
 * @author Olek Procki <olo@velis.pl>
 */
class Absence extends DataObject
{

    const ORDER_DATE = 'date_from';


    /**
     * Filter list params by default
     * @var bool
     */
    protected static $_filterListParams = true;


    /**
     * List default order
     * @var string
     */
    protected static $_listDefaultOrder = self::ORDER_DATE;


    /**
     * If conflicts are checked
     * @var int
     */
    protected $_conflictsChecked;

    /**
     * @return string
     */
    protected function _getTableName()
    {
        return 'acl.user_absence_tab';
    }


    /**
     * @return string
     */
    protected function _getListDataSource()
    {
        return 'acl.user_absence ua';
    }


    /**
     * @return integer
     */
    protected function _getVacationTypeId()
    {
        return App::settings('HolidayAbsenceId');
    }


    /**
     * Returns absences list
     *
     * @param int $page
     * @param array|ArrayObject $params
     * @param string $order
     * @param int $limit
     * @param string|array $fields
     *
     * @return \Velis\User\Absence[]
     */
    public static function getList($page = 1, $params = null, $order = null, $limit = self::ITEMS_PER_PAGE, $fields = null)
    {
        if ($params['description']) {
            self::$_listConditions[] = "(description ILIKE :description)";
            self::$_listParams['description'] = '%' . trim($params['description'], '%') . '%';
            unset($params['description']);
        }

        if (!$params['archive']) {
            self::$_listConditions[] = "date_to >= NOW()";
        } else {
            if ($order == null) {
                $order = 'date_from DESC';
            }
        }

        if (strlen($params['is_private'])) {
            self::$_listConditions[] = "is_private = :is_private";
            self::$_listParams['is_private'] = Filter::filterInt($params['is_private']);

            unset($params['is_private']);
        }

        if ($params['toAccept']) {
            self::$_listConditions[] = "is_private = 1";
            self::$_listConditions[] = "is_accepted = 0";

            unset($params['is_private']);
        }

        if ($params['date_from']) {
            if ($params['h_from']) {
                $date_from = $params['date_from'] . ' ' . str_replace('T', '', $params['h_from']);
            } else {
                $date_from = $params['date_from'];
            }
            self::$_listParams['date_from'] = $date_from;
            unset($params['date_from']);
        }

        if ($params['date_to']) {
            if ($params['h_to']) {
                $date_to = $params['date_to'] . ' ' . str_replace('T', '', $params['h_to']);
            } else {
                $date_to = $params['date_to'];
            }
            self::$_listParams['date_to'] = $date_to;
            unset($params['date_to']);
        }

        if ($date_from && $date_to) {
            self::$_listConditions[] = "((date_from::date,date_to::date) OVERLAPS (:date_from::timestamp without time zone,:date_to::timestamp without time zone + INTERVAL '1 day'))";
        } else {
            if ($date_from) {
                self::$_listConditions[] = "date_from >= :date_from";
            }

            if ($date_to) {
                self::$_listConditions[] = "date_to <= :date_to";
            }
        }

        if ($params['user_id']) {
            if (is_array($params['user_id'])) {
                 $userIds = implode(', ', array_filter($params['user_id']));
            } else {
                 $userIds = $params['user_id'];
            }

            if ($userIds) {
                self::$_listConditions[] = 'user_id IN(' . $userIds . ')';
            }
            unset($params['user_id']);
        }

        if ($params['is_current']) {
            self::$_listConditions[] = 'now() BETWEEN date_from AND date_to';
        }

        return parent::getList($page, $params, $order, $limit, $fields);
    }


    /**
     * Validates absence dates
     * @throws \LogicException
     */
    protected function _checkConflict()
    {
        if ($this->_conflictsChecked) {
            return $this;
        }

        $query = "SELECT COUNT(*) FROM acl.user_absence_tab
                    WHERE user_id=:user_id
                      AND user_absence_id != COALESCE(:user_absence_id,0)
                      AND (:date_from BETWEEN date_from AND date_to
                           OR :date_to BETWEEN date_from AND date_to
                           OR (:date_from < date_from AND :date_to > date_to))";

        $params = Arrays::extractFields($this, array('user_id', 'user_absence_id', 'date_from', 'date_to'));

        if (!$this->id()) {
            $params['user_absence_id'] = null;
        }
        if (self::$_db->getOne($query, $params)) {
            throw new \LogicException(Lang::get('USER_ABSENCE_DATE_OVERLAPS'));
        }
        $this->_conflictsChecked = 1;
        return $this;
    }


    /**
     * Returns user absence stats
     *
     * @param \User\User $user
     * @param int $year
     *
     * @return array
     */
    public static function getUserStats($user, $year = null)
    {
        $result = self::$_db->getAll(
            "SELECT
                    user_id,
                    user_absence_id,
                    user_absence_type_id,
                    is_private,
                    date_from,
                    date_to,
                    color,
                    name,
                    (sum( (date_part('hour', absence_hours)*60) + date_part('minute', absence_hours)))::integer as absence_minutes_used
              FROM acl.user_absence_used uau
              WHERE user_id = :user_id AND :year = date_part('year', day)
              GROUP BY user_absence_id, user_id, is_private, color, name, date_from, date_to, user_absence_type_id
              ORDER BY user_id ASC",
            array (
                'user_id' => $user instanceof \Velis\User ? $user->id() : $user,
                'year'    => $year ? $year : date("Y"),
            )
        );
        $list = array();

        foreach ($result as $row) {
            if (isset($list[$row['user_absence_type_id']])) {
                $row['absence_minutes_used'] = $list[$row['user_absence_type_id']]['absence_minutes_used'] + $row['absence_minutes_used'];
            }
            $list[$row['user_absence_type_id']] = $row;
        }

        return $list;
    }


    /**
     * Adds new absence
     *
     * @param bool $updateObjectId
     * @return \Velis\User\Absence
     */
    public function add($updateObjectId = true)
    {
        $this->_checkConflict();

        self::$_db->startTrans();

        try {
            User::unsetListCache();
            parent::add($updateObjectId);
            $this->_conflictsChecked = 0;

            $user = $this->getUser();
            $params = $this->getArrayCopy();

            if ($params['user_absence_type_id'] == Absence::_getVacationTypeId()  && !empty($user['vested_vacation_days'])) {
                $userStats =  self::getUserStats($user);
                $userHolidayStats = Arrays::byValue($userStats, array('user_absence_type_id' => Absence::_getVacationTypeId()));

                if ($userHolidayStats) {
                    foreach ($userHolidayStats as $id => $item) {
                         $workingDays = ($item['absence_minutes_used'] / 60) / 8 ;
                    }
                } else {
                    $workingDays = 0;
                }

                  $userVacationDays = $user['vested_vacation_days'] + $user['overdue_vacation_days'] - $workingDays ;

                if ($userVacationDays  < 0) {
                      self::$_db->rollback();
                      throw new \LogicException(Lang::get('GENERAL_ERROR') . " " . Lang::get('USER_ABSENCE_NO_VACATION_DAY'));
                } else {
                       self::$_db->commit();
                       return true;
                }
            } else {
                self::$_db->commit();
                return true;
            }
        } catch (Exception $e) {
            $this->_conflictsChecked = 0;
            self::$_db->rollback();
            throw $e;
        }
    }


    /**
     * Modifies user absence
     *
     * @param bool $checkDiff
     * @return \Velis\User\Absence
     */
    public function modify($checkDiff = false)
    {
        $this->_checkConflict();

        User::unsetListCache();
        $this->_conflictsChecked = 0;
        return parent::modify($checkDiff);
    }


    /**
     * Removes user absence
     * @return bool
     */
    public function remove()
    {
        User::unsetListCache();
        return parent::_remove();
    }


    /**
     * Returns related user
     * @return \Velis\User
     */
    public function getUser()
    {
        return User::get($this['user_id']);
    }

    /**
     * Returns hash to assept/remove
     * @return string
     */
    public function getHash()
    {
        return sha1($this['user_id'] . 'troche przypraw' . $this->id());
    }


    /**
     * Returns substitute user
     * @return Velis_User
     */
    public function getSubstitute()
    {
        return User::get($this['substitute_user_id']);
    }


    /**
     * Returns type
     * @return \Velis\User\Absence\Type
     */
    public function getType()
    {
        $types = Type::listCached();
        return $types[$this['user_absence_type_id']];
    }
}
