package lark

import (
	"context"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"strings"

	log "github.com/sirupsen/logrus"

	"github.com/pingcap-inc/it-automation/internal/sync-google-email/model"
)

const (
	updateUser          = "https://open.feishu.cn/open-apis/contact/v3/users"
	getUserIdUrl        = "https://open.feishu.cn/open-apis/contact/v3/users/batch_get_id"
	getExcelInfoUrl     = "https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/shtcnYsdIkOsicfTEgb3s2t69Jb/values/f49ac9"
	subDepartmentUrl    = "https://open.feishu.cn/open-apis/contact/v3/departments"
	userByDepartmentUrl = "https://open.feishu.cn/open-apis/contact/v3/users/find_by_department"
)

const (
	HrbpType     = "GENERIC_USER"
	HrbpId       = "C-7143156434032312324"
	ExtraMailId  = "C-7137192459914477572"
	SystemMailId = "C-7230736880819388420"
)

type Lark interface {
	UpdateUser(ctx context.Context, hrbpLark model.LarkUser, extraMail string, larkUser model.LarkUser)
	UpdateUserMail(ctx context.Context, afterMail, userId string)
	GetUserIds(ctx context.Context, body GetUserIdBody) ([]Info, error)
	GetSubDepartment(ctx context.Context, departmentId, pageToken string) (*SubDepartmentInfo, error)
	GetUsersByDepartment(ctx context.Context, departmentId, pageToken string) (*UserByDptData, error)
	BatchDeleteTableRecords(ctx context.Context, recordIds []string) (*BatchDeleteRes, error)
	GetTableRecords(ctx context.Context) ([]ListTableItem, error)
	WriteToBiTable(ctx context.Context, fields map[string]interface{}) (*WriteTableRes, error)
	BatchWriteToBiTable(ctx context.Context, writeBody BatchWriteTableBody) error
}

var _ Lark = (*larkService)(nil)

type larkService struct {
}

func NewLarkService() Lark {
	return &larkService{}
}

func (ls *larkService) UpdateUser(ctx context.Context, hrbpLark model.LarkUser, extraMail string, larkUser model.LarkUser) {
	cli := &http.Client{}

	hrbpAttr := Attr{
		Type: HrbpType,
		Id:   HrbpId,
		Value: Value{
			GenericUser: GenericUser{
				Id:   hrbpLark.LarkUserID,
				Type: 1,
			},
		},
	}
	extraMailAttr := Attr{
		Type: "TEXT",
		Id:   ExtraMailId,
		Value: Value{
			Text: extraMail,
		},
	}
	systemMailAttr := Attr{
		Type: "TEXT",
		Id:   SystemMailId,
		Value: Value{
			Text: larkUser.Email,
		},
	}
	reqBody := UpdateUserBody{
		CustomAttrs: []Attr{
			hrbpAttr,
			extraMailAttr,
			systemMailAttr,
		},
	}
	marshal, err := json.Marshal(reqBody)
	if err != nil {
		log.Errorf("Error when marshal req body: %v", err)

		return
	}
	req, err := http.NewRequest("PATCH", updateUser+"/"+larkUser.LarkUserID, strings.NewReader(string(marshal)))
	if err != nil {
		log.Errorf("Error when make http request: %v", err)

		return
	}
	query := req.URL.Query()
	query.Add("user_id_type", "user_id")
	req.URL.RawQuery = query.Encode()
	token := GetTenantAccessToken(ctx)
	req.Header.Add("Authorization", "Bearer "+token)
	req.Header.Add("Content-Type", "application/json; charset=utf-8")

	resp, err := cli.Do(req)
	if err != nil {
		log.Errorf("Error when send http request: %v", err)

		return
	}

	defer resp.Body.Close()

	res := &UpdateResponse{}
	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Errorf("Error when read response body: %v", err)

		return
	}
	err = json.Unmarshal(bytes, res)
	if err != nil {
		log.Errorf("Error when unmarshal response body: %v", err)

		return
	}
	log.Infof("%s update info success mail: %s", larkUser.Name, extraMail)
}

func (ls *larkService) UpdateUserMail(ctx context.Context, userId, afterMail string) {
	cli := &http.Client{}
	body := UserNewInfo{
		Email: afterMail,
	}
	marshal, err := json.Marshal(&body)
	if err != nil {
		log.Errorf("Error when marshal req body: %v", err)

		return
	}
	req, err := http.NewRequest("PATCH", updateUser+"/"+userId, strings.NewReader(string(marshal)))
	if err != nil {
		log.Errorf("Error when make http request: %v", err)

		return
	}
	query := req.URL.Query()
	query.Add("user_id_type", "user_id")
	req.URL.RawQuery = query.Encode()
	token := GetTenantAccessToken(ctx)
	req.Header.Add("Authorization", "Bearer "+token)
	req.Header.Add("Content-Type", "application/json; charset=utf-8")

	resp, err := cli.Do(req)
	if err != nil {
		log.Errorf("Error when send http request: %v", err)

		return
	}

	defer resp.Body.Close()

	res := &UpdateResponse{}
	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Errorf("Error when read response body: %v", err)

		return
	}
	err = json.Unmarshal(bytes, res)
	if err != nil {
		log.Errorf("Error when unmarshal response body: %v", err)

		return
	}
	log.Infof("%s update info success mail: %s", userId, afterMail)
}

func (ls *larkService) GetUserIds(ctx context.Context, body GetUserIdBody) ([]Info, error) {
	cli := &http.Client{}

	reqBody, err := json.Marshal(body)
	if err != nil {
		return nil, err
	}
	req, err := http.NewRequest("POST", getUserIdUrl, strings.NewReader(string(reqBody)))

	token := GetTenantAccessToken(ctx)
	req.Header.Add("Authorization", "Bearer "+token)
	req.Header.Add("Content-Type", "application/json; charset=utf-8")
	if err != nil {
		return nil, err
	}
	query := req.URL.Query()
	query.Add("user_id_type", "user_id")
	req.URL.RawQuery = query.Encode()

	resp, err := cli.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		log.Error(resp.Status)
	}
	defer resp.Body.Close()

	resBody, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return nil, err
	}

	response := &Response{}
	err = json.Unmarshal(resBody, response)

	if err != nil {
		return nil, err
	}
	return response.Data.UserList, nil
}

type UserByDptRes struct {
	Message string        `json:"message"`
	Data    UserByDptData `json:"data"`
	Code    int           `json:"code"`
}
type UserByDptData struct {
	HasMore   bool            `json:"has_more"`
	PageToken string          `json:"page_token"`
	Items     []UserByDptItem `json:"items"`
}
type UserByDptItem struct {
	UserId       string          `json:"user_id"`
	OpenId       string          `json:"open_id"`
	Name         string          `json:"name"`
	Email        string          `json:"email"`
	CustomAttrs  []Attr          `json:"custom_attrs"`
	Status       UserByDptStatus `json:"status"`
	IsFrozen     bool            `json:"is_frozen"`
	LeaderUserId string          `json:"leader_user_id"`
}
type Attr struct {
	Type  string `json:"type"`
	Id    string `json:"id"`
	Value Value  `json:"value"`
}

type Value struct {
	Text        string      `json:"text,omitempty"`
	Url         string      `json:"url,omitempty"`
	PcUrl       string      `json:"pc_url,omitempty"`
	OptionId    string      `json:"option_id,omitempty"`
	OptionValue string      `json:"option_value,omitempty"`
	Name        string      `json:"name,omitempty"`
	PictureUrl  string      `json:"picture_url,omitempty"`
	GenericUser GenericUser `json:"generic_user,omitempty"`
}

type GenericUser struct {
	Id   string `json:"id"`
	Type int    `json:"type"`
}

type UserByDptStatus struct {
	IsFrozen    bool `json:"is_frozen"`
	IsResigned  bool `json:"is_resigned"`
	IsActivated bool `json:"is_activated"`
	IsExited    bool `json:"is_exited"`
	IsUnjoin    bool `json:"is_unjoin"`
}

func (ls *larkService) GetUsersByDepartment(ctx context.Context, departmentId, pageToken string) (*UserByDptData, error) {
	cli := &http.Client{}

	req, err := http.NewRequest("GET", userByDepartmentUrl, nil)
	if err != nil {
		log.Errorf("Error when make http request: %v", err)

		return nil, err
	}
	token := GetTenantAccessToken(ctx)
	req.Header.Add("Authorization", "Bearer "+token)
	req.Header.Add("Content-Type", "application/json; charset=utf-8")

	query := req.URL.Query()
	query.Add("user_id_type", "user_id")
	query.Add("department_id_type", "department_id")
	query.Add("department_id", departmentId)
	query.Add("page_size", "50")
	query.Add("page_token", pageToken)
	req.URL.RawQuery = query.Encode()

	resp, err := cli.Do(req)
	if err != nil {
		log.Errorf("Error when send http request: %v", err)

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

	resBody, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		log.Errorf("Error when read response body: %v", err)

		return nil, err
	}

	response := &UserByDptRes{}
	err = json.Unmarshal(resBody, response)
	if err != nil {
		log.Errorf("Error when unmarshal response body: %v", err)

		return nil, err
	}
	if response.Code != 0 || resp.StatusCode != http.StatusOK {
		log.Error("lark http response error")

		return nil, errors.New("lark http response error")
	}

	return &response.Data, nil
}

func (ls *larkService) GetSubDepartment(ctx context.Context, departmentId, pageToken string) (*SubDepartmentInfo, error) {

	cli := &http.Client{}

	req, err := http.NewRequest("GET", subDepartmentUrl+"/"+departmentId+"/children", nil)

	token := GetTenantAccessToken(ctx)
	req.Header.Add("Authorization", "Bearer "+token)
	req.Header.Add("Content-Type", "application/json; charset=utf-8")
	if err != nil {
		log.Errorf("Error when make request:%v", err)

		return nil, err
	}
	query := req.URL.Query()
	query.Add("user_id_type", "user_id")
	query.Add("department_id_type", "department_id")
	query.Add("fetch_child", "true")
	query.Add("page_size", "50")
	query.Add("page_token", pageToken)

	req.URL.RawQuery = query.Encode()

	resp, err := cli.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	resBody, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return nil, err
	}

	response := &SubDepartmentRes{}
	err = json.Unmarshal(resBody, response)

	if resp.StatusCode != http.StatusOK || response.Code != 0 {
		log.Errorf("Error when get sub department")

		return nil, errors.New("error when get sub department")
	}
	if err != nil {
		return nil, err
	}
	return &response.Data, nil
}
func GetUser(ctx context.Context, token, openId string) (*User, error) {
	cli := &http.Client{}

	req, err := http.NewRequest("GET", updateUser+openId, nil)
	if err != nil {
		return nil, err
	}
	query := req.URL.Query()
	query.Add("user_id_type", "open_id")
	req.URL.RawQuery = query.Encode()

	req.Header.Add("Authorization", "Bearer "+token)
	req.Header.Add("Content-Type", "application/json; charset=utf-8")

	resp, err := cli.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		log.Error("获取员工信息失败")
		return nil, err
	}
	defer resp.Body.Close()

	res := &UpdateResponse{}
	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(bytes, res)
	if err != nil {
		return nil, err
	}
	return &res.Data.User, nil
}

func GetExcelInfo(token string) (*GetExcelResponse, error) {

	cli := &http.Client{}

	req, err := http.NewRequest("GET", getExcelInfoUrl, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Authorization", "Bearer "+token)
	req.Header.Add("Content-Type", "application/json")

	resp, err := cli.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		log.Error("获取Excel信息失败")
		return nil, err
	}
	defer resp.Body.Close()

	res := &GetExcelResponse{}
	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	err = json.Unmarshal(bytes, res)
	if err != nil {
		return nil, err
	}
	return res, nil
}

func ExcelResChange(response *GetExcelResponse) map[string]string {
	resMap := make(map[string]string)

	for i, each := range response.Data.ValueRange.Values {
		if i == 0 {
			continue
		}
		s1 := each[0].([]interface{})
		if len(s1) == 0 {
			continue
		}

		s2 := each[1].([]interface{})
		if len(s2) == 0 {
			continue
		}
		m1 := s1[0].(map[string]interface{})
		m2 := s2[0].(map[string]interface{})
		t1, ok1 := m1["text"]
		t2, ok2 := m2["text"]
		if ok2 && ok1 {
			resMap[t1.(string)] = t2.(string)
		}
	}
	return resMap
}
