package v1

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"

	"github.com/spf13/viper"

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

type webService struct {
	store store.Factory
}

type WebSrv interface {
	Company(c context.Context, list v1.CompanySearchList) (ListData, error)
	CompanyBasic(c context.Context, list v1.CompanySearchList) (ListData, error)
	Problem(c context.Context, list v1.CompanySearchList) (ListData, error)
	Sanction(c context.Context, list v1.CompanySearchList) (ListData, error)
	Reference(c context.Context, list v1.CompanySearchList) (ListData, error)
	CompanyInfoPrincipalsContacts(c context.Context, list v1.CompanySearchList) (ListData, error)
	ResetCompanies(c context.Context, list v1.CompanySearchList) error
	FreshInformation(c context.Context, list v1.CompanySearchList) error
}

var _ WebSrv = (*webService)(nil)

func newWebService(srv *service) *webService {
	return &webService{store: srv.store}
}

func (s *webService) CompanyBasic(c context.Context, list v1.CompanySearchList) (ListData, error) {
	log.L(c).Infof("start get company basic list")
	offset := GetPage(list.Page, list.PageSize)
	var data interface{}
	var total int64
	var err error

	data, total, err = s.store.Company().ListCompanyBasicByWeb(list.CompanyName, list.Country, list.CompanyID, offset, list.PageSize)
	if err != nil {
		log.L(c).Errorf("get company basic list error: %v", err)

		return ListData{}, err
	}

	log.L(c).Infof("end get company basic list")

	return ListData{
		Items: data,
		Total: total,
	}, nil
}

func (s *webService) CompanyInfoPrincipalsContacts(c context.Context, list v1.CompanySearchList) (ListData, error) {
	log.L(c).Infof("start get company basic list")

	go s.store.Company().SaveUserLog(c, &v1.UserLog{
		UserID:  list.User,
		Content: "get company info and principals:" + strconv.Itoa(list.CompanyBasicID),
	})
	offset := GetPage(list.Page, list.PageSize)
	var total int64
	var err error

	result, total, err := s.store.Company().ListCompanyInfoPrincipalsContactsByWeb(list.CompanyBasicID, offset, list.PageSize)
	if err != nil {
		log.L(c).Errorf("get company basic list error: %v", err)

		return ListData{}, err
	}

	// 返回给前端视
	var infoPrincipalsView []v1.OrganizationPri
	var infoPrincipals *v1.CompanyInfoPrincipalReq

	// 调用邓白氏api获取
	if len(result) == 0 {
		infoPrincipals, err = s.SearchCompanyInfoPrincipals(c, list.CompanyBasicID)
		if err != nil {
			log.L(c).Errorf("get company info and principals error: %v", err)

			return ListData{}, err
		}
	} else {
		for _, v := range result {
			err := json.Unmarshal([]byte(v.RawData), &infoPrincipals)
			if err != nil {
				log.L(c).Errorf("unmarshal company info and principals error: %v", err)

				return ListData{}, err
			}
		}
	}

	infoPrincipals.OrganizationPri.PrimaryAddressStr = infoPrincipals.OrganizationPri.PrimaryAddress.StreetAddress.Line1 +
		" | " + infoPrincipals.OrganizationPri.PrimaryAddress.StreetAddress.Line2 +
		" | " + infoPrincipals.OrganizationPri.PrimaryAddress.MinorTownName +
		" | " + infoPrincipals.OrganizationPri.PrimaryAddress.AddressCountry.Name +
		" | " + infoPrincipals.OrganizationPri.PrimaryAddress.AddressLocality.Name +
		" | " + infoPrincipals.OrganizationPri.PrimaryAddress.AddressRegion.Name +
		" | " + infoPrincipals.OrganizationPri.PrimaryAddress.PostalCode

	var currentPrincipalsStr string
	for _, v := range infoPrincipals.OrganizationPri.CurrentPrincipals {
		currentPrincipalsStr += v.FullName + " : "
		// titles
		for i, t := range v.JobTitles {
			if i == 0 {
				currentPrincipalsStr += t.Title
			} else {
				currentPrincipalsStr += "," + t.Title
			}
		}
		currentPrincipalsStr += " | \n"
	}
	infoPrincipals.OrganizationPri.CurrentPrincipalsStr = currentPrincipalsStr
	infoPrincipalsView = append(infoPrincipalsView, infoPrincipals.OrganizationPri)

	log.L(c).Infof("end get company basic list")

	return ListData{
		Items: infoPrincipalsView,
		Total: total,
	}, nil
}

// 调用邓白氏api获取
func (s *webService) SearchCompanyInfoPrincipals(ctx context.Context, companyBasicID int) (*v1.CompanyInfoPrincipalReq, error) {
	log.L(ctx).Infof("start get company basic list")
	combasic, err := s.store.Company().GetCompanyBasicByID(ctx, companyBasicID)
	if err != nil {
		log.L(ctx).Errorf("get company basic error: %v", err)

		return nil, err
	}

	if combasic.DunsNumber == "" {
		log.L(ctx).Errorf("duns number is empty")

		return nil, errors.New("duns number is empty")
	}

	dtLockToken, err := GetDunsToken(ctx, 0)

	infoPrincipals, err := GetCompanyInfoAndPrincipals(ctx, combasic, dtLockToken)
	if err != nil {
		log.L(ctx).Errorf("get company info and principals error: %v", err)

		return nil, err
	}

	// 过滤多余信息
	var industryCodes []v1.IndustryCode
	for _, icode := range infoPrincipals.OrganizationPri.IndustryCodes {
		if icode.TypeDnBCode == 399 {
			industryCodes = append(industryCodes, icode)
		}
	}
	infoPrincipals.OrganizationPri.IndustryCodes = industryCodes
	// 只保留前3个网站
	if len(infoPrincipals.OrganizationPri.WebsiteAddress) > 3 {
		infoPrincipals.OrganizationPri.WebsiteAddress = infoPrincipals.OrganizationPri.WebsiteAddress[:3]
	}
	if len(infoPrincipals.OrganizationPri.TradeStyleNames) > 3 {
		infoPrincipals.OrganizationPri.Email = infoPrincipals.OrganizationPri.Email[:3]
	}
	if len(infoPrincipals.OrganizationPri.Email) > 3 {
		infoPrincipals.OrganizationPri.MostSeniorPrincipal.JobTitles = infoPrincipals.OrganizationPri.MostSeniorPrincipal.JobTitles[:3]
	}
	for i, v := range infoPrincipals.OrganizationPri.CurrentPrincipals {
		if len(v.JobTitles) > 3 {
			infoPrincipals.OrganizationPri.CurrentPrincipals[i].JobTitles = v.JobTitles[:3]
		}
	}

	// 只保留前6组
	if len(infoPrincipals.OrganizationPri.StockExchanges) > 6 {
		infoPrincipals.OrganizationPri.StockExchanges = infoPrincipals.OrganizationPri.StockExchanges[:6]
	}

	var financials []v1.Financial
	for _, fi := range infoPrincipals.OrganizationPri.Financials {
		for _, f := range fi.YearlyRevenue {
			if f.Currency == "USD" {
				financials = append(financials, fi)
			}
		}
	}
	infoPrincipals.OrganizationPri.Financials = financials

	marshal, _ := json.Marshal(infoPrincipals)
	err = s.store.Company().CreateCompanyInfoPrincipalsContacts(ctx, &v1.CompanyInfoPrincipalsContacts{
		CompanyBasicID: combasic.ID,
		RawData:        marshal,
	})
	if err != nil {
		log.L(ctx).Errorf("create company info and principals error: %v", err)

		return nil, err
	}

	return infoPrincipals, nil
}

func (s *webService) Company(c context.Context, list v1.CompanySearchList) (ListData, error) {
	log.L(c).Infof("start get company list")
	var data interface{}
	var total int64
	var err error

	data, total, err = s.store.Company().ListCompanyByWeb(list.CompanyName, list.Country, list.From, GetPage(list.Page, list.PageSize), list.PageSize)
	if err != nil {
		log.L(c).Errorf("get company list error: %v", err)

		return ListData{}, err
	}

	log.L(c).Infof("end get company list")

	return ListData{
		Items: data,
		Total: total,
	}, nil
}

func (s *webService) Problem(c context.Context, list v1.CompanySearchList) (ListData, error) {
	log.L(c).Infof("start get problem list")
	var total int64
	var err error

	data, total, err := s.store.Company().ListProblemByWeb(list.CompanyName, list.CompanyID, GetPage(list.Page, list.PageSize), list.PageSize)
	if err != nil {
		log.L(c).Errorf("get sanction list error: %v", err)

		return ListData{}, err
	}

	log.L(c).Infof("end get problem list")

	return ListData{
		Items: data,
		Total: total,
	}, nil
}

func (s *webService) Sanction(c context.Context, list v1.CompanySearchList) (ListData, error) {
	log.L(c).Infof("start get sanction list")
	var total int64
	var err error

	data, total, err := s.store.Company().ListSanctionByWeb(list.CompanyName, list.EntityID, list.CompanyID, GetPage(list.Page, list.PageSize), list.PageSize)
	if err != nil {
		log.L(c).Errorf("get sanction list error: %v", err)

		return ListData{}, err
	}

	log.L(c).Infof("end get sanction list")

	return ListData{
		Items: data,
		Total: total,
	}, nil
}

func (s *webService) Reference(c context.Context, list v1.CompanySearchList) (ListData, error) {
	log.L(c).Infof("start get reference list")
	var data interface{}
	var total int64
	var err error

	data, total, err = s.store.Company().ListReferenceByWeb(list.SanctionID, GetPage(list.Page, list.PageSize), list.PageSize)
	if err != nil {
		log.L(c).Errorf("get reference list error: %v", err)

		return ListData{}, err
	}

	log.L(c).Infof("end get reference list")

	return ListData{
		Items: data,
		Total: total,
	}, nil
}

func (s *webService) ResetCompanies(c context.Context, list v1.CompanySearchList) error {
	log.L(c).Infof("start reset companies")

	sdMsg(c, errors.New("正在开始获取历史数据"))

	s.store.Company().SaveUserLog(c, &v1.UserLog{
		UserID:  list.User,
		Content: "reset companies",
	})

	// copy history data
	tableNames := []string{"company", "company_basic", "company_problem", "company_sanction", "company_sanction_references", "company_info_principalscontacts"}
	for _, tableName := range tableNames {
		if err := SaveDbLocal(c, tableName); err != nil {
			log.L(c).Errorf("backup table error: %v", err)

			return err
		}
	}

	// 清空表
	err := s.clearTable(c)
	if err != nil {
		log.L(c).Errorf("clear table error: %v", err)

		return err
	}

	// 开始检索历史数据
	dunsSrv := dunsService{store: s.store}
	err = dunsSrv.DunCheck(c)
	if err != nil {
		log.L(c).Errorf("DunCheck error: %v", err)

		return err
	}

	sdMsg(c, errors.New("历史数据检索完成"))
	log.L(c).Infof("end reset companies")

	return nil
}

func (s *webService) FreshInformation(c context.Context, list v1.CompanySearchList) error {
	log.L(c).Infof("start fresh information")

	sdMsg(c, errors.New("开始获取刷新公司数据"+list.CompanyName))

	s.store.Company().SaveUserLog(c, &v1.UserLog{
		UserID:  list.User,
		Content: "fresh company information:" + list.CompanyName,
	})

	// 清理数据库公司信息
	err := s.store.Company().DeleteCompanyInformation(c, list.CompanyID)
	if err != nil {
		log.L(c).Errorf("delete company information error: %v", err)

		sdMsg(c, err)

		return err
	}

	// 获取公司信息
	dunsSrv := dunsService{store: s.store}
	err = dunsSrv.DunCheck(c)
	if err != nil {
		log.L(c).Errorf("DunCheck error: %v", err)

		sdMsg(c, err)

		return err
	}

	sdMsg(c, errors.New("获取刷新公司数据成功:"+list.CompanyName))

	log.L(c).Infof("end fresh information")

	return nil
}

func (s *webService) clearTable(c context.Context) error {
	log.L(c).Infof("start clear table")

	err := s.store.Company().DeleteAll(c)
	if err != nil {
		log.L(c).Errorf("delete all error: %v", err)

		return err
	}

	err = s.store.Company().BatchUpdateCompanyStatus(c, 0)
	if err != nil {
		log.L(c).Errorf("batch update company status error: %v", err)

		return err
	}

	log.L(c).Infof("end clear table")

	return nil
}

func SaveDbLocal(c context.Context, tableName string) error {
	log.L(c).Infof("start backup table: %s", tableName)

	// 从配置中获取数据库连接信息和保存路径
	hostAndPort := viper.GetString("mysql.host")
	host := strings.Split(hostAndPort, ":")[0]
	port := strings.Split(hostAndPort, ":")[1]
	username := viper.GetString("mysql.username-sub")
	password := viper.GetString("mysql.password-sub")
	database := viper.GetString("mysql.database-sub")
	savePath := viper.GetString("mysql.save-path")

	// 构建保存文件路径
	backupFile := fmt.Sprintf("%s%s_%s.sql", savePath, tableName, time.Now().Format("2006-01-02_15-04-05"))

	// 构建 mysqldump 命令
	cmd := exec.Command("mysqldump", "-h", host, "-P", port, "-u", username, fmt.Sprintf("-p%s", password), database, tableName)

	// 创建保存文件
	file, err := os.Create(backupFile)
	if err != nil {
		log.L(c).Errorf("create backup file error: %v", err)
		return err
	}
	defer file.Close()

	// 将 mysqldump 输出重定向到文件
	cmd.Stdout = file

	// 打印cmd内容
	log.L(c).Infof("cmd: %v", cmd)
	// 执行 mysqldump 命令
	if err := cmd.Run(); err != nil {
		log.L(c).Errorf("execute mysqldump error: %v", err)
		return err
	}

	log.L(c).Infof("backup completed successfully: %s", backupFile)
	return nil
}

func (s *webService) RestoreTable(c context.Context, backupFile string) error {
	log.L(c).Infof("start restore table from: %s", backupFile)

	// 从配置中获取数据库连接信息
	host := "127.0.0.1"
	username := "root"
	password := "123456"
	database := "security"

	// 构建 mysql 命令
	cmd := exec.Command("mysql", "-h", host, "-u", username, fmt.Sprintf("-p%s", password), database)

	// 打开备份文件
	file, err := os.Open(backupFile)
	if err != nil {
		log.L(c).Errorf("open backup file error: %v", err)
		return err
	}
	defer file.Close()

	// 将备份文件的内容重定向到 mysql 命令的标准输入
	cmd.Stdin = file

	// 打印cmd内容
	log.L(c).Infof("cmd: %v", cmd)
	// 执行 mysql 命令
	if err := cmd.Run(); err != nil {
		log.L(c).Errorf("execute mysql error: %v", err)
		return err
	}

	log.L(c).Infof("restore completed successfully from: %s", backupFile)
	return nil
}

func GetPage(page int, pageSize int) int {
	result := 0
	if page > 0 && pageSize > 0 {
		result = (page - 1) * pageSize
	}

	return result
}

type ListData struct {
	Items interface{} `json:"items"`
	Total int64       `json:"total"`
}
