package wecom

import (
	"context"
	"fmt"
	"strings"

	"github.com/bytedance/sonic"

	wecomapp "github.com/break/junhong_cmp_fiber/internal/application/wecom"
	"github.com/break/junhong_cmp_fiber/internal/model/dto"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
)

type approvalAttachmentPort interface {
	Upload(ctx context.Context, applicationID, instanceID uint, reference ApprovalFileReference) (string, error)
}

type approvalFormBuildResult struct {
	Contents []map[string]any
}

type attachmentPreparationError struct {
	err error
}

func (e *attachmentPreparationError) Error() string {
	return e.err.Error()
}

func (e *attachmentPreparationError) Unwrap() error {
	return e.err
}

// buildApprovalForm 按后台已校验映射将渠道无关业务快照组装为企微控件值。
func buildApprovalForm(ctx context.Context, applicationID, instanceID uint, mappingJSON, templateJSON, snapshotJSON []byte, attachments approvalAttachmentPort) (approvalFormBuildResult, error) {
	var mappings []dto.WeComControlMappingItem
	if err := sonic.Unmarshal(mappingJSON, &mappings); err != nil || len(mappings) == 0 {
		return approvalFormBuildResult{}, errors.New(errors.CodeInvalidStatus, "企业微信审批控件映射无效")
	}
	var snapshot map[string]any
	if err := sonic.Unmarshal(snapshotJSON, &snapshot); err != nil || snapshot == nil {
		return approvalFormBuildResult{}, errors.New(errors.CodeInvalidStatus, "审批业务快照无效")
	}
	var template wecomapp.TemplateDefinition
	if err := sonic.Unmarshal(templateJSON, &template); err != nil {
		return approvalFormBuildResult{}, errors.New(errors.CodeInvalidStatus, "企业微信审批模板快照无效")
	}
	requiredControls := make(map[string]bool, len(template.Controls))
	for _, control := range template.Controls {
		requiredControls[control.ID] = control.Required
	}
	contents := make([]map[string]any, 0, len(mappings))
	attachmentCount := 0
	for _, mapping := range mappings {
		raw, exists := lookupSnapshotValue(snapshot, mapping.BusinessField)
		if !exists || raw == nil {
			if requiredControls[mapping.ControlID] {
				return approvalFormBuildResult{}, errors.New(errors.CodeInvalidStatus, "审批业务快照缺少模板必填字段: "+mapping.BusinessField)
			}
			continue
		}
		value, count, err := buildControlValue(ctx, applicationID, instanceID, mapping, raw, attachments)
		if err != nil {
			return approvalFormBuildResult{}, err
		}
		attachmentCount += count
		if attachmentCount > constants.WeComApprovalMaxAttachmentCount {
			return approvalFormBuildResult{}, errors.New(errors.CodeInvalidParam, "单张企业微信审批单最多支持 6 个附件")
		}
		contents = append(contents, map[string]any{
			"control": mapping.ControlType, "id": mapping.ControlID, "value": value,
		})
	}
	return approvalFormBuildResult{Contents: contents}, nil
}

// lookupSnapshotValue 支持用点分路径读取嵌套业务快照，避免模板映射绑定具体 DTO。
func lookupSnapshotValue(snapshot map[string]any, path string) (any, bool) {
	segments := strings.Split(strings.TrimSpace(path), ".")
	var current any = snapshot
	for _, segment := range segments {
		object, ok := current.(map[string]any)
		if !ok {
			return nil, false
		}
		current, ok = object[segment]
		if !ok {
			return nil, false
		}
	}
	return current, true
}

// buildControlValue 按企微控件类型生成官方要求的 value 结构，复杂结构允许业务快照直接传入对象。
func buildControlValue(ctx context.Context, applicationID, instanceID uint, mapping dto.WeComControlMappingItem, raw any, attachments approvalAttachmentPort) (map[string]any, int, error) {
	if object, ok := raw.(map[string]any); ok && !strings.EqualFold(mapping.ControlType, "File") {
		return object, 0, nil
	}
	switch strings.ToLower(strings.TrimSpace(mapping.ControlType)) {
	case "text", "textarea":
		return map[string]any{"text": fmt.Sprint(raw)}, 0, nil
	case "number":
		return map[string]any{"new_number": fmt.Sprint(raw)}, 0, nil
	case "money":
		return map[string]any{"new_money": fmt.Sprint(raw)}, 0, nil
	case "date":
		return map[string]any{"date": map[string]any{"type": "day", "s_timestamp": fmt.Sprint(raw)}}, 0, nil
	case "selector":
		businessValue := fmt.Sprint(raw)
		key := mapping.OptionMapping[businessValue]
		if key == "" {
			return nil, 0, errors.New(errors.CodeInvalidStatus, "审批业务枚举值没有企微选项映射: "+mapping.BusinessField)
		}
		return map[string]any{"selector": map[string]any{"type": "single", "options": []map[string]string{{"key": key}}}}, 0, nil
	case "contact":
		return map[string]any{"members": []map[string]string{{"userid": fmt.Sprint(raw)}}}, 0, nil
	case "department":
		return map[string]any{"departments": []map[string]string{{"openapi_id": fmt.Sprint(raw)}}}, 0, nil
	case "file":
		if attachments == nil {
			return nil, 0, errors.New(errors.CodeServiceUnavailable, "企业微信审批附件上传服务未配置")
		}
		references, err := parseApprovalFileReferences(raw)
		if err != nil {
			return nil, 0, err
		}
		files := make([]map[string]string, 0, len(references))
		for _, reference := range references {
			mediaID, err := attachments.Upload(ctx, applicationID, instanceID, reference)
			if err != nil {
				return nil, 0, &attachmentPreparationError{err: err}
			}
			files = append(files, map[string]string{"file_id": mediaID})
		}
		return map[string]any{"files": files}, len(files), nil
	default:
		return nil, 0, errors.New(errors.CodeInvalidStatus, "暂不支持的企业微信审批控件类型: "+mapping.ControlType)
	}
}

// parseApprovalFileReferences 兼容现有退款和线下充值的字符串 Key 列表，以及带文件名的结构化引用。
func parseApprovalFileReferences(raw any) ([]ApprovalFileReference, error) {
	if storageKey, ok := raw.(string); ok && strings.TrimSpace(storageKey) != "" {
		return []ApprovalFileReference{{StorageKey: strings.TrimSpace(storageKey)}}, nil
	}
	bytes, err := sonic.Marshal(raw)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInvalidParam, err, "编码审批附件引用失败")
	}
	var references []ApprovalFileReference
	if err := sonic.Unmarshal(bytes, &references); err != nil {
		var storageKeys []string
		if keyErr := sonic.Unmarshal(bytes, &storageKeys); keyErr == nil && len(storageKeys) > 0 {
			references = make([]ApprovalFileReference, 0, len(storageKeys))
			for _, storageKey := range storageKeys {
				if strings.TrimSpace(storageKey) != "" {
					references = append(references, ApprovalFileReference{StorageKey: strings.TrimSpace(storageKey)})
				}
			}
			if len(references) > 0 {
				return references, nil
			}
		}
		var single ApprovalFileReference
		if singleErr := sonic.Unmarshal(bytes, &single); singleErr != nil {
			return nil, errors.New(errors.CodeInvalidParam, "审批附件必须提供对象存储引用")
		}
		references = []ApprovalFileReference{single}
	}
	if len(references) == 0 {
		return nil, errors.New(errors.CodeInvalidParam, "审批附件不能为空")
	}
	return references, nil
}
