package service

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

	"cloud.google.com/go/bigquery"
	"github.com/spf13/viper"

	"github.com/pingcap-inc/data-ingestion-tools/internal/itcronjob/store"

	modelv1 "github.com/pingcap-inc/data-ingestion-tools/model"

	"github.com/pingcap-inc/pchub/pkg/errors"
	"github.com/pingcap-inc/pchub/pkg/log"
)

const (
	LarkOpenAPIBase = "https://open.feishu.cn/open-apis"
)

type commonService struct {
	store store.Factory
}

type ComSrv interface {
	GetLarkToken(ctx context.Context) (string, error)
	GetDataByReqLarkAPI(ctx context.Context, m, url string, body io.Reader, params map[string]string, ttoken string) (json.RawMessage, error)
}

var _ ComSrv = (*commonService)(nil)

func newCommon(srv *service) *commonService {
	return &commonService{store: srv.store}
}

func (cs *commonService) GetLarkToken(ctx context.Context) (string, error) {
	log.L(ctx).Info("GetLarkToken Called.")

	appIDInLark := viper.GetString("lark.id")
	appSecInLark := viper.GetString("lark.secret")

	if appIDInLark == "" || appSecInLark == "" {
		err := errors.New("lark-app-id or lark-app-secret is empty.")
		log.L(ctx).Error(err.Error())

		return "", err
	}

	tatBody, _ := json.Marshal(map[string]string{ //nolint:errchkjson // must Marshal
		"app_id":     appIDInLark,
		"app_secret": appSecInLark,
	})
	tatReqBody := bytes.NewBuffer(tatBody)
	tatReq, _ := http.NewRequestWithContext(ctx, http.MethodPost, LarkOpenAPIBase+"/auth/v3/tenant_access_token/internal", tatReqBody)
	tatResp, err := (&http.Client{}).Do(tatReq)
	if err != nil {
		err := errors.Wrap(err, "error when request lark tenant access token")
		log.L(ctx).Error(err.Error())

		return "", err
	}
	defer func() {
		_ = tatResp.Body.Close()
	}()
	tatRespBody, _ := io.ReadAll(tatResp.Body)
	var tatRes modelv1.LarkTATokenRes
	if err := json.Unmarshal(tatRespBody, &tatRes); err != nil {
		log.L(ctx).Errorf("Error when unmarshal lark res: %s", err.Error())

		return "", errors.Wrap(err, "error when unmarshal lark tenant_access_token res")
	}
	tToken := tatRes.TAT

	return tToken, nil
}

func (cs *commonService) GetDataByReqLarkAPI(ctx context.Context, m, url string, body io.Reader, params map[string]string, ttoken string) (json.RawMessage, error) {
	log.L(ctx).Info("GetDataByReqLarkAPI Called.")

	tat := ttoken
	if tat == "" {
		rtat, err := cs.GetLarkToken(ctx)
		if err != nil {
			log.L(ctx).Error(err.Error())

			return nil, errors.Wrap(err, "Error when GetLarkToken")
		}
		tat = rtat
	}

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	req, _ := http.NewRequestWithContext(ctx, m, url, body)

	query := req.URL.Query()
	if params != nil || len(params) != 0 {
		for k, v := range params {
			query.Set(k, v)
		}
	}
	req.URL.RawQuery = query.Encode()
	req.Header.Set("Authorization", "Bearer "+tat)
	resp, err := (&http.Client{}).Do(req)
	if err != nil {
		log.L(ctx).Error(err.Error())
		err = fmt.Errorf("error when request lark api: %s %s, err: %w", m, url, err)

		return nil, err
	}
	defer func() {
		_ = resp.Body.Close()
	}()

	b, _ := io.ReadAll(resp.Body)
	var res modelv1.LarkCommonRes
	if err := json.Unmarshal(b, &res); err != nil {
		log.L(ctx).Error(err.Error())
		err = fmt.Errorf("error when unmarshal lark api res: %s %s, err: %w", m, url, err)

		return nil, err
	}
	if res.Code != 0 {
		log.L(ctx).Errorf("Lark API error code: %d, msg: %s", res.Code, res.Msg)
		err = fmt.Errorf("lark Error Code: %d", res.Code)

		return nil, err
	}

	return res.Data, nil
}

func IsCurrentMonth(lastRunTime time.Time) bool {
	// 获取 lastRunTime 的年份和月份
	year, month := lastRunTime.Year(), lastRunTime.Month()

	// 获取当前日期的年份和月份
	now := time.Now()
	currentYear, currentMonth := now.Year(), now.Month()

	// 判断 lastRunTime 是否在当前月份
	if year == currentYear && month == currentMonth {
		return true
	}

	return false
}

func GetLastMonth() string {
	now := time.Now()
	// 计算上个月的时间
	lastMonth := now.AddDate(0, -1, 0)

	// 将时间格式化为 "YYYY-MM" 格式
	s := lastMonth.Format("2006-01")

	return s
}

func TrimSplit(s string) []string {
	// remove [
	s = strings.TrimPrefix(s, "[")
	// remove ]
	s = strings.TrimSuffix(s, "]")
	//  split by ,
	s = strings.ReplaceAll(s, "null", "")
	s = strings.ReplaceAll(s, "\"", "")
	return strings.Split(s, ",")
}

func TripleExpression(s, expect, re, unre string) string {
	if s == expect {
		return re
	}

	return unre
}

func DateToString(d bigquery.NullDate) string {
	if d.Valid {
		// Format the Date as a string in the form "yyyy-mm-dd".
		return fmt.Sprintf("%04d-%02d-%02d", d.Date.Year, d.Date.Month, d.Date.Day)
	} else {
		// If the Date is not valid, return an empty string or some other placeholder.
		return ""
	}
}
