package user

import (
	"context"
	"fmt"
	"time"

	"github.com/perfect-panel/server/pkg/constant"
	"gorm.io/gorm"
)

func (m *defaultUserModel) execSubscribeMutation(ctx context.Context, cacheModels []*Subscribe, execFn func(conn *gorm.DB) error, tx ...*gorm.DB) error {
	defer func() {
		_ = m.ClearSubscribeCacheByModels(ctx, cacheModels...)
	}()

	return m.ExecNoCacheCtx(ctx, func(conn *gorm.DB) error {
		if len(tx) > 0 {
			conn = tx[0]
		}
		return execFn(conn)
	})
}

func (m *defaultUserModel) UpdateUserSubscribeCache(ctx context.Context, data *Subscribe) error {
	return m.ClearSubscribeCacheByModels(ctx, data)
}

// QueryActiveSubscriptions returns the number of active subscriptions.
func (m *defaultUserModel) QueryActiveSubscriptions(ctx context.Context, subscribeId ...int64) (map[int64]int64, error) {
	type SubscriptionCount struct {
		SubscribeId int64
		Total       int64
	}
	var result []SubscriptionCount
	err := m.QueryNoCacheCtx(ctx, &result, func(conn *gorm.DB, v interface{}) error {
		return conn.Model(&Subscribe{}).
			Where("subscribe_id IN ? AND `status` IN ?", subscribeId, []int64{1, 0}).
			Select("subscribe_id, COUNT(id) as total").
			Group("subscribe_id").
			Scan(&result).
			Error
	})

	if err != nil {
		return nil, err
	}

	resultMap := make(map[int64]int64)
	for _, item := range result {
		resultMap[item.SubscribeId] = item.Total
	}

	return resultMap, nil
}

func (m *defaultUserModel) FindOneSubscribeByOrderId(ctx context.Context, orderId int64) (*Subscribe, error) {
	var data Subscribe
	err := m.QueryNoCacheCtx(ctx, &data, func(conn *gorm.DB, v interface{}) error {
		return conn.Model(&Subscribe{}).Where("order_id = ?", orderId).First(&data).Error
	})
	return &data, err
}

// FindSingleModeAnchorSubscribe finds the latest paid subscription for single subscribe mode routing.
func (m *defaultUserModel) FindSingleModeAnchorSubscribe(ctx context.Context, userId int64) (*Subscribe, error) {
	var data Subscribe
	err := m.QueryNoCacheCtx(ctx, &data, func(conn *gorm.DB, _ interface{}) error {
		return conn.Model(&Subscribe{}).
			Where("user_id = ? AND token != '' AND (order_id > 0 OR token LIKE 'iap:%') AND `status` IN ?", userId, []int64{0, 1, 2, 3, 4, 5}).
			Order("expire_time DESC").
			Order("updated_at DESC").
			Order("id DESC").
			First(&data).Error
	})
	return &data, err
}

func (m *defaultUserModel) FindOneSubscribe(ctx context.Context, id int64) (*Subscribe, error) {
	var data Subscribe
	key := fmt.Sprintf("%s%d", cacheUserSubscribeIdPrefix, id)
	err := m.QueryCtx(ctx, &data, key, func(conn *gorm.DB, v interface{}) error {
		return conn.Model(&Subscribe{}).Where("id = ?", id).First(&data).Error
	})
	return &data, err
}

func (m *defaultUserModel) FindUsersSubscribeBySubscribeId(ctx context.Context, subscribeId int64) ([]*Subscribe, error) {
	var data []*Subscribe
	err := m.QueryNoCacheCtx(ctx, &data, func(conn *gorm.DB, v interface{}) error {
		return conn.Model(&Subscribe{}).Where("subscribe_id = ? AND `status` IN ?", subscribeId, []int64{1, 0}).Find(v).Error
	})
	if err != nil {
		return nil, err
	}
	// Activate pending subscribes (status 0 -> 1) in a separate write operation
	if err := m.ExecNoCacheCtx(ctx, func(conn *gorm.DB) error {
		return conn.Model(&Subscribe{}).Where("subscribe_id = ? AND `status` = ?", subscribeId, 0).Update("status", 1).Error
	}); err != nil {
		return data, err
	}
	return data, nil
}

// QueryUserSubscribe returns a list of records that meet the conditions.
func (m *defaultUserModel) QueryUserSubscribe(ctx context.Context, userId int64, status ...int64) ([]*SubscribeDetails, error) {
	var list []*SubscribeDetails

	// Check if includeExpired is set in context
	includeExpired, _ := ctx.Value(constant.CtxKeyIncludeExpired).(string)
	cacheKeySuffix := ""
	if includeExpired == "all" {
		cacheKeySuffix = ":all"
	}

	key := fmt.Sprintf("%s%d%s", cacheUserSubscribeUserPrefix, userId, cacheKeySuffix)
	err := m.QueryCtx(ctx, &list, key, func(conn *gorm.DB, v interface{}) error {
		conn = conn.Model(&Subscribe{}).Where("`user_id` = ?", userId)
		if len(status) > 0 {
			conn = conn.Where("`status` IN ?", status)
		}

		if includeExpired == "all" {
			// 查询所有订阅（包括已过期的）
			return conn.Preload("Subscribe").Find(&list).Error
		}

		// 默认只查询有效订阅
		now := time.Now()
		sevenDaysAgo := time.Now().Add(-7 * 24 * time.Hour)
		return conn.Where("`expire_time` > ? OR `finished_at` >= ? OR `expire_time` = ?", now, sevenDaysAgo, time.UnixMilli(0)).
			Preload("Subscribe").
			Find(&list).Error
	})
	return list, err
}

// FindOneUserSubscribe  finds a subscribeDetails by id.
func (m *defaultUserModel) FindOneUserSubscribe(ctx context.Context, id int64) (subscribeDetails *SubscribeDetails, err error) {
	//TODO cache
	//key := fmt.Sprintf("%s%d", cacheUserSubscribeUserPrefix, userId)
	subscribeDetails = &SubscribeDetails{}
	err = m.QueryNoCacheCtx(ctx, subscribeDetails, func(conn *gorm.DB, v interface{}) error {
		return conn.Model(&Subscribe{}).Preload("Subscribe").Where("id = ?", id).First(&subscribeDetails).Error
	})
	return
}

// FindOneSubscribeByToken  finds a record by token.
func (m *defaultUserModel) FindOneSubscribeByToken(ctx context.Context, token string) (*Subscribe, error) {
	var data Subscribe
	key := fmt.Sprintf("%s%s", cacheUserSubscribeTokenPrefix, token)
	err := m.QueryCtx(ctx, &data, key, func(conn *gorm.DB, v interface{}) error {
		return conn.Model(&Subscribe{}).Where("token = ?", token).First(&data).Error
	})
	return &data, err
}

// UpdateSubscribe updates a record.
func (m *defaultUserModel) UpdateSubscribe(ctx context.Context, data *Subscribe, tx ...*gorm.DB) error {
	old, err := m.FindOneSubscribe(ctx, data.Id)
	if err != nil {
		return err
	}

	return m.execSubscribeMutation(ctx, []*Subscribe{old, data}, func(conn *gorm.DB) error {
		return conn.Model(&Subscribe{}).Where("id = ?", data.Id).Save(data).Error
	}, tx...)
}

// DeleteSubscribe deletes a record.
func (m *defaultUserModel) DeleteSubscribe(ctx context.Context, token string, tx ...*gorm.DB) error {
	data, err := m.FindOneSubscribeByToken(ctx, token)
	if err != nil {
		return err
	}

	return m.execSubscribeMutation(ctx, []*Subscribe{data}, func(conn *gorm.DB) error {
		return conn.Where("token = ?", token).Delete(&Subscribe{}).Error
	}, tx...)
}

// InsertSubscribe insert Subscribe into the database.
func (m *defaultUserModel) InsertSubscribe(ctx context.Context, data *Subscribe, tx ...*gorm.DB) error {
	return m.execSubscribeMutation(ctx, []*Subscribe{data}, func(conn *gorm.DB) error {
		return conn.Create(data).Error
	}, tx...)
}

func (m *defaultUserModel) DeleteSubscribeById(ctx context.Context, id int64, tx ...*gorm.DB) error {
	data, err := m.FindOneSubscribe(ctx, id)
	if err != nil {
		return err
	}

	return m.execSubscribeMutation(ctx, []*Subscribe{data}, func(conn *gorm.DB) error {
		return conn.Where("id = ?", id).Delete(&Subscribe{}).Error
	}, tx...)
}

func (m *defaultUserModel) ClearSubscribeCache(ctx context.Context, data ...*Subscribe) error {
	return m.ClearSubscribeCacheByModels(ctx, data...)
}
