package lark

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

	"github.com/spf13/viper"

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

const (
	biTable = "https://open.feishu.cn/open-apis/bitable/v1/apps/"
)

type CommonRes struct {
	Code    int        `json:"code"`
	Message string     `json:"message"`
	Data    CommonData `json:"data"`
}
type CommonData struct {
	HasMore   bool            `json:"has_more"`
	PageToken string          `json:"page_token"`
	Total     int             `json:"total"`
	Items     []ListTableItem `json:"items"`
}
type ListTableItem struct {
	RecordId string `json:"record_id"`
	Fields   Field  `json:"fields"`
}
type Field struct {
	Key   string      `json:"key"`
	Value interface{} `json:"value"`
}
type TableMetaData struct {
	App App `json:"app"`
}
type App struct {
	AppToken   string `json:"app_token"`
	Name       string `json:"name"`
	Revision   int    `json:"revision"`
	IsAdvanced bool   `json:"is_advanced"`
}

func (ls *larkService) GetTableRecords(ctx context.Context) ([]ListTableItem, error) {
	log.L(ctx).Info("Start to get table records")

	token := GetTenantAccessToken(ctx)

	cli := &http.Client{}
	appToken := viper.GetString("bitable.app-token")
	tableId := viper.GetString("bitable.table-id")
	viewId := viper.GetString("bitable.view-id")

	var pageToken string
	var result []ListTableItem
	for {
		req, err := http.NewRequest("GET", biTable+appToken+"/tables/"+tableId+"/records", nil)
		if err != nil {
			return nil, err
		}
		req.Header.Add("Authorization", "Bearer "+token)
		// req.Header.Add("Content-Type", "application/json")

		query := req.URL.Query()
		query.Add("view_id", viewId)
		query.Add("page_size", "500")
		query.Add("page_token", pageToken)
		req.URL.RawQuery = query.Encode()
		resp, err := cli.Do(req)

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

			return nil, err
		}
		defer resp.Body.Close()
		res := &CommonRes{}
		ResBytes, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Errorf("Error when read table records: %v", err)

			return nil, err
		}
		err = json.Unmarshal(ResBytes, res)
		if err != nil || res.Code != 0 {
			log.Errorf("Error when unmarshal table records: %v code:%d", err, res.Code)

			return nil, err
		}

		result = append(result, res.Data.Items...)
		pageToken = res.Data.PageToken
		if pageToken == "" || pageToken == " " {
			break
		}
	}

	log.Info("Get table records success")

	return result, nil
}

type BatchDeleteRes struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
	Data struct {
		Records []struct {
			Deleted  bool   `json:"deleted"`
			RecordId string `json:"record_id"`
		} `json:"records"`
	} `json:"data"`
}

func (ls *larkService) BatchDeleteTableRecords(ctx context.Context, recordIds []string) (*BatchDeleteRes, error) {
	log.L(ctx).Info("Start to batch delete table records")

	token := GetTenantAccessToken(ctx)

	cli := &http.Client{}
	appToken := viper.GetString("bitable.app-token")
	tableId := viper.GetString("bitable.table-id")
	// viewId := viper.GetString("bitable.view-id")

	var records struct {
		Records []string `json:"records"`
	}
	records.Records = recordIds
	rIdsByte, err := json.Marshal(records)
	if err != nil {
		log.L(ctx).Errorf("Error when marshal record ids: %v", err)

		return nil, err
	}

	req, err := http.NewRequest("POST", biTable+appToken+"/tables/"+tableId+"/records/batch_delete", bytes.NewReader(rIdsByte))
	if err != nil {
		log.L(ctx).Errorf("Error when new request: %v", err)

		return nil, err
	}
	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.L(ctx).Errorf("Error when batch delete table records: %v", err)

		return nil, err
	}
	defer resp.Body.Close()
	res := &BatchDeleteRes{}
	resByte, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Errorf("Error when read table records: %v", err)

		return nil, err
	}
	err = json.Unmarshal(resByte, res)
	if err != nil || res.Code != 0 {
		log.Errorf("Error when unmarshal table records: %v code:%d", err, res.Code)

		return nil, err
	}
	log.Info("Batch delete table records success")

	return res, nil
}

type WriteTableRes struct {
	Code int `json:"code"`
	Data struct {
		Record struct {
			Fields   map[string]interface{} `json:"fields"`
			RecordId string                 `json:"record_id"`
		} `json:"record"`
	} `json:"data"`
	Msg string `json:"msg"`
}

func (ls *larkService) WriteToBiTable(ctx context.Context, fields map[string]interface{}) (*WriteTableRes, error) {
	log.L(ctx).Info("Start to write to bitable")
	token := GetTenantAccessToken(ctx)

	cli := &http.Client{}
	appToken := viper.GetString("bitable.app-token")
	tableId := viper.GetString("bitable.table-id")
	// viewId := viper.GetString("bitable.view-id")

	var body struct {
		Fields map[string]interface{} `json:"fields"`
	}
	body.Fields = fields
	bodyByte, err := json.Marshal(body)
	if err != nil {
		log.L(ctx).Errorf("Error when marshal record ids: %v", err)

		return nil, err
	}

	req, err := http.NewRequest("POST", biTable+appToken+"/tables/"+tableId+"/records", bytes.NewReader(bodyByte))
	if err != nil {
		log.L(ctx).Errorf("Error when new request: %v", err)

		return nil, err
	}
	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.L(ctx).Errorf("Error when batch delete table records: %v", err)

		return nil, err
	}
	defer resp.Body.Close()
	res := &WriteTableRes{}
	resByte, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Errorf("Error when read table records: %v", err)

		return nil, err
	}
	err = json.Unmarshal(resByte, res)
	if err != nil || res.Code != 0 {
		log.Errorf("Error when unmarshal table records: %v code:%d", err, res.Code)

		return nil, err
	}
	log.Infof("Write to bitable success, record id: %s", res.Data.Record.RecordId)

	return res, nil
}

type BatchWriteTableBody struct {
	Records []Record `json:"records"`
}
type Record struct {
	Fields map[string]interface{} `json:"fields"`
}
type BatchWriteTableRes struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    struct {
		Records []Record `json:"records"`
	} `json:"data"`
}

func (ls *larkService) BatchWriteToBiTable(ctx context.Context, writeBody BatchWriteTableBody) error {
	log.L(ctx).Info("Start to batch write to bitable")
	token := GetTenantAccessToken(ctx)

	cli := &http.Client{}
	appToken := viper.GetString("bitable.app-token")
	tableId := viper.GetString("bitable.table-id")
	// viewId := viper.GetString("bitable.view-id")

	bodyByte, err := json.Marshal(writeBody)
	if err != nil {
		log.L(ctx).Errorf("Error when marshal record ids: %v", err)

		return err
	}

	req, err := http.NewRequest("POST", biTable+appToken+"/tables/"+tableId+"/records/batch_create", bytes.NewReader(bodyByte))
	if err != nil {
		log.L(ctx).Errorf("Error when new request: %v", err)

		return err
	}
	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.L(ctx).Errorf("Error when batch delete table records: %v", err)

		return err
	}
	defer resp.Body.Close()
	res := &WriteTableRes{}
	resByte, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Errorf("Error when read table records: %v", err)

		return err
	}
	err = json.Unmarshal(resByte, res)
	if err != nil || res.Code != 0 {
		log.Errorf("Error when unmarshal table records: %v code:%d", err, res.Code)

		return err
	}
	log.Infof("Batch write to bitable success")

	return nil
}
