package main

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	"github.com/dgrijalva/jwt-go"
	"github.com/labstack/echo"
)

type handler struct{}
type userInfo struct {
	Result struct {
		Result struct {
			Sshpubkeyfp      []string `json:"sshpubkeyfp"`
			HasKeytab        bool     `json:"has_keytab"`
			Ipasshpubkey     []string `json:"ipasshpubkey"`
			Cn               []string `json:"cn"`
			Krbcanonicalname []string `json:"krbcanonicalname"`
			Krbticketflags   []string `json:"krbticketflags"`
			MemberofGroup    []string `json:"memberof_group"`
			HasPassword      bool     `json:"has_password"`
			Homedirectory    []string `json:"homedirectory"`
			Nsaccountlock    bool     `json:"nsaccountlock"`
			UID              []string `json:"uid"`
			Title            []string `json:"title"`
			Loginshell       []string `json:"loginshell"`
			Uidnumber        []string `json:"uidnumber"`
			Preserved        bool     `json:"preserved"`
			Krbextradata     []struct {
				Base64 string `json:"__base64__"`
			} `json:"krbextradata"`
			Mail                     []string `json:"mail"`
			MemberofindirectHbacrule []string `json:"memberofindirect_hbacrule"`
			Dn                       string   `json:"dn"`
			Displayname              []string `json:"displayname"`
			Mepmanagedentry          []string `json:"mepmanagedentry"`
			Ipauniqueid              []string `json:"ipauniqueid"`
			Krbloginfailedcount      []string `json:"krbloginfailedcount"`
			Krbpwdpolicyreference    []string `json:"krbpwdpolicyreference"`
			Krbprincipalname         []string `json:"krbprincipalname"`
			Givenname                []string `json:"givenname"`
			Krblastadminunlock       []struct {
				Datetime string `json:"__datetime__"`
			} `json:"krblastadminunlock"`
			Krbpasswordexpiration []struct {
				Datetime string `json:"__datetime__"`
			} `json:"krbpasswordexpiration"`
			Krblastfailedauth []struct {
				Datetime string `json:"__datetime__"`
			} `json:"krblastfailedauth"`
			Objectclass      []string `json:"objectclass"`
			Gidnumber        []string `json:"gidnumber"`
			Gecos            []string `json:"gecos"`
			Sn               []string `json:"sn"`
			MemberofSudorule []string `json:"memberof_sudorule"`
			Krblastpwdchange []struct {
				Datetime string `json:"__datetime__"`
			} `json:"krblastpwdchange"`
			Initials []string `json:"initials"`
		} `json:"result"`
		Value   string      `json:"value"`
		Summary interface{} `json:"summary"`
	} `json:"result"`
	Version   string      `json:"version"`
	Error     interface{} `json:"error"`
	ID        int         `json:"id"`
	Principal string      `json:"principal"`
}

// Most of the code is taken from the echo guide
// https://echo.labstack.com/cookbook/jwt
func (h *handler) login(c echo.Context) error {
	username := c.FormValue("username")
	password := c.FormValue("password")
	_url := "https://ipa.sf.faraborddi.dc/ipa/session/login_password"
	method := "POST"
	params := url.Values{}
	params.Add("user", username)
	params.Add("password", password)
	payload := strings.NewReader(params.Encode())
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	req, err := http.NewRequest(method, _url, payload)
	audit("Recieved Login request from: " + RealIP)
	if err != nil {
		fmt.Println(err)
	}
	req.Header.Add("Referer", "https://ipa.sf.faraborddi.dc/ipa")
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Accept", "text/plain")
	res, err := client.Do(req)
	cockie := res.Cookies()
	defer res.Body.Close()
	//fmt.Println(res.StatusCode)
	if res.StatusCode == 200 {
		user := getUserInfo(cockie, username)
		//fmt.Println(user.Result.Value)
		tokens, err := generateTokenPair(user)
		if err != nil {
			return err
		}

		return c.JSON(http.StatusOK, tokens)
	}

	return echo.ErrUnauthorized
}
func getUserInfo(cockie []*http.Cookie, username string) userInfo {
	url := "https://ipa.sf.faraborddi.dc/ipa/session/json"
	method := "POST"
	_json := fmt.Sprintf(`
{
    "method": "user_show",
    "params": [
        [
            "%s"
        ],
        {
            "all": true,
            "version": "2.215"
        }
    ],
    "id": 0
}
`, username)

	payload := strings.NewReader(_json)
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr}
	req, err := http.NewRequest(method, url, payload)

	if err != nil {
		fmt.Println(err)
	}
	req.Header.Add("Referer", "https://ipa.sf.faraborddi.dc/ipa")
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Accept", "text/plain")
	req.Header.Add("Cookie", cockie[0].Raw)
	res, err := client.Do(req)
	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)
	user := userInfo{}
	json.Unmarshal(body, &user)
	//fmt.Println(user.Result.Value)
	//fmt.Println(user.Result.Result.MemberofGroup)
	return user
}

// This is the api to refresh tokens
// Most of the code is taken from the jwt-go package's sample codes
// https://godoc.org/github.com/dgrijalva/jwt-go#example-Parse--Hmac
//func (h *handler) token(c echo.Context) error {
//	type tokenReqBody struct {
//		RefreshToken string `json:"refresh_token"`
//	}
//	tokenReq := tokenReqBody{}
//	c.Bind(&tokenReq)
//
//	// Parse takes the token string and a function for looking up the key.
//	// The latter is especially useful if you use multiple keys for your application.
//	// The standard is to use 'kid' in the head of the token to identify
//	// which key to use, but the parsed token (head and claims) is provided
//	// to the callback, providing flexibility.
//	token, err := jwt.Parse(tokenReq.RefreshToken, func(token *jwt.Token) (interface{}, error) {
//		// Don't forget to validate the alg is what you expect:
//		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
//			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
//		}
//
//		// hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key")
//		return []byte("secret"), nil
//	})
//
//	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
//		// Get the user record from database or
//		// run through your business logic to verify if the user can log in
//		if int(claims["sub"].(float64)) == 1 {
//
//			newTokenPair, err := generateTokenPair()
//			if err != nil {
//				return err
//			}
//
//			return c.JSON(http.StatusOK, newTokenPair)
//		}
//
//		return echo.ErrUnauthorized
//	}
//
//	return err
//}

// Most of the code is taken from the echo guide
// https://echo.labstack.com/cookbook/jwt
func (h *handler) private(c echo.Context) error {
	user := c.Get("user").(*jwt.Token)
	claims := user.Claims.(jwt.MapClaims)
	name := claims["name"].(string)
	return c.String(http.StatusOK, "Welcome "+name+"!")
}
