package nbi

import (
	"bytes"
	"fmt"
	"net/http"
	"strings"

	"nms_nbi/lib/dborm"

	"github.com/go-resty/resty/v2"

	"github.com/gorilla/mux"

	"nms_nbi/lib/global"
	"nms_nbi/lib/log"
	"nms_nbi/lib/oauth"
	"nms_nbi/lib/services"
	"nms_nbi/lib/session"
	"nms_nbi/restagent/config"
)

type ErrorOAuthResponse struct {
	Error map[string]interface{}
}

type FailOAuthResponse struct {
	Error struct {
		ErrorCode string
		ErrorInfo string
	}
}

type ApiResponse struct {
	ResultCode    string
	ResultMessage interface{}
}

var globalSession = session.NewSessManager("restagent")

var (
	MAX_RMUID_NUM   int
	MAX_ALARMID_NUM int
	MAX_PMUID_NUM   int
	MAX_SUBID_NUM   int
	MAX_URI_LEN     int
	RMUID_REGEXP    string
)

var (
	// Northbound interface
	GetNRMUri             = config.DefaultUriPrefix + "/resourceManagement/{apiVersion}/elementType/{elementTypeValue}/objectType/{objectTypeValue}"
	NorthboundGetAlarmUri = config.DefaultUriPrefix + "/faultManagement/{apiVersion}/alarms" // ?alarmIds={alarmIdValues}

	CustomGetNRMUri = config.UriPrefix + "/resourceManagement/{apiVersion}/elementType/{elementTypeValue}/objectType/{objectTypeValue}"
)

func CheckParameterName(r *http.Request) []string {
	var errorParams []string
	vars := r.URL.Query()
	for k, v := range vars {
		log.Debug("vars:", k, v)
		if k != "rmUIDs" && k != "fields" {
			errorParams = append(errorParams, k)
		}
	}

	return errorParams
}

func GetRmUIDArr(r *http.Request) []string {
	vars := r.URL.Query()
	rmUIDs, ok := vars["rmUIDs"]
	if !ok {
		log.Debug("rmUIDs is not exist")
		return nil
	}

	var rmUIDValues []string
	for _, r := range rmUIDs {
		if r != "" {
			rmUIDValues = global.MergeStringArr(rmUIDValues, strings.Split(r, `,`))
		}
	}

	return rmUIDValues
}

func GetAttrNameArr(r *http.Request) []string {
	vars := r.URL.Query()
	fields, ok := vars["fields"]
	if !ok {
		log.Debug("attributeNames does not exist")
		return nil
	}
	var attrNames []string
	for _, a := range fields {
		if a != "" {
			attrNames = global.MergeStringArr(attrNames, strings.Split(a, `,`))
		}
	}

	return attrNames
}

func CheckValidRmUID(rmUIDs []string) []string {
	log.Debug("CheckValidRmUID processing... ")

	var invalidRmUIDs []string
	for _, r := range rmUIDs {
		if !global.MatchRmUID(RMUID_REGEXP, r) {
			invalidRmUIDs = append(invalidRmUIDs, r)
		}
	}

	return invalidRmUIDs
}

func CheckLocalRmUID(rmUIDs []string) string {
	log.Debug("GetLocalRmUID processing... ")

	rmUID := config.GetRmUIDFromConfig()
	for _, r := range rmUIDs {
		if r == rmUID {
			return rmUID
		}
	}

	return ""
}

func NBIGetNRMFromNF(w http.ResponseWriter, r *http.Request) {
	log.Debug("NBIGetNRMFromNF processing... ")

	// response 414-4 uri too long ? (optional)
	// todo ... ?
	if bytes.Count([]byte(r.RequestURI), nil) > config.GetUriMaxLenFromConfig() {
		log.Error("Request Uri too long:", bytes.Count([]byte(r.RequestURI), nil), config.GetUriMaxLenFromConfig())
		services.ResponseRequestURITooLong414UriTooLong(w)
		return
	}

	// check media type(content type) only support "application/json"
	// response 415-1
	if !services.IsVallidContentType(r, config.GetYamlConfig().OMC.CheckContentType) {
		log.Debug("Invalid Content-Type")
		services.ResponseUnsupportedMediaType415(w)
		return
	}

	// error processing ...
	// 401-1 response
	token, ret := oauth.IsCarriedToken(r)
	if ret == false {
		log.Error("AccessToken is not carried")
		services.ResponseUnauthorized401AccessTokenNotCarried(w)
		return
	}

	// 401-2 response
	if dborm.XormExistValidToken(token, config.GetExpiresFromConfig()) == false {
		log.Error("AccessToken fails or does not exist")
		services.ResponseUnauthorized401AccessTokenNotExist(w)
		return
	}

	_, err := dborm.XormUpdateSessionShakeTime(token)
	if err != nil {
		log.Error("Failed to update session table:", err)
		services.ResponseUnauthorized401AccessTokenNotExist(w)
		return
	}

	/*
		// response 403 Forbidden, permissions deny
		// todo...
		plist := globalSession.GetPermissionFromSession(token)
		log.Debug("permission list:", plist)
		if len(plist) == 0 || plist[0] == false {
			log.Debug("User permission deny")
			services.ResponseForbidden403NotPermission(w)
			return
		}
	*/
	vars := mux.Vars(r)
	neType := vars["elementTypeValue"]

	apiVer := vars["apiVersion"]
	if apiVer != "v1" {
		log.Error("Uri is invalid")
		services.ResponseNotFound404UriNotExist(w, r)
		return
	}

	// response 406-1
	rmUIDValues := GetRmUIDArr(r)
	if rmUIDValues == nil {
		log.Error("missing parameter: rmUIDs")
		services.ResponseNotAcceptable406MissingParam(w)
		return
	}

	// response 406-2
	errorParams := CheckParameterName(r)
	if errorParams != nil {
		log.Error("parameter name error: ", errorParams)
		services.ResponseNotAcceptable406ParamError(w, errorParams)
		return
	}

	// response 400-5
	if len(rmUIDValues) == 0 {
		log.Error("rmUIDs is wrong or NULL")
		services.ResponseBadRequest400WrongParamValue(w)
		return
	}

	// response 414-1
	if len(rmUIDValues) > config.GetYamlConfig().Params.RmUIDMaxNum {
		log.Error("rmUID greater than", config.GetYamlConfig().Params.RmUIDMaxNum)
		services.ResponseRequestURITooLong414NRMNumExceed(w, config.GetYamlConfig().Params.RmUIDMaxNum)
		return
	}

	var response *resty.Response
	respMsg := make(map[string]interface{})
	for _, rmUID := range rmUIDValues {
		neInfo, err := dborm.XormGetNeInfoByRmUID(neType, rmUID)
		if err != nil {
			log.Error("dborm.XormGetNeInfo is failed:", err)
			services.ResponseInternalServerError500DatabaseOperationFailed(w)
			return
		}

		requestURI2NF := fmt.Sprintf("http://%s:%v%s", neInfo.Ip, neInfo.Port, r.RequestURI)
		log.Debug("requestURI2NF: GET ", requestURI2NF)

		client := resty.New()
		response, err = client.R().
			EnableTrace().
			SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
			SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
			Get(requestURI2NF)
		if err != nil {
			log.Error("Failed to Get from NF:", err)
			services.ResponseInternalServerError500NFConnectRefused(w)
			return
		}
		switch response.StatusCode() {
		case http.StatusOK, http.StatusAccepted, http.StatusNoContent, http.StatusCreated:
			respMsg["data"] = response
		default:
			if response != nil {
				services.TransportResponse(w, response.StatusCode(), response.Body())
			}
		}
	}

	services.TransportResponse(w, response.StatusCode(), response.Body())
}
