package wecom

import (
	"context"
	"time"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
)

// ApplicationRepository 持久化企业微信应用配置。
type ApplicationRepository struct {
	db *gorm.DB
}

// NewApplicationRepository 创建企业微信应用 Repository。
func NewApplicationRepository(db *gorm.DB) *ApplicationRepository {
	return &ApplicationRepository{db: db}
}

// FindByIdentityForUpdate 在事务中锁定企业与应用唯一配置。
func (r *ApplicationRepository) FindByIdentityForUpdate(ctx context.Context, tx *gorm.DB, corpID string, agentID int64) (*model.WeComApplication, error) {
	var application model.WeComApplication
	err := tx.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).
		Where("corp_id = ? AND agent_id = ?", corpID, agentID).First(&application).Error
	if err == gorm.ErrRecordNotFound {
		return nil, nil
	}
	if err != nil {
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询企业微信应用配置失败")
	}
	return &application, nil
}

// Create 创建企业微信应用配置。
func (r *ApplicationRepository) Create(ctx context.Context, tx *gorm.DB, application *model.WeComApplication) error {
	if err := tx.WithContext(ctx).Create(application).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "创建企业微信应用配置失败")
	}
	return nil
}

// Update 更新企业微信应用配置及密文凭据。
func (r *ApplicationRepository) Update(ctx context.Context, tx *gorm.DB, application *model.WeComApplication) error {
	updates := map[string]any{
		"name": application.Name, "secret_ciphertext": application.SecretCiphertext,
		"callback_token_ciphertext":   application.CallbackTokenCiphertext,
		"encoding_aes_key_ciphertext": application.EncodingAESKeyCiphertext,
		"default_creator_userid":      application.DefaultCreatorUserID,
		"default_creator_name":        application.DefaultCreatorName,
		"status":                      application.Status, "updated_by": application.UpdatedBy, "updated_at": application.UpdatedAt,
	}
	if err := tx.WithContext(ctx).Model(&model.WeComApplication{}).Where("id = ?", application.ID).Updates(updates).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "更新企业微信应用配置失败")
	}
	return nil
}

// UpdateDefaultCreator 更新应用默认审批发起人快照。
func (r *ApplicationRepository) UpdateDefaultCreator(ctx context.Context, tx *gorm.DB, applicationID uint, userID, name string, operatorID uint, updatedAt time.Time) error {
	if err := tx.WithContext(ctx).Model(&model.WeComApplication{}).Where("id = ? AND deleted_at IS NULL", applicationID).Updates(map[string]any{
		"default_creator_userid": userID,
		"default_creator_name":   name,
		"updated_by":             operatorID,
		"updated_at":             updatedAt,
	}).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "更新企业微信默认审批发起人失败")
	}
	return nil
}

// List 分页查询企业微信应用配置及密文凭据。
func (r *ApplicationRepository) List(ctx context.Context, page, pageSize int) ([]model.WeComApplication, int64, error) {
	query := r.db.WithContext(ctx).Model(&model.WeComApplication{})
	var total int64
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, errors.Wrap(errors.CodeDatabaseError, err, "统计企业微信应用配置失败")
	}
	var applications []model.WeComApplication
	if err := query.Order("id ASC").Offset((page - 1) * pageSize).Limit(pageSize).Find(&applications).Error; err != nil {
		return nil, 0, errors.Wrap(errors.CodeDatabaseError, err, "查询企业微信应用列表失败")
	}
	return applications, total, nil
}

// GetEnabled 查询启用的企业微信应用及密文凭据。
func (r *ApplicationRepository) GetEnabled(ctx context.Context, applicationID uint) (*model.WeComApplication, error) {
	var application model.WeComApplication
	if err := r.db.WithContext(ctx).Where("id = ? AND status = ?", applicationID, constants.StatusEnabled).First(&application).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeWeComApplicationNotFound)
		}
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询企业微信应用配置失败")
	}
	return &application, nil
}

// MarkConnected 记录最近一次成功取得 access_token 的时间。
func (r *ApplicationRepository) MarkConnected(ctx context.Context, applicationID uint, connectedAt time.Time) error {
	if err := r.db.WithContext(ctx).Model(&model.WeComApplication{}).Where("id = ?", applicationID).
		Update("last_connected_at", connectedAt.UTC()).Error; err != nil {
		return errors.Wrap(errors.CodeDatabaseError, err, "更新企业微信最近连接时间失败")
	}
	return nil
}
