package v1

import (
	"context"
	"fmt"
	"os"
	"strings"
	"time"

	"cloud.google.com/go/bigquery"
	"github.com/pingcap-inc/pchub/pkg/log"
	"github.com/spf13/viper"
	"google.golang.org/api/iterator"
	"google.golang.org/api/option"

	"github.com/pingcap-inc/security/internal/alert-server/store"
	v1 "github.com/pingcap-inc/security/model/v1"
)

type fscService struct {
	store store.Factory
}

type FscSrv interface {
	Fsc(ctx context.Context)
}

var _ FscSrv = (*fscService)(nil)

func newFsc(srv *service) *fscService {
	return &fscService{store: srv.store}
}

const (
	defaultProject = "gcp-digital-bi"
	InvalidCompany = 3
	salesforce     = 0
	fxiaoke        = 1
	tidbcloud      = 2
)

func (s *fscService) Fsc(ctx context.Context) {
	err := s.FscCheck(ctx)
	if err != nil {
		sdMsg(ctx)
		return
	}

	dunsSrv := &dunsService{store: s.store}
	err = dunsSrv.DunCheck(ctx)
	if err != nil {
		sdMsg(ctx)
		return
	}

	log.L(ctx).Info("Fsc Finished.")
}

func (s *fscService) FscCheck(ctx context.Context) error {
	log.L(ctx).Info("Fsc Called.")

	biMap := NewBiMap()
	biMap.Put("salesforce", salesforce)
	biMap.Put("纷享销客", fxiaoke)
	biMap.Put("TidbCloud", tidbcloud)

	saLoc := viper.GetString("bigquery-sa")
	_, err := os.Stat(saLoc)
	if err != nil {
		log.L(ctx).Errorf("Failed to stat: %v", err)

		return err
	}

	bqSrv, err := bigquery.NewClient(ctx, defaultProject, option.WithCredentialsFile(saLoc))
	if err != nil {
		log.L(ctx).Errorf("Failed to create client: %v", err)

		return err
	}

	// five day ago
	newDay := time.Now().AddDate(0, 0, -5).Format("2006-01-02")

	baseSql := "SELECT %s as name,%s as country FROM `gcp-digital-bi.%s.%s` where %s >='" + newDay + "'"

	bqMap, err := s.FetchFromBq(ctx, bqSrv, baseSql, biMap)
	if err != nil {
		return err
	}

	comDb, err := s.store.Company().ListCompany(ctx)
	if err != nil {
		log.L(ctx).Errorf("list error: %v", err)

		return err
	}

	comMap := make(map[string]v1.Company)
	for _, v := range comDb {
		comMap[v.Name+v.Country] = v
	}

	if len(bqMap) != 0 {

		saveComs := make([]v1.Company, 0)
		for k, v := range bqMap {
			for _, rd := range v {
				if rd == nil {
					continue
				}

				_, ok := comMap[rd.Name.StringVal+rd.Country.StringVal]
				if ok {
					continue
				}

				if k == tidbcloud || k == salesforce {
					if strings.Contains(rd.Name.StringVal, "org") || strings.Contains(rd.Name.StringVal, "Org") {
						continue
					}
				}
				if strings.ReplaceAll(rd.Name.StringVal, " ", "") == "" {
					continue
				}
				stas := Unchecked
				if !isValid(rd.Name.StringVal, rd.Country.StringVal) {
					stas = InvalidCompany
				}
				tpCom := v1.Company{
					Name:      rd.Name.StringVal,
					From:      biMap.GetByValue(k),
					Country:   rd.Country.StringVal,
					CreatedAt: time.Now(),
					Status:    stas,
				}

				saveComs = append(saveComs, tpCom)
			}

		}
		err = s.store.Company().CreateCompany(ctx, saveComs)
		if err != nil && !strings.Contains(err.Error(), "Duplicate entry") {

			log.L(ctx).Errorf("create error: %v", err)

			return err
		}
	}

	log.L(ctx).Info("Fsc Finished.")

	return nil
}

func (s *fscService) FetchFromBq(ctx context.Context, bqSrv *bigquery.Client, baseSql string, bimap *BiMap) (map[int][]*v1.BqAccount, error) {
	log.L(ctx).Info("FetchFromBq Called.")

	resmap := make(map[int][]*v1.BqAccount)

	for i := 0; i < 3; i++ {
		var sql string
		switch i {
		case salesforce:
			sql = fmt.Sprintf(baseSql, "name", "billing_country_code", "salesforce", "account", "created_date")
		case fxiaoke:
			sql = fmt.Sprintf(baseSql, "name", "'CN'", "salesforce", "fxiaoke_account", "CreateTime")
		case tidbcloud:
			sql = fmt.Sprintf(baseSql, "company_name", "country_code", "metadb_new", "billing_informations", "created_at")
		default:
			log.L(ctx).Errorf("not support type")

			return nil, fmt.Errorf("not support")
		}

		res, err := GetBqData(ctx, bqSrv, sql)
		if err != nil {
			return nil, err
		}
		resmap[i] = res
	}

	log.L(ctx).Info("FetchFromBq Finished.")

	return resmap, nil
}

func GetBqData(ctx context.Context, bqSrv *bigquery.Client, querySql string) ([]*v1.BqAccount, error) {
	log.L(ctx).Infof("GetBqData Called. sql: %s", querySql)

	query := bqSrv.Query(querySql)
	it, err := query.Read(ctx)
	if err != nil {
		log.L(ctx).Errorf("Failed to read query: %v", err)

		return nil, err
	}
	res := make([]*v1.BqAccount, 0)

	for {
		var value v1.BqAccount
		err := it.Next(&value)
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.L(ctx).Errorf("Failed to read row: %v", err)

			return nil, err
		}

		res = append(res, &value)
	}

	log.L(ctx).Info("GetBqData Finished.")

	return res, nil
}

func isValid(name, country string) bool {
	if strings.ReplaceAll(name, " ", "") == "" ||
		strings.ReplaceAll(country, " ", "") == "" {
		return false
	}

	return true
}
