<?php
/**
 * The model file of team module of ZenTaoPMS.
 * @copyright   Copyright 2009-2023 禅道软件（青岛）有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
 * @license     ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
 * @author      Yuting Wang <wangyuting@easycorp.ltd>
 * @package     team
 * @link        https://www.zentao.net
 */
class teamModel extends model
{
    /**
     * 获取团队名称列表。
     * Get team pairs.
     *
     * @param  string      $type
     * @param  string      $status
     * @param  string      $teamIDList
     * @access public
     * @return array|false
     */
    public function getPairs($type = '', $status = '', $teamIDList = '')
    {
        return $this->dao->select('id,name')->from(TABLE_TEAMGROUP)
            ->where('deleted')->eq('0')
            ->beginIF($type)->andWhere('type')->eq($type)->fi()
            ->beginIF($status)->andWhere('status')->eq($status)->fi()
            ->beginIF($teamIDList)->andWhere('id')->in($teamIDList)->fi()
            ->fetchPairs();
    }

    /**
     * 根据ID获取团队名称列表。
     * Get teams by idList.
     *
     * @param  string|array $idList
     * @access public
     * @return array
     */
    public function getByIdList($idList = ''): array
    {
        return $this->dao->select('*')->from(TABLE_TEAMGROUP)
            ->where('deleted')->eq('0')
            ->andWhere('id')->in($idList)
            ->fetchAll('id');
    }

    /**
     * 获取团队列表。
     * Get team list.
     *
     * @param  string     $browseType
     * @param  string     $orderBy
     * @param  object     $pager
     * @access public
     * @return array|false
     */
    public function getList($browseType = 'all', $orderBy = 'id_desc', $pager = null)
    {
        return $this->dao->select('*')->from(TABLE_TEAMGROUP)
            ->where('deleted')->eq('0')
            ->beginIF($browseType != 'all')->andWhere('status')->eq($browseType)->fi()
            ->orderBy($orderBy)
            ->page($pager)
            ->fetchAll('id');
    }

    /**
     * 获取团队成员。
     * Get team members.
     *
     * @param  int         $teamID
     * @access public
     * @return array|false
     */
    public function getTeamMembers($teamID)
    {
        return $this->dao->select('*')->from(TABLE_TEAM)
            ->where('root')->eq($teamID)
            ->andWhere('type')->eq('teamgroup')
            ->fetchAll('id');
    }

    /**
     * 获取团队成员键值对。
     * Get team members.
     *
     * @param  int         $teamID
     * @access public
     * @return array
     */
    public function getTeamMembersPairs($teamID): array
    {
        return $this->dao->select('account')->from(TABLE_TEAM)
            ->where('root')->eq($teamID)
            ->andWhere('type')->eq('teamgroup')
            ->fetchPairs();
    }

    /**
     * 根据团队ID获取团队成员，包括父团队。
     * Get team member list.
     *
     * @param  int         $teamID
     * @access public
     * @return array
     */
    public function getTeamMemberList($teamID): array
    {
        if(!$teamID) return array();

        $teamIdList = $this->getAllChild($teamID);

        $members = $this->dao->select('*')->from(TABLE_TEAM)
            ->where('root')->in($teamIdList)
            ->andWhere('type')->eq('teamgroup')
            ->fetchAll('account');

        return $members;
    }

    /**
     * 获取成员的团队列表。
     * Get member's team list.
     *
     * @param  int    $teamID
     * @access public
     * @return array
     */
    public function getMemberTeamList()
    {
        $memberTeamGroup = $this->dao->select('t1.id, t1.name, t2.account')->from(TABLE_TEAMGROUP)->alias('t1')
            ->leftJoin(TABLE_TEAM)->alias('t2')->on('t1.id = t2.root')
            ->where('t2.type')->eq('teamgroup')
            ->andWhere('t2.account')->ne('')
            ->andWhere('t1.status')->eq('enable')
            ->andWhere('t1.deleted')->eq('0')
            ->andWhere('t1.type')->eq('child')
            ->fetchGroup('account', 'id');

        $optionMenu = $this->getOptionMenu();
        foreach($memberTeamGroup as $account => $teamGroup)
        {
            $teams = array(0 => '/');
            foreach($teamGroup as $team)
            {
                $teams[$team->id] = zget($optionMenu, $team->id, $team->name);
            }

            $memberTeamGroup[$account] = $teams;
        }

        return $memberTeamGroup;
    }

    /**
     * 获取按照团队分组的用户列表。
     * get users group by team.
     *
     * @param  int    $teamID
     * @access public
     * @return array
     */
    public function getTeamUsers($teamID = 0): array
    {
        $teamUsers = array();
        $teams = $this->dao->select('*')->from(TABLE_TEAMGROUP)->where('parent')->eq($teamID)->andWhere('status')->eq('enable')->andWhere('deleted')->eq('0')->fetchAll('id');
        if(!$teams) return $teamUsers;

        static $users = '';
        if(!$users)
        {
            $users    = $this->loadModel('user')->getList();
            $userList = array();
            foreach($users as $user) $userList[$user->account] = $user;
            $users = $userList;
        }

        foreach($teams as $team)
        {
            $items = array();
            if($team->type == 'parent') $items = $this->getTeamUsers($team->id);
            if($team->type == 'child')
            {
                $userList = $this->getTeamMembers($team->id);
                foreach($userList as $user)
                {
                    $user = zget($users, $user->account, '');
                    if(!$user) continue;

                    $item = array('text' => $user->realname, 'value' => "$team->id-$user->account");
                    if($user->avatar) $item['image']    = $user->avatar;
                    if($user->role)   $item['subTitle'] = zget($this->lang->user->roleList, $user->role);
                    $items[] = $item;
                }
            }
            if(!$items) continue;

            $teamUser = array();
            $teamUser['value'] = $team->id;
            $teamUser['text']  = $team->name;
            $teamUser['items'] = $items;
            if($team->logo) $teamUser['image'] = $team->logo;

            $teamUsers[] = $teamUser;
        }
        return $teamUsers;
    }

    /**
     * 获取用户的所属团队。
     * Get teams by account.
     *
     * @param  string  $account
     * @access public
     * @return array
     */
    public function getByAccount($account): array
    {
        return $this->dao->select('root')->from(TABLE_TEAM)
            ->where('account')->eq($account)
            ->andWhere('type')->eq('teamgroup')
            ->fetchPairs();
    }

    /**
     * 获取各个团队的成员数量。
     * Get member count of each team.
     *
     * @param  array  $idList
     * @access public
     * @return array
     */
    public function getMemberCount($idList): array
    {
        $teamGroup = $this->dao->select('t1.id, t1.path, t2.account')->from(TABLE_TEAMGROUP)->alias('t1')
             ->leftJoin(TABLE_TEAM)->alias('t2')->on('t1.id = t2.root')
             ->where('t2.type')->eq('teamgroup')
             ->andWhere('t1.id')->in($idList)
             ->andWhere('t1.type')->eq('child')
             ->fetchGroup('path', 'account');

        $memberGroup = array();
        foreach($teamGroup as $path => $teamMembers)
        {
            $pathList = explode(',', $path);
            foreach($pathList as $id)
            {
                if(!$id) continue;

                foreach($teamMembers as $account => $team) $memberGroup[$id][$account] = $account;
            }
        }

        $memberCount = array();
        foreach($memberGroup as $id => $members) $memberCount[$id] = count($members);

        return $memberCount;
    }

    /**
     * 获取团队下拉菜单的树状结构。
     * Get option menu of team.
     *
     * @param  string $type
     * @param  string $status
     * @param  string $exclude
     * @access public
     * @return void
     */
    public function getOptionMenu($type = '', $status = '', $exclude = '')
    {
        $teams = $this->dao->select('*')->from(TABLE_TEAMGROUP)
            ->where('deleted')->eq('0')
            ->beginIF($status)->andWhere('status')->eq($status)->fi()
            ->beginIF($type)->andWhere('type')->eq($type)->fi()
            ->beginIF($exclude)->andWhere('id')->notin($exclude)->fi()
            ->orderBy('id_asc')
            ->fetchAll('id');

        return $this->buildOptionMenu($teams);
    }

    /**
     * 获取团队的所有子团队。
     * Get all child teams of a team.
     *
     * @param  int    $teamID
     * @access public
     * @return array
     */
    public function getAllChild($teamID): array
    {
        return $this->dao->select('id')->from(TABLE_TEAMGROUP)
            ->where("FIND_IN_SET($teamID, path)")
            ->andWhere('deleted')->eq('0')
            ->fetchPairs();
    }

    /**
     * 获取我所属的团队列表。
     * Get myy teams.
     *
     * @param  string      $account
     * @access public
     * @return array|false
     */
    public function getMyTeams($account = '')
    {
        if(!$account) $account = $this->app->user->account;

        return $this->dao->select('t1.id,t1.name')->from(TABLE_TEAMGROUP)->alias('t1')
            ->leftJoin(TABLE_TEAM)->alias('t2')->on('t1.id=t2.root')
            ->where('t2.type')->eq('teamgroup')
            ->andWhere('t2.account')->eq($account)
            ->andWhere('t1.deleted')->eq('0')
            ->andWhere('t1.status')->eq('enable')
            ->fetchPairs();
    }

    /*
     * 查找项目团队成员所属的已解散团队。
     * Get disbanded teams by project id.
     *
     * @param  int         $objectID
     * @param  string      $objectType
     * @access public
     * @return array|false
     */
    /**
     * @param int $objectID
     * @param string $objectType
     */
    public function getDisbandTeamsByProject($objectID, $objectType)
    {
        $teamGroup = $this->dao->select('*')->from(TABLE_TEAM)
            ->where('type')->eq($objectType)
            ->andWhere('root')->eq($objectID)
            ->fetchGroup('teamgroup');

        if(!$teamGroup) return false;

        $teams = array_keys($teamGroup);

        $disbandTeams = $this->dao->select('id,name')->from(TABLE_TEAMGROUP)
            ->where('id')->in($teams)
            ->andWhere('status')->eq('disband')
            ->fetchPairs();

        return $disbandTeams;
    }

    /**
     * 创建一个团队。
     * Create a team.
     *
     * @param  object    $team
     * @access public
     * @return int|false
     */
    public function create($team)
    {
        $this->dao->insert(TABLE_TEAMGROUP)->data($team)
            ->autoCheck()
            ->batchCheck($this->config->team->create->requiredFields, 'notempty')
            ->check('name', 'unique')
            ->exec();

        if(dao::isError()) return false;

        /* 保存历史记录。 */
        $teamID = $this->dao->lastInsertID();
        $this->loadModel('action')->create('teamgroup', $teamID, 'Opened');

        $files = $this->loadModel('file')->saveUpload('teamlogo', $teamID);
        if($files)
        {
            $file  = $this->file->getById(key($files));
            if($file) $this->dao->update(TABLE_TEAMGROUP)->set('logo')->eq($file->webPath)->where('id')->eq($teamID)->exec();
        }

        /* 更新Path. */
        $team->id = $teamID;
        $this->fixPath($team);

        return $teamID;
    }

    /**
     * 更新一个团队。
     * Update a team.
     *
     * @param  object $team
     * @access public
     * @return bool
     */
    public function update($team): bool
    {
        if(empty($team->id)) return false;

        $oldTeam = $this->fetchByID($team->id, 'teamgroup');

        $this->dao->update(TABLE_TEAMGROUP)->data($team)
            ->autoCheck()
            ->batchCheck($this->config->team->edit->requiredFields, 'notempty')
            ->check('name', 'unique', "id != $team->id")
            ->where('id')->eq($team->id)
            ->exec();

        if(dao::isError()) return false;

        /* 保存历史记录。 */
        $changes  = common::createChanges($oldTeam, $team);
        if($changes)
        {
            $actionID = $this->loadModel('action')->create('teamgroup', $team->id, 'Edited');
            $this->action->logHistory($actionID, $changes);
        }

        $files = $this->loadModel('file')->saveUpload('teamlogo', $team->id);
        if($files)
        {
            $file  = $this->file->getById(key($files));
            if($file) $this->dao->update(TABLE_TEAMGROUP)->set('logo')->eq($file->webPath)->where('id')->eq($team->id)->exec();
        }

        /* 更新Path. */
        $this->fixPath($team);

        return true;
    }

    /**
     * 添加团队成员。
     * Add team members.
     *
     * @param  int    $teamID
     * @param  array  $members
     * @access public
     * @return bool
     */
    public function addMember($teamID, $members): bool
    {
        $team = $this->teamTao->fetchByID($teamID, 'teamgroup');
        if(!$team) return false;

        /* 插入新成员。 */
        foreach($members as $account)
        {
            $member = new stdclass();
            $member->account = $account;
            $member->root    = $teamID;
            $member->type    = 'teamgroup';
            $member->join    = helper::today();

            $this->dao->insert(TABLE_TEAM)->data($member)->autoCheck()->exec();
        }

        if(dao::isError()) return false;

        $this->loadModel('action')->create('teamgroup', $teamID, 'addMember', '', implode(',', $members));

        return true;
    }

    /**
     * 删除团队成员。
     * Remove team members.
     *
     *  @param  int    $teamID
     *  @param  string $account
     *  @access public
     *  @return bool
     */
    public function removeMember($teamID, $account): bool
    {
        $this->dao->delete()->from(TABLE_TEAM)
            ->where('root')->eq($teamID)
            ->andWhere('type')->eq('teamgroup')
            ->andWhere('account')->eq($account)
            ->exec();

        if(dao::isError()) return false;

        $this->loadModel('action')->create('teamgroup', $teamID, 'removeMember', '', $account);

        return true;
    }

    /**
     * 更新团队的Path。
     * Update the path of a team.
     *
     * @param  object $team
     * @static
     * @access public
     * @return void
     */
    public function fixPath($team)
    {
        if($team->parent)
        {
            $parentTeam = $this->teamTao->fetchByID($team->parent, 'teamgroup');
            $path       = $parentTeam->path . $team->id . ',';
            $grade      = $parentTeam->grade + 1;
        }
        else
        {
            $path  = ',' . $team->id . ',';
            $grade = 1;
        }

        $this->dao->update(TABLE_TEAMGROUP)
             ->set('path')->eq($path)
             ->set('grade')->eq($grade)
             ->where('id')->eq($team->id)
             ->exec();
    }

    /**
     * 解散|启用一个团队。
     * Disband|Enable a team.
     *
     * @param  int    $teamID
     * @param  string $status
     * @static
     * @access public
     * @return array
     */
    public function changeStatus($teamID, $status = 'enable'): array
    {
        $oldTeam = $this->teamTao->fetchByID($teamID, 'teamgroup');

        $team = new stdclass();
        $team->status = $status;
        if($status == 'disband') $team->disbandedDate = helper::today();

        $this->dao->update(TABLE_TEAMGROUP)->data($team)->where('id')->eq($teamID)->exec();

        return common::createChanges($oldTeam, $team);
    }

    /**
     * 检查按钮是否允许被点击。
     * Check clickable.
     *
     * @param  object $object
     * @param  string $action
     * @param  string $module
     * @static
     * @access public
     * @return bool
     */
    public static function isClickable($object, $action, $module = 'team'): bool
    {
        $action = strtolower($action);

        if($object->deleted) return false;

        if($action == 'disband') return $object->status == 'enable';
        if($action == 'enable')  return $object->status == 'disband';

        return true;
    }

    /**
     * 判断团队或团队下的成员是否关联过项目、执行。
     * Judge if the team member has linked project or execution.
     *
     * @param  int    $teamID
     * @param  string $account
     * @access public
     * @return bool
     */
    public function hasLinkedProject($teamID, $account = ''): bool
    {
        $teamID = $this->dao->select('t1.id')->from(TABLE_TEAM)->alias('t1')
            ->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.root = t2.id')
            ->where('t1.teamgroup')->eq($teamID)
            ->andWhere('t1.type')->in('execution,project')
            ->andWhere('t2.status')->ne('closed')
            ->beginIF($account)->andWhere('t1.account')->eq($account)->fi()
            ->fetch('id');

        return $teamID > 0;
    }

    /**
     * 判断团队是否关联过ART、PI。
     * Judge if the team has linked ART or PI.
     *
     * @param  int    $teamID
     * @access public
     * @return bool
     */
    public function hasLinkedART($teamID): bool
    {
        $ART = $this->dao->select('id')->from(TABLE_ART)
            ->where("find_in_set($teamID, team)")
            ->andWhere('status')->ne('closed')
            ->fetch('id');

        $PI = $this->dao->select('id')->from(TABLE_PI)
            ->where("find_in_set($teamID, team)")
            ->andWhere('status')->ne('closed')
            ->fetch('id');

        return ($ART > 0) or ($PI > 0) ;
    }

    /**
     * 判断团队是否在项目、执行中消耗过工时。
     * Judge if the team has consumed hours in project or execution.
     *
     * @param  int    $teamID
     * @access public
     * @return bool
     */
    public function hasConsumed($teamID): bool
    {
        /* 查询当前团队的人。 */
        $members = $this->dao->select('account')->from(TABLE_TEAM)
             ->where('root')->eq($teamID)
             ->andWhere('type')->eq('teamgroup')
             ->fetchPairs();

        if(empty($members)) return false;

        /* 查询当前团队关联的项目、执行。 */
        $projects = $this->dao->select('root')->from(TABLE_TEAM)
             ->where('teamgroup')->eq($teamID)
             ->andWhere('type')->in('project,execution')
             ->fetchPairs();

        if(empty($projects)) return false;

        /* 查询当前团队中的人是否在项目、执行下消耗过工时。 */
        $hasConsumed = $this->dao->select('consumed')->from(TABLE_EFFORT)
            ->where('account')->in($members)
            ->andWhere('(project')->in($projects)
            ->orWhere('execution')->in($projects)
            ->markRight(true)
            ->fetch();

        return !empty($hasConsumed);
    }
}
