<?php
namespace local_treestudyplan;

use local_treestudyplan\local\helpers\webservicehelper;

require_once($CFG->libdir.'/externallib.php');

class associationservice extends \external_api 
{
    const CAP_EDIT = "local/treestudyplan:editstudyplan";
    const CAP_VIEW = "local/treestudyplan:viewuserreports";

    public static function user_structure(){
        return new \external_single_structure([
            "id" => new \external_value(PARAM_INT, 'user id'),
            "username" => new \external_value(PARAM_TEXT, 'username'),
            "firstname" => new \external_value(PARAM_TEXT, 'first name'),
            "lastname" => new \external_value(PARAM_TEXT, 'last name'),
            "idnumber" => new \external_value(PARAM_TEXT, 'id number'),
            "email" => new \external_value(PARAM_TEXT, 'email address'),
        ]);
    }

    public static function make_user_model($r){
        return [
            "id" => $r->id,
            "username" => $r->username,
            "firstname" => $r->firstname,
            "lastname" => $r->lastname,
            "idnumber" => $r->idnumber,
            "email" => $r->email,
        ];
    }

    public static function cohort_structure(){
        return new \external_single_structure([
            "id" => new \external_value(PARAM_INT, 'cohort id'),
            "name" => new \external_value(PARAM_TEXT, 'name'),
            "idnumber" => new \external_value(PARAM_TEXT, 'id number'),
            "description" => new \external_value(PARAM_TEXT, 'description'),
            "visible" => new \external_value(PARAM_BOOL, 'is visible'),
            "context" => new \external_single_structure([
                "name" => new \external_value(PARAM_TEXT, 'context name'),
                "shortname" => new \external_value(PARAM_TEXT, 'context short name'),
                "path" => new \external_multiple_structure( new \external_value(PARAM_TEXT)),
                "shortpath" => new \external_multiple_structure( new \external_value(PARAM_TEXT)),
            ], 'context information', VALUE_OPTIONAL),
        ]);
    }

    public static function make_cohort_model($r){
        global $DB;

        $ctx = \context::instance_by_id($r->contextid);
        $ctxPath = array_reverse($ctx->get_parent_context_ids(true));
        if(count($ctxPath) > 1 && $ctxPath[0] == 1) {
            array_shift($ctxPath);
        }
        
        $result = [
            "id" => $r->id,
            "name" => $r->name,
            "idnumber" => $r->idnumber,
            "description" => $r->description,
            "visible" => $r->visible,
            "context" => [
                "name" => $ctx->get_context_name(false,false),
                "shortname" => $ctx->get_context_name(false,true),
                "path" => array_map(function($c){ return \context::instance_by_id($c)->get_context_name(false,false);},$ctxPath),
                "shortpath" => array_map(function($c){ return \context::instance_by_id($c)->get_context_name(false,true);},$ctxPath),
            ]
        ]; 

        return $result;
        
    }    


    public static function list_cohort_parameters() 
	{
        return new \external_function_parameters( [ 
            'like' => new \external_value(PARAM_TEXT, 'search text', VALUE_OPTIONAL),
            'exclude_id' => new \external_value(PARAM_INT, 'exclude members of this studyplan', VALUE_OPTIONAL),
            'context_ic' => new \external_value(PARAM_INT, 'context for this request', VALUE_OPTIONAL),
        ] );
    }

	public static function list_cohort_returns() 
	{
        return new \external_multiple_structure(self::cohort_structure());
    }

	// Actual functions
	public static function list_cohort($like='',$exclude_id=null,$context_id=1) 
	{
        global $CFG, $DB;

        // Only allow this if the user has the right to edit in this context
        $context = webservicehelper::find_context($context_id);
        webservicehelper::require_capabilities(self::CAP_EDIT, $context);

        $pattern = "%{$like}%";

        $params = ["pattern_nm" => $pattern,"pattern_id" => $pattern,];

        $sql =  "SELECT c.* from {cohort} c LEFT JOIN {local_treestudyplan_cohort} j ON c.id = j.cohort_id";
        $sql .= " WHERE c.visible = 1 AND(name LIKE :pattern_nm OR idnumber LIKE :pattern_id)";
        if(isset($exclude_id) && is_numeric($exclude_id)){
            $sql .= " AND  (j.studyplan_id IS NULL OR j.studyplan_id != :exclude_id)";
            $params['exclude_id'] = $exclude_id;
        }
        if($context_id > 1){ // system context returns all cohorts, including system cohorts
            // otherwise, 
            $sql .= " AND contextid = :context_id";
            $params['context_id'] = $context_id;
        }

        $cohorts = [];
        $rs = $DB->get_recordset_sql($sql,$params);
        foreach ($rs as $r) {
            $cohorts[] = static::make_cohort_model($r);
        }
        $rs->close();
        return $cohorts;
    }

    public static function find_user_parameters() 
	{
        return new \external_function_parameters( [ 
            'like' => new \external_value(PARAM_TEXT, 'search text'),
            'exclude_id' => new \external_value(PARAM_INT, 'exclude members of this studyplan', VALUE_OPTIONAL),
            'context_id' => new \external_value(PARAM_INT, 'context for this request', VALUE_OPTIONAL),
        ] );
    }

	public static function find_user_returns() 
	{
        return new \external_multiple_structure(self::user_structure());
    }

	// Actual functions
	public static function find_user($like,$exclude_id=null,$context_id=1) 
	{
		global $CFG, $DB;

        // Only allow this if the user has the right to edit in this context (using system rights would make things more confusing)
        $context = webservicehelper::find_context($context_id);
        webservicehelper::require_capabilities(self::CAP_EDIT,$context);

        $pattern = "%{$like}%";
        $params = ["pattern_fn" => $pattern,
                   "pattern_ln" => $pattern,
                   "pattern_un" => $pattern,
                  ];
        $sql =  "SELECT u.* from {user} u LEFT JOIN {local_treestudyplan_user} j ON u.id = j.user_id";
        $sql .= " WHERE u.deleted != 1 AND (firstname LIKE :pattern_fn OR lastname LIKE :pattern_ln OR username LIKE :pattern_un)";
        if(isset($exclude_id) && is_numeric($exclude_id)){
            $sql .= " AND  (j.studyplan_id IS NULL OR j.studyplan_id != :exclude_id)";
            $params['exclude_id'] = $exclude_id;
        }        

        $users = [];
        $rs = $DB->get_recordset_sql($sql, $params);
        foreach ($rs as $r) {
            $users[] = static::make_user_model($r);
        }
        $rs->close();

        self::sortusermodels($users);
        return $users;
    }

    public static function connect_cohort_parameters() 
	{
        return new \external_function_parameters( [
            "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL),
            "cohort_id" => new \external_value(PARAM_INT, 'id of cohort to link', VALUE_OPTIONAL),
        ] );
    }

	public static function connect_cohort_returns() 
	{
        return new \external_single_structure([
            "success" => new \external_value(PARAM_BOOL, 'operation completed succesfully'),
            "msg" => new \external_value(PARAM_TEXT, 'message'),
        ]);
    }

	// Actual functions
	public static function connect_cohort($studyplan_id,$cohort_id) 
	{
        global $CFG, $DB;

        $studyplan = studyplan::findById($studyplan_id);
        webservicehelper::require_capabilities(self::CAP_EDIT,$studyplan->context());

        if(!$DB->record_exists('local_treestudyplan_cohort', ['studyplan_id' => $studyplan_id, 'cohort_id' => $cohort_id]))
        {
            $id = $DB->insert_record('local_treestudyplan_cohort', [
                'studyplan_id' => $studyplan_id, 
                'cohort_id' => $cohort_id,
            ]);

            $studyplan->mark_csync_changed();
            return ['success' => true, 'msg'=>'Cohort connected'];

        } else {
            return ['success' => true, 'msg'=>'Cohort already connected'];
        }

    }

    public static function disconnect_cohort_parameters() 
	{
        return new \external_function_parameters( [
            "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL),
            "cohort_id" => new \external_value(PARAM_INT, 'id of cohort to link', VALUE_OPTIONAL),
        ] );
    }

	public static function disconnect_cohort_returns() 
	{
        return new \external_single_structure([
            "success" => new \external_value(PARAM_BOOL, 'operation completed succesfully'),
            "msg" => new \external_value(PARAM_TEXT, 'message'),
        ]);
    }

	// Actual functions
	public static function disconnect_cohort($studyplan_id,$cohort_id) 
	{
		global $CFG, $DB;

        $studyplan = studyplan::findById($studyplan_id);
        webservicehelper::require_capabilities(self::CAP_EDIT,$studyplan->context());

        if($DB->record_exists('local_treestudyplan_cohort', ['studyplan_id' => $studyplan_id, 'cohort_id' => $cohort_id]))
        {
            $DB->delete_records('local_treestudyplan_cohort', [
                'studyplan_id' => $studyplan_id, 
                'cohort_id' => $cohort_id,
            ]);
            
            $studyplan->mark_csync_changed();

            return ['success' => true, 'msg'=>'Cohort Disconnected'];
        } else {
            return ['success' => true, 'msg'=>'Connection does not exist'];
        }

    }    

    public static function connect_user_parameters() 
	{
        return new \external_function_parameters( [
            "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL),
            "user_id" => new \external_value(PARAM_INT, 'id of user to link', VALUE_OPTIONAL),
        ] );
    }

	public static function connect_user_returns() 
	{
        return new \external_single_structure([
            "success" => new \external_value(PARAM_BOOL, 'operation completed succesfully'),
            "msg" => new \external_value(PARAM_TEXT, 'message'),
        ]);
    }

	// Actual functions
	public static function connect_user($studyplan_id,$user_id) 
	{
		global $CFG, $DB;
        
        $studyplan = studyplan::findById($studyplan_id);
        webservicehelper::require_capabilities(self::CAP_EDIT,$studyplan->context());
        
        if(!$DB->record_exists('local_treestudyplan_user', ['studyplan_id' => $studyplan_id, 'user_id' => $user_id]))
        {
            $id = $DB->insert_record('local_treestudyplan_user', [
                'studyplan_id' => $studyplan_id, 
                'user_id' => $user_id,
            ]);
            $studyplan->mark_csync_changed();

            return ['success' => true, 'msg'=>'Cohort connected'];

        } else {
            return ['success' => true, 'msg'=>'Cohort already connected'];
        }
    }

    public static function disconnect_user_parameters() 
	{
        return new \external_function_parameters( [
            "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL),
            "user_id" => new \external_value(PARAM_INT, 'id of user to link', VALUE_OPTIONAL),
        ] );
    }

	public static function disconnect_user_returns() 
	{
        return new \external_single_structure([
            "success" => new \external_value(PARAM_BOOL, 'operation completed succesfully'),
            "msg" => new \external_value(PARAM_TEXT, 'message'),
        ]);
    }

	// Actual functions
	public static function disconnect_user($studyplan_id,$user_id) 
	{
		global $CFG, $DB;
        $studyplan = studyplan::findById($studyplan_id);
        webservicehelper::require_capabilities(self::CAP_EDIT,$studyplan->context());

        if($DB->record_exists('local_treestudyplan_user', ['studyplan_id' => $studyplan_id, 'user_id' => $user_id]))
        {
            $DB->delete_records('local_treestudyplan_user', [
                'studyplan_id' => $studyplan_id, 
                'user_id' => $user_id,
            ]);

            $studyplan->mark_csync_changed();

            return ['success' => true, 'msg'=>'User Disconnected'];
        } else {
            return ['success' => true, 'msg'=>'Connection does not exist'];
        }
    }

    public static function associated_users_parameters() 
	{
        return new \external_function_parameters( [
            "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL),
        ] );
    }

	public static function associated_users_returns() 
	{
        return new \external_multiple_structure(self::user_structure());
    }

	// Actual functions
	public static function associated_users($studyplan_id) 
	{
		global $CFG, $DB;
        $studyplan = studyplan::findById($studyplan_id);
        webservicehelper::require_capabilities(self::CAP_VIEW,$studyplan->context());

        $sql = "SELECT DISTINCT u.* FROM {user} u INNER JOIN {local_treestudyplan_user} j ON j.user_id = u.id";
        $sql .= " WHERE j.studyplan_id = :studyplan_id";
        $rs = $DB->get_recordset_sql($sql, ['studyplan_id' => $studyplan_id]);

        $users = [];
        foreach($rs as $u)
        {
            $users[] = self::make_user_model($u);
        }
        $rs->close();
        self::sortusermodels($users);
        return $users;
    }

    public static function associated_cohorts_parameters() 
	{
        return new \external_function_parameters( [
            "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL),
        ] );
    }

	public static function associated_cohorts_returns() 
	{
        return new \external_multiple_structure(self::cohort_structure());
    }

	// Actual functions
	public static function associated_cohorts($studyplan_id) 
	{
		global $CFG, $DB;
        $studyplan = studyplan::findById($studyplan_id);
        webservicehelper::require_capabilities(self::CAP_VIEW,$studyplan->context());

        $sql = "SELECT DISTINCT c.* FROM {cohort} c INNER JOIN {local_treestudyplan_cohort} j ON j.cohort_id = c.id";
        $sql .= " WHERE j.studyplan_id = :studyplan_id";
        $rs = $DB->get_recordset_sql($sql, ['studyplan_id' => $studyplan_id]);
        $cohorts = [];
        foreach($rs as $c)
        {
            $cohorts[] = self::make_cohort_model($c);
        }
        $rs->close();
        return $cohorts;
    }


    public static function all_associated_parameters() 
	{
        return new \external_function_parameters( [
            "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL),
        ] );
    }

	public static function all_associated_returns() 
	{
        return new \external_multiple_structure(self::user_structure());
    }

	// Actual functions
	public static function all_associated($studyplan_id) 
	{
		global $CFG, $DB;

        $studyplan = studyplan::findById($studyplan_id);
        webservicehelper::require_capabilities(self::CAP_VIEW,$studyplan->context());


        $users = [];
        // SQL JOIN script selecting all users that have a cohort linked to this studyplan 
        // or are directly linked
        $sql = "SELECT DISTINCT u.id, u.username, u.firstname, u.lastname, u.idnumber, u.email
                FROM {user} u 
                LEFT JOIN {cohort_members}              cm ON u.id = cm.userid
                LEFT JOIN {local_treestudyplan_cohort}  tc ON cm.cohortid = tc.cohort_id
                LEFT JOIN {local_treestudyplan_user}    tu ON u.id = tu.user_id
                WHERE tc.studyplan_id = {$studyplan_id} 
                   OR tu.studyplan_id = {$studyplan_id}
                ORDER BY u.lastname, u.firstname";
        $rs = $DB->get_recordset_sql($sql);

        foreach($rs as $u)
        {
            $users[] = self::make_user_model($u);
        }
        $rs->close();

        self::sortusermodels($users);
        return $users;
    }

    public static function sortusermodels(&$list){
        return usort($list,function($a,$b){
            $m= [];
            if(preg_match("/.*?([A-Z].*)/",$a['lastname'],$m)){
                $sort_ln_a = $m[1];
            } else {
                $sort_ln_a = $a['lastname'];
            }
            if(preg_match("/.*?([A-Z].*)/",$b['lastname'],$m)){
                $sort_ln_b = $m[1];
            } else {
                $sort_ln_b = $b['lastname'];
            }
            $cmp= $sort_ln_a <=> $sort_ln_b;
            return  ($cmp != 0)?$cmp:$a['firstname'] <=> $b['firstname'];
        });
    }

    public static function cascade_cohortsync_parameters() 
	{
        return new \external_function_parameters( [
            "studyplan_id" => new \external_value(PARAM_INT, 'id of studyplan', VALUE_OPTIONAL),
        ] );
    }

	public static function cascade_cohortsync_returns() 
	{
        return success::structure();
    }

	// Actual functions
	public static function cascade_cohortsync($studyplan_id) 
	{
        $studyplan = studyplan::findById($studyplan_id);
        webservicehelper::require_capabilities(self::CAP_EDIT,$studyplan->context());

        $enroller = new cascadecohortsync($studyplan);
        $enroller->sync();

        if(get_config("local_treestudyplan","csync_users")){
            $userenroller = new cascadeusersync($studyplan);
            $userenroller->sync();
        }
        $studyplan->clear_csync_changed(); // Clear the csync required flag

        return success::success()->model();

    }





}