package authing

import (
	"bytes"
	"context"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/spf13/viper"

	"github.com/pingcap-inc/it-automation/pkg/log"

	"github.com/pingcap-inc/it-automation/internal/pkg/redis"
)

var (
	TokenUrl  = "https://api.authing.pingcap.net/api/v2/userpools/access-token"
	ExportUrl = "https://api.authing.pingcap.net/api/v2/orgs/export"
	Prefix    = "authing:"
	Token     = "token"
)

type AuthRestService interface {
	GetToken(ctx context.Context) (string, error)
	ExportAll(ctx context.Context) ([]OrgNode, error)
}

var _ AuthRestService = (*authRestService)(nil)

type authRestService struct {
}

func NewAuthRestService() *authRestService {
	return &authRestService{}
}

type TokenRequestBody struct {
	AccessKeyId     string `json:"userPoolId"`
	AccessKeySecret string `json:"secret"`
}

type TokenRequestResponse struct {
	AccessToken string `json:"accessToken"`
	Iat         int    `json:"iat"`
	Exp         int    `json:"exp"`
}

func (ars *authRestService) GetToken(ctx context.Context) (string, error) {

	restore := &redis.Redis{}
	key, err := restore.GetRedis(Prefix).GetKey(Token)
	if err != nil {
		if err.Error() != "key not found" {
			log.L(ctx).Errorf("Error when get tokenInR from redis: %v", err)

			// return "", err
		}
	}
	if key != "" {
		return key, nil
	}
	method := "POST"

	marshal, err := json.Marshal(TokenRequestBody{
		AccessKeyId:     viper.GetString("authing.id"),
		AccessKeySecret: viper.GetString("authing.secret"),
	})
	if err != nil {
		log.L(ctx).Errorf("Error when marshal json: %v", err)
		return "", err
	}

	client := &http.Client{}
	req, err := http.NewRequest(method, TokenUrl, bytes.NewReader(marshal))

	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("accept", "application/json")
	if err != nil {
		log.L(ctx).Errorf("Error when new request: %v", err)
		return "", err
	}

	res, err := client.Do(req)
	if err != nil {
		log.L(ctx).Errorf("Error when do request: %v", err)
		return "", err
	}
	defer res.Body.Close()

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.L(ctx).Errorf("Error when read body: %v", err)
		return "", err
	}
	result := &TokenRequestResponse{}
	err = json.Unmarshal(body, result)
	if err != nil {
		log.L(ctx).Errorf("Error when unmarshal json: %v", err)
		return "", err
	}
	err = restore.GetRedis(Prefix).SetKey(Token, result.AccessToken, time.Hour)
	if err != nil {
		log.L(ctx).Errorf("Error when set token to redis: %v", err)

		// return "", err
	}
	return result.AccessToken, nil
}

type OrgNode struct {
	Id            string    `json:"id"`
	Name          string    `json:"name"`
	LeaderUserIds []string  `json:"leaderUserIds"`
	LeaderUserId  string    `json:"leaderUserId"`
	Members       []Member  `json:"members"`
	Children      []OrgNode `json:"children"`
}

type Member struct {
	Id    string `json:"id"`
	Name  string `json:"name"`
	Email string `json:"email"`
}

func (ars *authRestService) ExportAll(ctx context.Context) ([]OrgNode, error) {
	token, err := ars.GetToken(ctx)
	if err != nil {
		log.L(ctx).Errorf("Error when get token: %v", err)

		return nil, err
	}

	method := "GET"

	client := &http.Client{}
	req, err := http.NewRequest(method, ExportUrl, nil)

	if err != nil {
		log.L(ctx).Errorf("Error when new request: %v", err)

		return nil, err
	}
	req.Header.Add("x-authing-userpool-id", viper.GetString("authing.id"))
	req.Header.Add("authorization", "Bearer "+token)

	res, err := client.Do(req)
	if err != nil {
		log.L(ctx).Errorf("Error when do request: %v", err)

		return nil, err
	}
	defer res.Body.Close()

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.L(ctx).Errorf("Error when read body: %v", err)

		return nil, err
	}
	result := &CommonResponse{}
	err = json.Unmarshal(body, result)
	if err != nil {
		log.L(ctx).Errorf("Error when unmarshal json: %v", err)

		return nil, err
	}
	marshalBody, err := json.Marshal(result.Data)
	if err != nil {
		log.L(ctx).Errorf("Error when marshal json: %v", err)

		return nil, err
	}
	var orgNodes []OrgNode
	err = json.Unmarshal(marshalBody, &orgNodes)
	if err != nil {
		log.L(ctx).Errorf("Error when unmarshal json: %v", err)

		return nil, err
	}

	return orgNodes, nil
}
