package controllers

import (
	"encoding/json"
	"fmt"
	"net/http"
	"oc-auth/infrastructure"
	auth_connectors "oc-auth/infrastructure/auth_connector"
	"regexp"
	"strings"

	oclib "cloud.o-forge.io/core/oc-lib"
	model "cloud.o-forge.io/core/oc-lib/models/peer"
	beego "github.com/beego/beego/v2/server/web"
)

// Operations about auth
type OAuthController struct {
	beego.Controller
}

// @Title Logout
// @Description unauthenticate user
// @Param	Authorization		header  string	false "auth token"
// @Success 200 {string}
// @router /ldap/logout [delete]
func (o *OAuthController) LogOutLDAP() {
	// authorize user
	reqToken := o.Ctx.Request.Header.Get("Authorization")
	splitToken := strings.Split(reqToken, "Bearer ")
	if len(splitToken) < 2 {
		reqToken = ""
	} else {
		reqToken = splitToken[1]
	}
	var res auth_connectors.Token
	json.Unmarshal(o.Ctx.Input.CopyBody(10000000), &res)

	token, err := infrastructure.GetAuthConnector().Logout(reqToken)
	if err != nil || token == nil {
		o.Data["json"] = err
	} else {
		o.Data["json"] = token
	}
	o.ServeJSON()
}

// @Title Login
// @Description authenticate user
// @Param	body		body 	models.workflow	true		"The workflow content"
// @Success 200 {string}
// @router /ldap/login [post]
func (o *OAuthController) LoginLDAP() {
	// authorize user
	var res auth_connectors.Token
	json.Unmarshal(o.Ctx.Input.CopyBody(10000000), &res)
	ldap := auth_connectors.New()
	found, err := ldap.Authenticate(o.Ctx.Request.Context(), res.Username, res.Password)
	if err != nil || !found {
		o.Data["json"] = err
		o.Ctx.ResponseWriter.WriteHeader(401)
		o.ServeJSON()
		return
	}
	token, err := infrastructure.GetAuthConnector().Login(res.Username,
		&http.Cookie{ // open a session
			Name:  "csrf_token",
			Value: o.XSRFToken(),
		})
	if err != nil || token == nil {
		o.Data["json"] = err
		o.Ctx.ResponseWriter.WriteHeader(401)
	} else {
		o.Data["json"] = token
	}
	o.ServeJSON()
}

// @Title Introspection
// @Description introspect token
// @Param	body		body 	models.Token	true		"The token info"
// @Success 200 {string}
// @router /refresh [post]
func (o *OAuthController) Refresh() {
	var token auth_connectors.Token
	json.Unmarshal(o.Ctx.Input.CopyBody(100000), &token)
	// refresh token
	newToken, err := infrastructure.GetAuthConnector().Refresh(&token)
	if err != nil || newToken == nil {
		o.Data["json"] = err
		o.Ctx.ResponseWriter.WriteHeader(401)
	} else {
		o.Data["json"] = newToken
	}
	o.ServeJSON()
}

// @Title Introspection
// @Description introspect token
// @Param	Authorization		header  string	false "auth token"
// @Success 200 {string}
// @router /introspect [get]
func (o *OAuthController) Introspect() {
	reqToken := o.Ctx.Request.Header.Get("Authorization")
	splitToken := strings.Split(reqToken, "Bearer ")
	if len(splitToken) < 2 {
		reqToken = ""
	} else {
		reqToken = splitToken[1]
	}

	token, err := infrastructure.GetAuthConnector().Introspect(reqToken)
	if err != nil || !token {
		o.Data["json"] = err
		o.Ctx.ResponseWriter.WriteHeader(401)
	}
	o.ServeJSON()
}

var whitelist = []string{
	"/login",
	"/refresh",
	"/introspect",
}

// @Title AuthForward
// @Description auth forward
// @Param	Authorization		header  string	false "auth token"
// @Success 200 {string}
// @router /forward [get]
func (o *OAuthController) InternalAuthForward() {
	fmt.Println("InternalAuthForward")
	reqToken := o.Ctx.Request.Header.Get("Authorization")
	if reqToken == "" {
		for _, w := range whitelist {
			if strings.Contains(o.Ctx.Request.Header.Get("X-Forwarded-Uri"), w) {
				o.Ctx.ResponseWriter.WriteHeader(200)
				o.ServeJSON()
				return
			}
		}
		o.Ctx.ResponseWriter.WriteHeader(401)
		o.ServeJSON()
		return
	}
	splitToken := strings.Split(reqToken, "Bearer ")
	if len(splitToken) < 2 {
		reqToken = ""
	} else {
		reqToken = splitToken[1]
	}
	origin, publicKey, external := o.extractOrigin()
	if !infrastructure.GetAuthConnector().CheckAuthForward( //reqToken != "" &&
		reqToken, publicKey, origin,
		o.Ctx.Request.Header.Get("X-Forwarded-Method"),
		o.Ctx.Request.Header.Get("X-Forwarded-Uri"), external) && origin != "" && publicKey != "" {
		o.Ctx.ResponseWriter.WriteHeader(401)
		o.ServeJSON()
		return
	}
	o.ServeJSON()
}

func (o *OAuthController) extractOrigin() (string, string, bool) {
	external := true
	publicKey := ""
	origin := o.Ctx.Request.Header.Get("X-Forwarded-Host")
	if origin == "" {
		origin = o.Ctx.Request.Header.Get("Origin")
	}
	searchStr := origin
	r := regexp.MustCompile("(:[0-9]+)")
	t := r.FindString(searchStr)
	if t != "" {
		searchStr = strings.Replace(searchStr, t, "", -1)
	}
	peer := oclib.Search(nil, searchStr, oclib.LibDataEnum(oclib.PEER))
	if peer.Code != 200 || len(peer.Data) == 0 { // TODO: add state of partnership
		return "", "", external
	}
	p := peer.Data[0].(*model.Peer)
	publicKey = p.PublicKey
	origin = p.Url
	if origin != "" { // is external
		if strings.Contains(origin, "localhost") || strings.Contains(origin, "127.0.0.1") || p.State == model.SELF {
			external = false
		}
	} else {
		external = false
	}
	return origin, publicKey, external
}