package domain

import (
	"encoding/json"
	"time"

	"github.com/usual2970/certimate/internal/domain/expr"
	maputil "github.com/usual2970/certimate/internal/pkg/utils/map"
)

const CollectionNameWorkflow = "workflow"

type Workflow struct {
	Meta
	Name          string                `json:"name" db:"name"`
	Description   string                `json:"description" db:"description"`
	Trigger       WorkflowTriggerType   `json:"trigger" db:"trigger"`
	TriggerCron   string                `json:"triggerCron" db:"triggerCron"`
	Enabled       bool                  `json:"enabled" db:"enabled"`
	Content       *WorkflowNode         `json:"content" db:"content"`
	Draft         *WorkflowNode         `json:"draft" db:"draft"`
	HasDraft      bool                  `json:"hasDraft" db:"hasDraft"`
	LastRunId     string                `json:"lastRunId" db:"lastRunId"`
	LastRunStatus WorkflowRunStatusType `json:"lastRunStatus" db:"lastRunStatus"`
	LastRunTime   time.Time             `json:"lastRunTime" db:"lastRunTime"`
}

type WorkflowNodeType string

const (
	WorkflowNodeTypeStart               = WorkflowNodeType("start")
	WorkflowNodeTypeEnd                 = WorkflowNodeType("end")
	WorkflowNodeTypeApply               = WorkflowNodeType("apply")
	WorkflowNodeTypeUpload              = WorkflowNodeType("upload")
	WorkflowNodeTypeMonitor             = WorkflowNodeType("monitor")
	WorkflowNodeTypeDeploy              = WorkflowNodeType("deploy")
	WorkflowNodeTypeNotify              = WorkflowNodeType("notify")
	WorkflowNodeTypeBranch              = WorkflowNodeType("branch")
	WorkflowNodeTypeCondition           = WorkflowNodeType("condition")
	WorkflowNodeTypeExecuteResultBranch = WorkflowNodeType("execute_result_branch")
	WorkflowNodeTypeExecuteSuccess      = WorkflowNodeType("execute_success")
	WorkflowNodeTypeExecuteFailure      = WorkflowNodeType("execute_failure")
)

type WorkflowTriggerType string

const (
	WorkflowTriggerTypeAuto   = WorkflowTriggerType("auto")
	WorkflowTriggerTypeManual = WorkflowTriggerType("manual")
)

type WorkflowNode struct {
	Id   string           `json:"id"`
	Type WorkflowNodeType `json:"type"`
	Name string           `json:"name"`

	Config  map[string]any   `json:"config"`
	Inputs  []WorkflowNodeIO `json:"inputs"`
	Outputs []WorkflowNodeIO `json:"outputs"`

	Next     *WorkflowNode  `json:"next,omitempty"`
	Branches []WorkflowNode `json:"branches,omitempty"`

	Validated bool `json:"validated"`
}

type WorkflowNodeConfigForApply struct {
	Domains               string         `json:"domains"`                         // 域名列表,以半角分号分隔
	ContactEmail          string         `json:"contactEmail"`                    // 联系邮箱
	ChallengeType         string         `json:"challengeType"`                   // TODO: 验证方式。目前仅支持 dns-01
	Provider              string         `json:"provider"`                        // DNS 提供商
	ProviderAccessId      string         `json:"providerAccessId"`                // DNS 提供商授权记录 ID
	ProviderConfig        map[string]any `json:"providerConfig"`                  // DNS 提供商额外配置
	CAProvider            string         `json:"caProvider,omitempty"`            // CA 提供商(零值时使用全局配置)
	CAProviderAccessId    string         `json:"caProviderAccessId,omitempty"`    // CA 提供商授权记录 ID
	CAProviderConfig      map[string]any `json:"caProviderConfig,omitempty"`      // CA 提供商额外配置
	KeyAlgorithm          string         `json:"keyAlgorithm"`                    // 证书算法
	Nameservers           string         `json:"nameservers,omitempty"`           // DNS 服务器列表,以半角分号分隔
	DnsPropagationWait    int32          `json:"dnsPropagationWait,omitempty"`    // DNS 传播等待时间,等同于 lego 的 `--dns-propagation-wait` 参数
	DnsPropagationTimeout int32          `json:"dnsPropagationTimeout,omitempty"` // DNS 传播检查超时时间(零值时使用提供商的默认值)
	DnsTTL                int32          `json:"dnsTTL,omitempty"`                // DNS 解析记录 TTL(零值时使用提供商的默认值)
	DisableFollowCNAME    bool           `json:"disableFollowCNAME,omitempty"`    // 是否关闭 CNAME 跟随
	DisableARI            bool           `json:"disableARI,omitempty"`            // 是否关闭 ARI
	SkipBeforeExpiryDays  int32          `json:"skipBeforeExpiryDays,omitempty"`  // 证书到期前多少天前跳过续期(零值时默认值 30)
}

type WorkflowNodeConfigForUpload struct {
	Certificate string `json:"certificate"` // 证书 PEM 内容
	PrivateKey  string `json:"privateKey"`  // 私钥 PEM 内容
	Domains     string `json:"domains,omitempty"`
}

type WorkflowNodeConfigForMonitor struct {
	Host        string `json:"host"`                  // 主机地址
	Port        int32  `json:"port,omitempty"`        // 端口(零值时默认值 443)
	Domain      string `json:"domain,omitempty"`      // 域名(零值时默认值 [Host])
	RequestPath string `json:"requestPath,omitempty"` // 请求路径
}

type WorkflowNodeConfigForDeploy struct {
	Certificate         string         `json:"certificate"`                // 前序节点输出的证书,形如“${NodeId}#certificate”
	Provider            string         `json:"provider"`                   // 主机提供商
	ProviderAccessId    string         `json:"providerAccessId,omitempty"` // 主机提供商授权记录 ID
	ProviderConfig      map[string]any `json:"providerConfig,omitempty"`   // 主机提供商额外配置
	SkipOnLastSucceeded bool           `json:"skipOnLastSucceeded"`        // 上次部署成功时是否跳过
}

type WorkflowNodeConfigForNotify struct {
	Channel          string         `json:"channel,omitempty"`        // Deprecated: v0.4.x 将废弃
	Provider         string         `json:"provider"`                 // 通知提供商
	ProviderAccessId string         `json:"providerAccessId"`         // 通知提供商授权记录 ID
	ProviderConfig   map[string]any `json:"providerConfig,omitempty"` // 通知提供商额外配置
	Subject          string         `json:"subject"`                  // 通知主题
	Message          string         `json:"message"`                  // 通知内容
}

type WorkflowNodeConfigForCondition struct {
	Expression expr.Expr `json:"expression"` // 条件表达式
}

func (n *WorkflowNode) GetConfigForApply() WorkflowNodeConfigForApply {
	return WorkflowNodeConfigForApply{
		Domains:               maputil.GetString(n.Config, "domains"),
		ContactEmail:          maputil.GetString(n.Config, "contactEmail"),
		Provider:              maputil.GetString(n.Config, "provider"),
		ProviderAccessId:      maputil.GetString(n.Config, "providerAccessId"),
		ProviderConfig:        maputil.GetKVMapAny(n.Config, "providerConfig"),
		CAProvider:            maputil.GetString(n.Config, "caProvider"),
		CAProviderAccessId:    maputil.GetString(n.Config, "caProviderAccessId"),
		CAProviderConfig:      maputil.GetKVMapAny(n.Config, "caProviderConfig"),
		KeyAlgorithm:          maputil.GetString(n.Config, "keyAlgorithm"),
		Nameservers:           maputil.GetString(n.Config, "nameservers"),
		DnsPropagationWait:    maputil.GetInt32(n.Config, "dnsPropagationWait"),
		DnsPropagationTimeout: maputil.GetInt32(n.Config, "dnsPropagationTimeout"),
		DnsTTL:                maputil.GetInt32(n.Config, "dnsTTL"),
		DisableFollowCNAME:    maputil.GetBool(n.Config, "disableFollowCNAME"),
		DisableARI:            maputil.GetBool(n.Config, "disableARI"),
		SkipBeforeExpiryDays:  maputil.GetOrDefaultInt32(n.Config, "skipBeforeExpiryDays", 30),
	}
}

func (n *WorkflowNode) GetConfigForUpload() WorkflowNodeConfigForUpload {
	return WorkflowNodeConfigForUpload{
		Certificate: maputil.GetString(n.Config, "certificate"),
		PrivateKey:  maputil.GetString(n.Config, "privateKey"),
		Domains:     maputil.GetString(n.Config, "domains"),
	}
}

func (n *WorkflowNode) GetConfigForMonitor() WorkflowNodeConfigForMonitor {
	host := maputil.GetString(n.Config, "host")
	return WorkflowNodeConfigForMonitor{
		Host:        host,
		Port:        maputil.GetOrDefaultInt32(n.Config, "port", 443),
		Domain:      maputil.GetOrDefaultString(n.Config, "domain", host),
		RequestPath: maputil.GetString(n.Config, "path"),
	}
}

func (n *WorkflowNode) GetConfigForDeploy() WorkflowNodeConfigForDeploy {
	return WorkflowNodeConfigForDeploy{
		Certificate:         maputil.GetString(n.Config, "certificate"),
		Provider:            maputil.GetString(n.Config, "provider"),
		ProviderAccessId:    maputil.GetString(n.Config, "providerAccessId"),
		ProviderConfig:      maputil.GetKVMapAny(n.Config, "providerConfig"),
		SkipOnLastSucceeded: maputil.GetBool(n.Config, "skipOnLastSucceeded"),
	}
}

func (n *WorkflowNode) GetConfigForNotify() WorkflowNodeConfigForNotify {
	return WorkflowNodeConfigForNotify{
		Channel:          maputil.GetString(n.Config, "channel"),
		Provider:         maputil.GetString(n.Config, "provider"),
		ProviderAccessId: maputil.GetString(n.Config, "providerAccessId"),
		ProviderConfig:   maputil.GetKVMapAny(n.Config, "providerConfig"),
		Subject:          maputil.GetString(n.Config, "subject"),
		Message:          maputil.GetString(n.Config, "message"),
	}
}

func (n *WorkflowNode) GetConfigForCondition() WorkflowNodeConfigForCondition {
	expression := n.Config["expression"]
	if expression == nil {
		return WorkflowNodeConfigForCondition{}
	}

	exprRaw, _ := json.Marshal(expression)
	expr, err := expr.UnmarshalExpr([]byte(exprRaw))
	if err != nil {
		return WorkflowNodeConfigForCondition{}
	}

	return WorkflowNodeConfigForCondition{
		Expression: expr,
	}
}

type WorkflowNodeIO struct {
	Label         string                      `json:"label"`
	Name          string                      `json:"name"`
	Type          string                      `json:"type"`
	Required      bool                        `json:"required"`
	Value         any                         `json:"value"`
	ValueSelector WorkflowNodeIOValueSelector `json:"valueSelector"`
}

type WorkflowNodeIOValueSelector = expr.ExprValueSelector

const WorkflowNodeIONameCertificate string = "certificate"