package service

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"time"

	"github.com/spf13/viper"

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

type FullProjSrv interface {
	SyncFullProj(ctx context.Context) error
}

type fullProjService struct {
	store store.Factory
}

var _ FullProjSrv = (*fullProjService)(nil)

func newFullProj(str *service) *fullProjService {
	return &fullProjService{store: str.store}
}

func (f *fullProjService) SyncFullProj(ctx context.Context) error {
	log.L(ctx).Info("start sync full projects")
	f.SyncFullProjWithHistory(ctx)

	projs, err := f.store.CloudResource().ListAllProj(ctx)
	localPath := viper.GetString("localPath")
	if err != nil {
		log.L(ctx).Errorf("list all proj failed:%s", err)

		return err
	}
	var data []CloudResource
	for _, proj := range projs {
		data = append(data,
			CloudResource{proj.ClassType,
				authTypeMap[proj.AuthType],
				mgTypeMap[proj.ManagementType],
				proj.Department.Name,
				proj.DepartLarkID,
				proj.Desc,
				proj.Owner.Name,
				proj.Owner.Email,
				proj.Name,
				proj.CloudInsID,
				proj.Provider.Name,
				proj.ManagementID,
				time.Now().Format("2006-01-02 15:04:05"),
			})
	}
	path, err := Csv(ctx, data, localPath)
	if err != nil {
		return err
	}
	gcsSrv := gcsService{store: f.store}
	err = gcsSrv.Upload(ctx, path, "full-projects")
	if err != nil {
		return err
	}

	err = f.SyncFullProjWithHistory(ctx)
	if err != nil {
		log.L(ctx).Errorf("sync full projects with history failed:%s", err)

		return err
	}
	log.L(ctx).Info("upload full projects success")

	return nil
}

func (f *fullProjService) SyncFullProjWithHistory(ctx context.Context) error {
	log.L(ctx).Info("start sync full projects with history")

	projs, err := f.store.CloudResource().ListAllProjWithHistory(ctx)
	localPath := viper.GetString("localPath")
	if err != nil {
		log.L(ctx).Errorf("list all proj failed:%s", err)

		return err
	}
	var data []CloudResourceWithHistory
	for _, proj := range projs {
		if proj.CloudInsID == "720353139560" {
			fmt.Println("CloudInsID is empty")
		}
		isDel := false
		var delTime *time.Time = nil
		// sql.nulltime Cannot convert 'nil' to type 'sql.NullTime'
		if proj.DeletedAt.Valid {
			isDel = true

			// deep copy ptr
			delTime = new(time.Time)
			*delTime = proj.DeletedAt.Time
		}
		data = append(data,
			CloudResourceWithHistory{proj.ClassType,
				authTypeMap[proj.AuthType],
				mgTypeMap[proj.ManagementType],
				proj.Department.Name,
				proj.DepartLarkID,
				proj.Desc,
				proj.Owner.Name,
				proj.Owner.Email,
				proj.Name,
				proj.CloudInsID,
				proj.Provider.Name,
				proj.ManagementID,
				time.Now().Format("2006-01-02 15:04:05"),
				isDel,
				delTime,
			})
	}
	path, err := CsvWithHistory(ctx, data, localPath)
	if err != nil {
		return err
	}
	gcsSrv := gcsService{store: f.store}
	err = gcsSrv.Upload(ctx, path, "full-projects")
	if err != nil {
		return err
	}
	log.L(ctx).Info("upload full projects success")

	return nil
}

var mgTypeMap = map[int]string{
	0: "unknown",
	1: "self-managed",
	2: "IT-managed",
}

var authTypeMap = map[int]string{
	0: "unknown",
	1: "sso",
	2: "iam",
}

func Csv(ctx context.Context, data []CloudResource, localPath string) (string, error) {
	fileName := localPath + "/full-projects.jsonl"
	file, err := os.Create(fileName)
	if err != nil {
		log.L(ctx).Errorf("create file failed:%s", err)

		return "", err
	}
	defer file.Close()
	for _, d := range data {
		line, err := json.Marshal(d)
		if err != nil {
			panic(err)
		}
		line = append(line, '\n')
		_, err = file.Write(line)
		if err != nil {
			panic(err)
		}
	}
	return fileName, nil
}

func CsvWithHistory(ctx context.Context, data []CloudResourceWithHistory, localPath string) (string, error) {
	fileName := localPath + "/full-projects-u.jsonl"
	file, err := os.Create(fileName)
	if err != nil {
		log.L(ctx).Errorf("create file failed:%s", err)

		return "", err
	}
	defer file.Close()
	for _, d := range data {
		line, err := json.Marshal(d)
		if err != nil {
			panic(err)
		}
		line = append(line, '\n')
		_, err = file.Write(line)
		if err != nil {
			panic(err)
		}
	}
	return fileName, nil
}

type CloudResource struct {
	ClassType        string `json:"class_type"`
	AuthType         string `json:"auth_type"`
	ManagementType   string `json:"management_type"`
	Department       string `json:"department"`
	DepartmentLarkID string `json:"department_lark_id"`
	Description      string `json:"description"`
	Owner            string `json:"owner"`
	OwnerEmail       string `json:"owner_email"`
	Name             string `json:"name"`
	AccountID        string `json:"account_id"`
	Provider         string `json:"provider"`
	ManagementID     string `json:"management_id"`
	EtlLoadedAt      string `json:"etl_loaded_at"`
}

type CloudResourceWithHistory struct {
	ClassType        string     `json:"class_type"`
	AuthType         string     `json:"auth_type"`
	ManagementType   string     `json:"management_type"`
	Department       string     `json:"department"`
	DepartmentLarkID string     `json:"department_lark_id"`
	Description      string     `json:"description"`
	Owner            string     `json:"owner"`
	OwnerEmail       string     `json:"owner_email"`
	Name             string     `json:"name"`
	AccountID        string     `json:"account_id"`
	Provider         string     `json:"provider"`
	ManagementID     string     `json:"management_id"`
	EtlLoadedAt      string     `json:"etl_loaded_at"`
	IsDel            bool       `json:"is_del"`
	DeletedAt        *time.Time `json:"deleted_at"`
}
