package github

import (
	"fmt"
	"log"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/gin-gonic/gin"

	"github.com/pingcap/lark2es/pkg"
	"github.com/pingcap/lark2es/pkg/config"
)

var oauthCh chan GithubOAuth
var comfirmMap sync.Map

func RegisterRoute(engine *gin.Engine, ch chan GithubOAuth) {
	engine.LoadHTMLGlob("./tpl/*.tmpl")
	webhookGroup := engine.Group("/webhook")
	webhookGroup.GET("/github", githubCallback)
	webhookGroup.GET("/comfirm", comfirmCallback)

	oauthCh = ch
}

func comfirmCallback(c *gin.Context) {
	email := c.Query("email")

	if v, ok := comfirmMap.Load(email); ok {
		log.Println(v)
		d := v.(GithubOAuth)
		if time.Now().Sub(d.OauthTime) > 5*time.Minute {
			c.JSON(200, "Oops，因为超时确认失败，请给机器人发送bind-github命令，重试下。")
		}
		oauthCh <- d
		comfirmMap.Delete(email)
	}
	c.HTML(200, "info.tmpl", struct {
		Info string
	}{"绑定成功"})
}

func githubCallback(c *gin.Context) {
	code := c.Query("code")
	state := c.Query("state")
	email := ""
	if strings.LastIndex(state, "-") != -1 {
		email = state[:strings.LastIndex(state, "-")]
	} else {
		email = state
	}

	acToken := GetGithubAccessTokenByCode(code)
	if acToken != "" {
		// query github emails and save
		githubEmails := QueryGithubEmails(acToken)
		githubUser := QueryGithubUser(acToken)
		if githubUser == nil {
			log.Println("[error] QueryGithubUser = nil", email)
			c.JSON(200, "failed")
		}

		ss := make([]string, 0, len(githubEmails))
		for _, e := range githubEmails {
			if e.Primary {
				ss = append(ss, fmt.Sprintf("%s(primary)", e.Email))
			} else {
				ss = append(ss, e.Email)
			}
		}
		allGHEmailsStr := strings.Join(ss, ";")
		comfirmMap.Store(email,
			GithubOAuth{allGHEmailsStr, githubUser.Login,
				email, time.Now()})

		data := struct {
			Email        string
			Login        string
			GithubEmails []string
		}{email, githubUser.Login, ss}
		c.HTML(200, "index.tmpl", &data)
	} else {
		c.HTML(200, "info.tmpl", struct {
			Info string
		}{"失败，请重试！！"})
	}
	//oauthCh <- GithubOAuth{Code: code, Email: email}
}

func GetGithubAccessTokenByCode(code string) string {
	params := url.Values{}
	params.Add("client_id", config.GetString("githubappid"))
	params.Add("client_secret", config.GetString("githubappsecret"))
	params.Add("code", code)
	req, err := pkg.PostRequest("https://github.com/login/oauth/access_token?"+params.Encode(),
		nil, nil)
	if err != nil {
		log.Println("[error] GetGithubAccessTokenByCode", err)
		return ""
	}

	var resp string
	err = pkg.Do(req, &resp)
	if err != nil {
		log.Println("[error] GetGithubAccessTokenByCode", err)
		return ""
	}

	if strings.Contains(resp, "access_token") {
		return strings.TrimPrefix(strings.Split(resp, "&")[0], "access_token=")
	}
	log.Println("[error] GetGithubAccessTokenByCode resp ", resp)
	return ""
}

func QueryGithubUser(token string) *GithubUser {
	req, err := pkg.GetRequest("https://api.github.com/user",
		nil, nil,
		map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)})
	if err != nil {
		log.Println("[error] QueryGithubEmails", err)
		return nil
	}

	user := GithubUser{}
	err = pkg.Do(req, &user)
	if err != nil {
		log.Println("[error] QueryGithubEmails", err)
		return nil
	}
	return &user
}

func QueryGithubEmails(token string) (emails GithubEmails) {
	req, err := pkg.GetRequest("https://api.github.com/user/emails",
		map[string]string{"per_page": "100"}, nil,
		map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)})
	if err != nil {
		log.Println("[error] QueryGithubEmails", err)
		return
	}

	err = pkg.Do(req, &emails)
	if err != nil {
		log.Println("[error] QueryGithubEmails", err)
		return
	}
	return
}
