package storage

import (
	"errors"
	"strings"

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

	"sproutgate-backend/internal/models"
)

// MaxProfileLikesPerDay 每个点赞者每个自然日最多给多少位不同用户的公开主页点赞。
const MaxProfileLikesPerDay = 5

var (
	ErrCannotLikeOwnProfile    = errors.New("cannot like own profile")
	ErrAlreadyLikedToday       = errors.New("already liked today")
	ErrDailyLikeLimitReached   = errors.New("daily like limit reached")
	ErrProfileLikeTarget       = errors.New("user not found")
	ErrProfileLikeLiker        = errors.New("invalid liker")
)

// DBProfileLikeDailyQuota 点赞者当日剩余可点赞人数（与 profile_likes 计数同步；新自然日新行即视为刷新为满额）。
type DBProfileLikeDailyQuota struct {
	LikerAccount string `gorm:"primaryKey;column:liker_account;size:255"`
	LikeDate     string `gorm:"primaryKey;column:like_date;size:10"`
	Remaining    int    `gorm:"column:remaining;not null"`
	UpdatedAt    string `gorm:"column:updated_at;size:50"`
}

func (DBProfileLikeDailyQuota) TableName() string { return "profile_like_daily_quota" }

// DBProfileLike 记录「谁在某日给谁的主页点赞」（每人每天对同一主页最多一条）。
type DBProfileLike struct {
	LikerAccount string `gorm:"primaryKey;column:liker_account;size:255"`
	LikedAccount string `gorm:"primaryKey;column:liked_account;size:255"`
	LikeDate     string `gorm:"primaryKey;column:like_date;size:10"` // YYYY-MM-DD，与签到同历
	CreatedAt    string `gorm:"column:created_at;size:50"`
}

func (DBProfileLike) TableName() string { return "profile_likes" }

// CountProfileLikes 主页累计获赞次数（历史所有日的点赞条数之和）。
func (s *Store) CountProfileLikes(likedAccount string) (int64, error) {
	likedAccount = strings.TrimSpace(likedAccount)
	if likedAccount == "" {
		return 0, nil
	}
	var n int64
	if err := s.db.Model(&DBProfileLike{}).Where("liked_account = ?", likedAccount).Count(&n).Error; err != nil {
		return 0, err
	}
	return n, nil
}

// ViewerHasLikedProfileToday 当前自然日（Asia/Shanghai）内是否已为该主页点过赞。
func (s *Store) ViewerHasLikedProfileToday(likerAccount, likedAccount string) (bool, error) {
	likerAccount = strings.TrimSpace(likerAccount)
	likedAccount = strings.TrimSpace(likedAccount)
	if likerAccount == "" || likedAccount == "" {
		return false, nil
	}
	today := models.CurrentActivityDate()
	var n int64
	err := s.db.Model(&DBProfileLike{}).
		Where("liker_account = ? AND liked_account = ? AND like_date = ?", likerAccount, likedAccount, today).
		Count(&n).Error
	return n > 0, err
}

// ProfileLikeRemainingToday 当日还可给多少人点赞（根据 profile_likes 计数计算，并与 quota 表对齐）。
func (s *Store) ProfileLikeRemainingToday(likerAccount string) (int, error) {
	likerAccount = strings.TrimSpace(likerAccount)
	if likerAccount == "" {
		return 0, nil
	}
	today := models.CurrentActivityDate()
	var used int64
	if err := s.db.Model(&DBProfileLike{}).
		Where("liker_account = ? AND like_date = ?", likerAccount, today).
		Count(&used).Error; err != nil {
		return 0, err
	}
	rem := MaxProfileLikesPerDay - int(used)
	if rem < 0 {
		rem = 0
	}
	var q DBProfileLikeDailyQuota
	err := s.db.Where("liker_account = ? AND like_date = ?", likerAccount, today).First(&q).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return rem, nil
	}
	if err != nil {
		return 0, err
	}
	if q.Remaining != rem {
		_ = s.db.Model(&DBProfileLikeDailyQuota{}).
			Where("liker_account = ? AND like_date = ?", likerAccount, today).
			Updates(map[string]interface{}{
				"remaining":  rem,
				"updated_at": models.NowISO(),
			}).Error
	}
	return rem, nil
}

// AddProfileLike 为公开主页点赞（每人每天每主页一次；每人每天最多给 MaxProfileLikesPerDay 位用户点赞）。返回新的累计赞数。
func (s *Store) AddProfileLike(likerAccount, likedAccount string) (int64, error) {
	likerAccount = strings.TrimSpace(likerAccount)
	likedAccount = strings.TrimSpace(likedAccount)
	if likerAccount == "" || likedAccount == "" {
		return 0, errors.New("account is required")
	}
	if strings.EqualFold(likerAccount, likedAccount) {
		return 0, ErrCannotLikeOwnProfile
	}

	likedUser, found, err := s.GetUser(likedAccount)
	if err != nil {
		return 0, err
	}
	if !found || likedUser.Banned {
		return 0, ErrProfileLikeTarget
	}
	likerUser, foundL, err := s.GetUser(likerAccount)
	if err != nil {
		return 0, err
	}
	if !foundL || likerUser.Banned {
		return 0, ErrProfileLikeLiker
	}

	today := models.CurrentActivityDate()
	likerCanon := likerUser.Account
	likedCanon := likedUser.Account
	row := DBProfileLike{
		LikerAccount: likerCanon,
		LikedAccount: likedCanon,
		LikeDate:     today,
		CreatedAt:    models.NowISO(),
	}

	txErr := s.db.Transaction(func(tx *gorm.DB) error {
		var quota DBProfileLikeDailyQuota
		qErr := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
			Where("liker_account = ? AND like_date = ?", likerCanon, today).
			First(&quota).Error
		if errors.Is(qErr, gorm.ErrRecordNotFound) {
			quota = DBProfileLikeDailyQuota{
				LikerAccount: likerCanon,
				LikeDate:     today,
				Remaining:    MaxProfileLikesPerDay,
				UpdatedAt:    models.NowISO(),
			}
			if cerr := tx.Create(&quota).Error; cerr != nil {
				if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
					Where("liker_account = ? AND like_date = ?", likerCanon, today).
					First(&quota).Error; err != nil {
					return err
				}
			}
		} else if qErr != nil {
			return qErr
		}
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
			Where("liker_account = ? AND like_date = ?", likerCanon, today).
			First(&quota).Error; err != nil {
			return err
		}

		var used int64
		if err := tx.Model(&DBProfileLike{}).
			Where("liker_account = ? AND like_date = ?", likerCanon, today).
			Count(&used).Error; err != nil {
			return err
		}
		if int(used) >= MaxProfileLikesPerDay {
			if err := tx.Model(&DBProfileLikeDailyQuota{}).
				Where("liker_account = ? AND like_date = ?", likerCanon, today).
				Updates(map[string]interface{}{
					"remaining":  0,
					"updated_at": models.NowISO(),
				}).Error; err != nil {
				return err
			}
			return ErrDailyLikeLimitReached
		}

		res := tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&row)
		if res.Error != nil {
			return res.Error
		}
		if res.RowsAffected == 0 {
			return ErrAlreadyLikedToday
		}

		var usedAfter int64
		if err := tx.Model(&DBProfileLike{}).
			Where("liker_account = ? AND like_date = ?", likerCanon, today).
			Count(&usedAfter).Error; err != nil {
			return err
		}
		rem := MaxProfileLikesPerDay - int(usedAfter)
		if rem < 0 {
			rem = 0
		}
		return tx.Model(&DBProfileLikeDailyQuota{}).
			Where("liker_account = ? AND like_date = ?", likerCanon, today).
			Updates(map[string]interface{}{
				"remaining":  rem,
				"updated_at": models.NowISO(),
			}).Error
	})
	if txErr != nil {
		return 0, txErr
	}
	return s.CountProfileLikes(likedCanon)
}
