//database checked
func generateAddModule(id, userId, jwt string, officers, assistants map[string]bool, op *jsonpatch.Operation, params map[string]string) (*jsonpatch.CommandContainer, error) {
	if err := checkAuthorityAndValidatePatch(jsonpatch.ADD, op.Type, userId, officers); err != nil {
		log.Println("Patch not acceptable: ", err)
		return nil, err
	}
	value := op.Value.(map[string]interface{})
	command := buildDefaultCommand("SELECT add_module(%v)", value["id"].(string), id, value["description"].(string), value["video_id"].(string), value["script_id"].(string), value["parents"])
	command.AfterCallback = func(transaction, prev interface{}) (interface{}, error) {
		client := serviceclient.New("acl-service")
		entity := map[string]interface{}{
			"id":     value["id"].(string),
			"parent": id,
		}
		jsonEntity, _ := json.Marshal(entity)
		err := checkStatus(client.Post("/objects", "json", bytes.NewReader(jsonEntity), "Authorization", jwt))
		if err != nil {
			log.Println("error while creating acl entry: ", err)
			return nil, err
		}
		if len(assistants) > 0 {
			assistantsString := ""
			for k, _ := range assistants {
				assistantsString = assistantsString + "&sid=" + k
			}
			assistantsString = strings.TrimLeft(assistantsString, "&")
			permissions, _ := json.Marshal(map[string]bool{"read_permission": true, "create_permission": true, "update_permission": false, "delete_permission": false})
			return nil, checkStatus(client.Put(fmt.Sprintf("/objects/%s/permissions?%s", value["id"], assistantsString), "application/json", bytes.NewReader(permissions), "Authorization", jwt))
		}
		return nil, nil
	}
	return command, nil
}
func TopicCreateHandler(mapper *pgmapper.Mapper) http.Handler {
	handlerFunc := func(w http.ResponseWriter, r *http.Request) int {
		var topic = make(map[string]interface{})
		err := json.NewDecoder(r.Body).Decode(&topic)
		if err != nil {
			log.Println("error while decoding new topic json: ", err)
			return http.StatusBadRequest
		}
		err = mapper.Execute("SELECT add_topic(%v)", topic["id"], topic["name"], topic["description"], topic["officers"])
		if err != nil {
			log.Println("error while inserting new topic into database")
			return http.StatusBadRequest // TODO it could be an internal server error as well. need distinction
		}
		client := serviceclient.New("acl-service")
		aclEntity, _ := json.Marshal(topic)
		resp, err := client.Post("/objects", "application/json", bytes.NewReader(aclEntity), "Authorization", r.Header.Get("Authorization"))
		if err != nil {
			log.Println("error while creating acl-object: ", err)
			return http.StatusInternalServerError
		}
		if resp.StatusCode >= 300 {
			log.Println("got unexpected statuscode from acl-service while creating object: ", resp.StatusCode)
			return http.StatusInternalServerError
		}
		return http.StatusCreated
	}
	return jwtware.New(createHandler(handlerFunc))
}
func Service(serviceName string, handler http.Handler) (*httptest.Server, *serviceclient.ServiceClient) {
	server := httptest.NewServer(handler)
	serviceclient.ResolverFactory = func() serviceclient.AddressResolver {
		return MockDnsResolver{server.URL}
	}
	client := serviceclient.New(serviceName)
	return server, client
}
//database checked
func generateRemoveExercise(id, userId string, officers, assistants map[string]bool, op *jsonpatch.Operation, params map[string]string) (*jsonpatch.CommandContainer, error) {
	if err := checkAuthorityAndValidatePatch(jsonpatch.REMOVE, op.Type, userId, officers, assistants); err != nil {
		return nil, err
	}
	command := buildDefaultCommand("SELECT remove_exercise(%v)", id, params["exerciseId"])
	command.AfterCallback = func(transaction, prev interface{}) (interface{}, error) {
		return nil, checkStatus(serviceclient.New("acl-service").Delete("/objects/" + params["exerciseId"]))
	}
	return command, nil
}
//database checked
func generateRemoveModule(id, userId, jwt string, officers, assistants map[string]bool, op *jsonpatch.Operation, params map[string]string) (*jsonpatch.CommandContainer, error) {
	if err := checkAuthorityAndValidatePatch(jsonpatch.REMOVE, op.Type, userId, officers); err != nil {
		return nil, err
	}
	command := buildDefaultCommand("SELECT remove_module(%v)", id, params["moduleId"])
	command.AfterCallback = func(transaction, prev interface{}) (interface{}, error) {
		client := serviceclient.New("acl-service")
		return nil, checkStatus(client.Delete(fmt.Sprintf("/objects/%s", params["moduleId"]), "Authorization", jwt))
	}
	return command, nil
}
//database checked
func generateAddExercise(id, userId string, officers, assistants map[string]bool, op *jsonpatch.Operation, params map[string]string) (*jsonpatch.CommandContainer, error) {
	if err := checkAuthorityAndValidatePatch(jsonpatch.ADD, op.Type, userId, officers, assistants); err != nil {
		return nil, err
	}
	value := op.Value.(map[string]interface{})
	command := buildDefaultCommand("SELECT add_exercise(%v)", value["id"], id, value["backend"])
	command.AfterCallback = func(transaction, prev interface{}) (interface{}, error) {
		return nil, checkStatus(serviceclient.New("acl-service").Post("/objects", "json", strings.NewReader(value["id"].(string))))
	}
	return command, nil
}
func defaultFetcher(id string, sid string) (result map[string]interface{}, err error) {
	instance := serviceclient.New("acl-service")
	var resp *http.Response
	resp, err = instance.Get(fmt.Sprintf("/objects/%s/permissions/%s", id, sid))
	if err != nil {
		return
	}
	defer resp.Body.Close()
	decoder := json.NewDecoder(resp.Body)
	err = decoder.Decode(&result)
	return
}
//database checked
func generateAddExercise(id, userId, jwt string, officers, assistants map[string]bool, op *jsonpatch.Operation, params map[string]string) (*jsonpatch.CommandContainer, error) {
	log.Printf("Got the following userId: %s  and the follwoing authority: %v , %v", userId, officers, assistants)
	if err := checkAuthorityAndValidatePatch(jsonpatch.ADD, op.Type, userId, officers, assistants); err != nil {
		return nil, err
	}
	value := op.Value.(map[string]interface{})
	command := buildDefaultCommand("SELECT add_exercise(%v)", value["id"], id, value["backend"])
	command.AfterCallback = func(transaction, prev interface{}) (interface{}, error) {
		jsonBytes, _ := json.Marshal(value)
		return nil, checkStatus(serviceclient.New("acl-service").Post("/objects", "json", bytes.NewReader(jsonBytes), "Authorization", jwt))
	}
	return command, nil
}
func SecretHandler(token *jwt.Token) (interface{}, error) {
	if secret == nil {
		serviceInstance := serviceclient.New("authentication-service")
		resp, err := serviceInstance.Get("/oauth/token_key")
		if err != nil {
			log.Println("error occured while requesting key: ", err)
			return nil, err
		}
		defer resp.Body.Close()
		result := make(map[string]interface{})
		err = json.NewDecoder(resp.Body).Decode(&result)
		if err != nil {
			log.Println("error occured while reading key: ", err)
			return nil, err
		}
		secret = []byte(result["value"].(string))
	}
	return secret, nil
}
//database checked
func generateRemoveModuleTree(id, userId, jwt string, officers, assistants map[string]bool, op *jsonpatch.Operation, params map[string]string) (*jsonpatch.CommandContainer, error) {
	if err := checkAuthorityAndValidatePatch(jsonpatch.REMOVE, op.Type, userId, officers); err != nil {
		return nil, err
	}
	command := new(jsonpatch.CommandContainer)
	command.MainCallback = func(transaction, prev interface{}) (interface{}, error) {
		row := transaction.(*sql.Tx).QueryRow("SELECT string_agg(id::varchar,'&oid=') from remove_module_tree($1,$2) group by id", id, params["moduleId"])
		var val string
		err := row.Scan(&val)
		if err != nil {
			return nil, err
		}
		return val, nil
	}
	command.AfterCallback = func(transaction, prev interface{}) (interface{}, error) {
		client := serviceclient.New("acl-service")
		return nil, checkStatus(client.Delete(fmt.Sprintf("/objects?oid=%s", prev.(string)), "Authorization", jwt))
	}
	return command, nil
}
func setPermissions(topicId, userId, jwt string, txn *sql.Tx, permissions map[string]bool) error {
	stmt := `SELECT string_agg(id::varchar, $1) from modules where topic_id = $2 GROUP BY id`
	row := txn.QueryRow(stmt, "&oid=", topicId)
	oids := ""
	err := row.Scan(&oids)
	if err != nil {
		return nil
	}
	oids = "oid=" + oids
	client := serviceclient.New("acl-service")
	//TODO post multiple sids in acl einfügen endpiont in acl-service einfügen
	newPermissions, _ := json.Marshal(permissions)
	resp, err := client.Put(fmt.Sprintf("/sids/%s/permissions?%s", userId, oids), "application/json", bytes.NewReader(newPermissions), "Authorization", jwt)
	if err != nil {
		return err
	}
	if resp.StatusCode >= 300 {
		return errors.New("acl-service returned a not successfull statuscode while setting permissions for new assistant.")
	}
	return nil
}
//database checked
func generateAddModule(id, userId string, officers, assistants map[string]bool, op *jsonpatch.Operation, params map[string]string) (*jsonpatch.CommandContainer, error) {
	if err := checkAuthorityAndValidatePatch(jsonpatch.ADD, op.Type, userId, officers); err != nil {
		return nil, err
	}
	value := op.Value.(map[string]interface{})
	command := buildDefaultCommand("SELECT add_module(%v)", value["id"], id, value["description"], value["video_id"], value["script_id"], value["parents"])
	command.AfterCallback = func(transaction, prev interface{}) (interface{}, error) {
		client := serviceclient.New("acl-service")
		err := checkStatus(client.Post("/objects", "json", strings.NewReader(value["id"].(string))))
		if err != nil {
			return nil, err
		}
		assistantsString := ""
		for _, a := range assistants {
			assistantsString = assistantsString + "&sid=" + strconv.FormatBool(a)
		}
		assistantsString = strings.TrimLeft(assistantsString, "&")
		permissions, _ := json.Marshal(map[string]bool{"read": true, "create": true, "update": false, "delete": false})
		return nil, checkStatus(client.Put(fmt.Sprintf("/objects/%s/permissions?%s", value["id"], assistantsString), "application/json", bytes.NewReader(permissions)))
	}
	return command, nil
}