package wecom

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"strings"

	"github.com/bytedance/sonic"
	"gorm.io/gorm"

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

// ApprovalProvider 实现通用 Approval Port 的企微准备与渠道上下文写入。
type ApprovalProvider struct {
	db           *gorm.DB
	scenes       *SceneRepository
	applications *ApplicationRepository
	members      *MemberRepository
	templates    wecomapp.TemplateProvider
}

type approvalPreparationContext struct {
	ApplicationID       uint                        `json:"application_id"`
	BusinessType        string                      `json:"business_type"`
	TemplateID          string                      `json:"template_id"`
	CreatorUserID       string                      `json:"creator_userid"`
	CreatorName         string                      `json:"creator_name"`
	CreatorSource       string                      `json:"creator_source"`
	ControlMapping      []map[string]any            `json:"control_mapping"`
	TemplateSnapshot    wecomapp.TemplateDefinition `json:"template_snapshot"`
	TemplateFingerprint string                      `json:"template_fingerprint"`
}

// NewApprovalProvider 创建企业微信通用审批渠道 Adapter。
func NewApprovalProvider(db *gorm.DB, scenes *SceneRepository, applications *ApplicationRepository, members *MemberRepository, templates wecomapp.TemplateProvider) *ApprovalProvider {
	return &ApprovalProvider{db: db, scenes: scenes, applications: applications, members: members, templates: templates}
}

// Prepare 在业务事务前校验场景、模板和可用的企微发起人。
func (p *ApprovalProvider) Prepare(ctx context.Context, request approvalapp.PrepareRequest) (approvalapp.ProviderPreparation, error) {
	if p == nil || p.db == nil || p.scenes == nil || p.applications == nil || p.members == nil || p.templates == nil {
		return approvalapp.ProviderPreparation{}, errors.New(errors.CodeServiceUnavailable, "企业微信审批 Adapter 未配置")
	}
	scene, err := p.scenes.GetEnabled(ctx, strings.TrimSpace(request.BusinessType))
	if err != nil {
		return approvalapp.ProviderPreparation{}, err
	}
	application, err := p.applications.GetEnabled(ctx, scene.ApplicationID)
	if err != nil {
		return approvalapp.ProviderPreparation{}, err
	}
	creator, source, err := p.resolveCreator(ctx, application, request.SubmitterAccountID)
	if err != nil {
		return approvalapp.ProviderPreparation{}, err
	}
	definition, err := p.templates.GetTemplateDetail(ctx, scene.ApplicationID, scene.TemplateID)
	if err != nil {
		return approvalapp.ProviderPreparation{}, err
	}
	fingerprint, err := templateFingerprint(definition)
	if err != nil {
		return approvalapp.ProviderPreparation{}, err
	}
	if fingerprint != scene.TemplateFingerprint {
		return approvalapp.ProviderPreparation{}, errors.New(errors.CodeInvalidStatus, "企业微信审批模板已变化，请管理员重新校验场景配置")
	}
	var mapping []map[string]any
	if err := sonic.Unmarshal(scene.ControlMapping, &mapping); err != nil || len(mapping) == 0 {
		return approvalapp.ProviderPreparation{}, errors.New(errors.CodeInvalidStatus, "企业微信审批控件映射无效")
	}
	channelContext, err := sonic.Marshal(approvalPreparationContext{
		ApplicationID: scene.ApplicationID, BusinessType: scene.BusinessType, TemplateID: scene.TemplateID,
		CreatorUserID: creator.UserID, CreatorName: creator.Name, CreatorSource: source,
		ControlMapping: mapping, TemplateSnapshot: definition, TemplateFingerprint: fingerprint,
	})
	if err != nil {
		return approvalapp.ProviderPreparation{}, errors.Wrap(errors.CodeInternalError, err, "编码企业微信审批准备结果失败")
	}
	return approvalapp.ProviderPreparation{Provider: constants.IntegrationProviderWeCom, ChannelContext: channelContext}, nil
}

// CreateContextInTx 在调用方事务中保存不含凭据的企微提交快照。
func (p *ApprovalProvider) CreateContextInTx(ctx context.Context, tx *gorm.DB, preparation approvalapp.ProviderPreparation, instanceID uint) error {
	if p == nil || tx == nil || preparation.Provider != constants.IntegrationProviderWeCom || instanceID == 0 {
		return errors.New(errors.CodeInvalidParam, "企业微信审批渠道上下文参数无效")
	}
	var prepared approvalPreparationContext
	if err := sonic.Unmarshal(preparation.ChannelContext, &prepared); err != nil {
		return errors.Wrap(errors.CodeInvalidParam, err, "解析企业微信审批准备结果失败")
	}
	mappingJSON, err := sonic.Marshal(prepared.ControlMapping)
	if err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "编码企业微信审批控件映射失败")
	}
	templateJSON, err := sonic.Marshal(prepared.TemplateSnapshot)
	if err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "编码企业微信审批模板快照失败")
	}
	record := model.WeComApprovalContext{
		ApprovalInstanceID: instanceID, ApplicationID: prepared.ApplicationID, BusinessType: prepared.BusinessType,
		TemplateID: prepared.TemplateID, CreatorUserID: prepared.CreatorUserID, CreatorName: prepared.CreatorName,
		CreatorSource: prepared.CreatorSource, ControlMapping: mappingJSON, TemplateSnapshot: templateJSON,
		TemplateFingerprint: prepared.TemplateFingerprint, SubmissionStatus: constants.WeComSubmissionStatusReady,
	}
	if err := tx.WithContext(ctx).Create(&record).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "创建企业微信审批渠道上下文失败")
	}
	return nil
}

// resolveCreator 保留真实业务提交人，同时按账号类型选择企微本人或应用默认成员作为接口发起人。
func (p *ApprovalProvider) resolveCreator(ctx context.Context, application *model.WeComApplication, accountID uint) (*model.WeComMember, string, error) {
	var account model.Account
	if err := p.db.WithContext(ctx).Select("id", "user_type", "wecom_corp_id", "wecom_userid").Where("id = ?", accountID).First(&account).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, "", errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
		}
		return nil, "", errors.Wrap(errors.CodeDatabaseError, err, "查询审批真实提交人失败")
	}
	useBoundCreator := account.UserType == constants.UserTypeSuperAdmin || account.UserType == constants.UserTypePlatform
	if useBoundCreator && strings.EqualFold(account.WeComCorpID, application.CorpID) && strings.TrimSpace(account.WeComUserID) != "" {
		member, err := p.members.GetVisible(ctx, application.ID, account.WeComUserID)
		if err == nil {
			return member, constants.WeComCreatorSourceBound, nil
		}
	}
	if strings.TrimSpace(application.DefaultCreatorUserID) == "" {
		return nil, "", errors.New(errors.CodeInvalidStatus, "企业微信应用尚未配置默认审批发起人")
	}
	member, err := p.members.GetVisible(ctx, application.ID, application.DefaultCreatorUserID)
	if err != nil {
		return nil, "", errors.New(errors.CodeInvalidStatus, "企业微信默认审批发起人已不可用，请管理员重新配置")
	}
	return member, constants.WeComCreatorSourceDefault, nil
}

func templateFingerprint(definition wecomapp.TemplateDefinition) (string, error) {
	snapshot, err := sonic.Marshal(definition)
	if err != nil {
		return "", errors.Wrap(errors.CodeInternalError, err, "编码企业微信模板快照失败")
	}
	hash := sha256.Sum256(snapshot)
	return hex.EncodeToString(hash[:]), nil
}

var _ approvalapp.ProviderPort = (*ApprovalProvider)(nil)
