package deployer

import (
	"context"
	"encoding/json"
	"fmt"
	"regexp"
	"strings"

	xerrors "github.com/pkg/errors"
	"github.com/usual2970/certimate/internal/domain"
	"github.com/usual2970/certimate/internal/pkg/core/uploader"
	volcenginelive "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/volcengine-live"
	"github.com/usual2970/certimate/internal/pkg/utils/cast"
	"github.com/volcengine/volc-sdk-golang/base"
	live "github.com/volcengine/volc-sdk-golang/service/live/v20230101"
)

type VolcengineLiveDeployer struct {
	option      *DeployerOption
	infos       []string
	sdkClient   *live.Live
	sslUploader uploader.Uploader
}

func NewVolcengineLiveDeployer(option *DeployerOption) (Deployer, error) {
	access := &domain.VolcengineAccess{}
	if err := json.Unmarshal([]byte(option.Access), access); err != nil {
		return nil, xerrors.Wrap(err, "failed to get access")
	}
	client := live.NewInstance()
	client.SetCredential(base.Credentials{
		AccessKeyID:     access.AccessKeyId,
		SecretAccessKey: access.SecretAccessKey,
	})
	uploader, err := volcenginelive.New(&volcenginelive.VolcEngineLiveUploaderConfig{
		AccessKeyId:     access.AccessKeyId,
		AccessKeySecret: access.SecretAccessKey,
	})
	if err != nil {
		return nil, xerrors.Wrap(err, "failed to create ssl uploader")
	}
	return &VolcengineLiveDeployer{
		option:      option,
		infos:       make([]string, 0),
		sdkClient:   client,
		sslUploader: uploader,
	}, nil
}

func (d *VolcengineLiveDeployer) GetID() string {
	return fmt.Sprintf("%s-%s", d.option.AccessRecord.GetString("name"), d.option.AccessRecord.Id)
}

func (d *VolcengineLiveDeployer) GetInfos() []string {
	return d.infos
}

func (d *VolcengineLiveDeployer) Deploy(ctx context.Context) error {
	apiCtx := context.Background()
	// 上传证书
	upres, err := d.sslUploader.Upload(apiCtx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
	if err != nil {
		return err
	}

	d.infos = append(d.infos, toStr("已上传证书", upres))

	domains := make([]string, 0)
	configDomain := d.option.DeployConfig.GetConfigAsString("domain")
	if strings.HasPrefix(configDomain, "*.") {
		// 如果是泛域名,获取所有的域名并匹配
		matchDomains, err := d.getDomainsByWildcardDomain(apiCtx, configDomain)
		if err != nil {
			d.infos = append(d.infos, toStr("获取域名列表失败", upres))
			return xerrors.Wrap(err, "failed to execute sdk request 'live.ListDomainDetail'")
		}
		if len(matchDomains) == 0 {
			return xerrors.Errorf("未查询到匹配的域名: %s", configDomain)
		}
		domains = matchDomains
	} else {
		domains = append(domains, configDomain)
	}

	// 部署证书
	// REF: https://www.volcengine.com/docs/6469/1186278#%E7%BB%91%E5%AE%9A%E8%AF%81%E4%B9%A6d
	for i := range domains {
		bindCertReq := &live.BindCertBody{
			ChainID: upres.CertId,
			Domain:  domains[i],
			HTTPS:   cast.BoolPtr(true),
		}
		bindCertResp, err := d.sdkClient.BindCert(apiCtx, bindCertReq)
		if err != nil {
			return xerrors.Wrap(err, "failed to execute sdk request 'live.BindCert'")
		} else {
			d.infos = append(d.infos, toStr(fmt.Sprintf("%s域名的证书已修改", domains[i]), bindCertResp))
		}
	}

	return nil
}

func (d *VolcengineLiveDeployer) getDomainsByWildcardDomain(ctx context.Context, wildcardDomain string) ([]string, error) {
	pageNum := int32(1)
	searchTotal := 0
	domains := make([]string, 0)
	for {
		listDomainDetailReq := &live.ListDomainDetailBody{
			PageNum:  pageNum,
			PageSize: 1000,
		}
		// 查询域名列表
		// REF: https://www.volcengine.com/docs/6469/1186277#%E6%9F%A5%E8%AF%A2%E5%9F%9F%E5%90%8D%E5%88%97%E8%A1%A8
		listDomainDetailResp, err := d.sdkClient.ListDomainDetail(ctx, listDomainDetailReq)
		if err != nil {
			return domains, err
		}
		if listDomainDetailResp.Result.DomainList != nil {
			for _, item := range listDomainDetailResp.Result.DomainList {
				if matchWildcardDomain(item.Domain, wildcardDomain) {
					domains = append(domains, item.Domain)
				}
			}
		}
		searchTotal += len(listDomainDetailResp.Result.DomainList)
		if int(listDomainDetailResp.Result.Total) > searchTotal {
			pageNum++
		} else {
			break
		}
	}

	return domains, nil
}

func matchWildcardDomain(domain, wildcardDomain string) bool {
	if strings.HasPrefix(wildcardDomain, "*.") {
		if "*."+domain == wildcardDomain {
			return true
		}
		regexPattern := "^([a-zA-Z0-9_-]+)\\." + regexp.QuoteMeta(wildcardDomain[2:]) + "$"
		regex := regexp.MustCompile(regexPattern)
		return regex.MatchString(domain)
	}
	return domain == wildcardDomain
}