package services

import (
	"context"
	"errors"

	"quyun/v2/app/errorx"
	"quyun/v2/app/http/v1/dto"
	"quyun/v2/app/requests"
	"quyun/v2/database/models"
	"quyun/v2/pkg/consts"

	"go.ipao.vip/gen/types"
	"gorm.io/gorm"
)

// @provider
type tenant struct{}

func (s *tenant) List(ctx context.Context, tenantID int64, filter *dto.TenantListFilter) (*requests.Pager, error) {
	tbl, q := models.TenantQuery.QueryContext(ctx)
	q = q.Where(tbl.Status.Eq(consts.TenantStatusVerified))
	if tenantID > 0 {
		q = q.Where(tbl.ID.Eq(tenantID))
	}

	if filter.Keyword != nil && *filter.Keyword != "" {
		q = q.Where(tbl.Name.Like("%" + *filter.Keyword + "%"))
	}

	filter.Pagination.Format()
	total, err := q.Count()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	list, err := q.Offset(int(filter.Pagination.Offset())).Limit(int(filter.Pagination.Limit)).Find()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	tenantIDs := make([]int64, 0, len(list))
	for _, t := range list {
		tenantIDs = append(tenantIDs, t.ID)
	}

	// 批量统计关注数与内容数，避免逐条 Count。
	followerMap, err := s.countFollowers(ctx, tenantIDs)
	if err != nil {
		return nil, err
	}
	contentMap, err := s.countPublishedContents(ctx, tenantIDs)
	if err != nil {
		return nil, err
	}

	var data []dto.TenantProfile
	for _, t := range list {
		cfg := t.Config.Data()
		data = append(data, dto.TenantProfile{
			ID:     t.ID,
			Name:   t.Name,
			Avatar: cfg.Avatar,
			Bio:    cfg.Bio,
			Stats: dto.Stats{
				Followers: int(followerMap[t.ID]),
				Contents:  int(contentMap[t.ID]),
			},
		})
	}

	return &requests.Pager{
		Pagination: filter.Pagination,
		Total:      total,
		Items:      data,
	}, nil
}

func (s *tenant) GetPublicProfile(ctx context.Context, tenantID, userID int64) (*dto.TenantProfile, error) {
	t, err := models.TenantQuery.WithContext(ctx).Where(models.TenantQuery.ID.Eq(tenantID)).First()
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errorx.ErrRecordNotFound
		}
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	// Stats
	followers, _ := models.TenantUserQuery.WithContext(ctx).Where(models.TenantUserQuery.TenantID.Eq(tenantID)).Count()
	contents, _ := models.ContentQuery.WithContext(ctx).
		Where(models.ContentQuery.TenantID.Eq(tenantID), models.ContentQuery.Status.Eq(consts.ContentStatusPublished)).
		Count()

	// Following status
	isFollowing := false
	if userID > 0 {
		uid := userID
		isFollowing, _ = models.TenantUserQuery.WithContext(ctx).
			Where(models.TenantUserQuery.TenantID.Eq(tenantID), models.TenantUserQuery.UserID.Eq(uid)).
			Exists()
	}

	cfg := t.Config.Data()
	return &dto.TenantProfile{
		ID:          t.ID,
		Name:        t.Name,
		Avatar:      cfg.Avatar,
		Cover:       cfg.Cover,
		Bio:         cfg.Bio,
		Description: cfg.Description,
		Stats: dto.Stats{
			Followers: int(followers),
			Contents:  int(contents),
		},
		IsFollowing: isFollowing,
	}, nil
}

func (s *tenant) Follow(ctx context.Context, tenantID, userID int64) error {
	if userID == 0 {
		return errorx.ErrUnauthorized
	}
	uid := userID

	// Check if tenant exists
	t, err := models.TenantQuery.WithContext(ctx).Where(models.TenantQuery.ID.Eq(tenantID)).First()
	if err != nil {
		return errorx.ErrRecordNotFound
	}

	tu := &models.TenantUser{
		TenantID: tenantID,
		UserID:   uid,
		Role:     types.Array[consts.TenantUserRole]{consts.TenantUserRoleMember},
		Status:   consts.UserStatusVerified,
	}

	if err := models.TenantUserQuery.WithContext(ctx).Save(tu); err != nil {
		return errorx.ErrDatabaseError.WithCause(err)
	}

	if Notification != nil {
		_ = Notification.Send(ctx, tenantID, t.UserID, "interaction", "新增粉丝", "有人关注了您的店铺: "+t.Name)
	}
	return nil
}

func (s *tenant) Unfollow(ctx context.Context, tenantID, userID int64) error {
	if userID == 0 {
		return errorx.ErrUnauthorized
	}
	uid := userID

	_, err := models.TenantUserQuery.WithContext(ctx).
		Where(models.TenantUserQuery.TenantID.Eq(tenantID), models.TenantUserQuery.UserID.Eq(uid)).
		Delete()
	if err != nil {
		return errorx.ErrDatabaseError.WithCause(err)
	}
	return nil
}

func (s *tenant) ListFollowed(ctx context.Context, tenantID, userID int64) ([]dto.TenantProfile, error) {
	if userID == 0 {
		return nil, errorx.ErrUnauthorized
	}
	uid := userID

	tbl, q := models.TenantUserQuery.QueryContext(ctx)
	q = q.Where(tbl.UserID.Eq(uid))
	if tenantID > 0 {
		q = q.Where(tbl.TenantID.Eq(tenantID))
	}
	list, err := q.Find()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	if len(list) == 0 {
		return []dto.TenantProfile{}, nil
	}

	tenantIDs := make([]int64, 0, len(list))
	tenantIDSet := make(map[int64]struct{}, len(list))
	for _, tu := range list {
		if _, ok := tenantIDSet[tu.TenantID]; ok {
			continue
		}
		tenantIDs = append(tenantIDs, tu.TenantID)
		tenantIDSet[tu.TenantID] = struct{}{}
	}

	tblTenant, qTenant := models.TenantQuery.QueryContext(ctx)
	tenants, err := qTenant.Where(tblTenant.ID.In(tenantIDs...)).Find()
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	tenantMap := make(map[int64]*models.Tenant, len(tenants))
	for _, t := range tenants {
		tenantMap[t.ID] = t
	}

	// 批量统计关注数与内容数，避免逐条 Count。
	followerMap, err := s.countFollowers(ctx, tenantIDs)
	if err != nil {
		return nil, err
	}
	contentMap, err := s.countPublishedContents(ctx, tenantIDs)
	if err != nil {
		return nil, err
	}

	var data []dto.TenantProfile
	for _, tu := range list {
		t, ok := tenantMap[tu.TenantID]
		if !ok {
			continue
		}

		cfg := t.Config.Data()
		data = append(data, dto.TenantProfile{
			ID:     t.ID,
			Name:   t.Name,
			Avatar: cfg.Avatar,
			Stats: dto.Stats{
				Followers: int(followerMap[tu.TenantID]),
				Contents:  int(contentMap[tu.TenantID]),
			},
			IsFollowing: true,
		})
	}

	return data, nil
}

// GetModelByID 获取指定 ID 的model
func (s *tenant) GetModelByID(ctx context.Context, id int64) (*models.Tenant, error) {
	tbl, query := models.TenantQuery.QueryContext(ctx)
	u, err := query.Where(tbl.ID.Eq(id)).First()
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errorx.ErrRecordNotFound
		}
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}
	return u, nil
}

type tenantStatRow struct {
	TenantID int64 `gorm:"column:tenant_id"`
	Total    int64 `gorm:"column:total"`
}

func (s *tenant) countFollowers(ctx context.Context, tenantIDs []int64) (map[int64]int64, error) {
	if len(tenantIDs) == 0 {
		return map[int64]int64{}, nil
	}

	tbl, q := models.TenantUserQuery.QueryContext(ctx)
	var rows []tenantStatRow
	err := q.Where(tbl.TenantID.In(tenantIDs...)).
		Select(tbl.TenantID, tbl.ALL.Count().As("total")).
		Group(tbl.TenantID).
		Scan(&rows)
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	result := make(map[int64]int64, len(rows))
	for _, row := range rows {
		result[row.TenantID] = row.Total
	}
	return result, nil
}

func (s *tenant) countPublishedContents(ctx context.Context, tenantIDs []int64) (map[int64]int64, error) {
	if len(tenantIDs) == 0 {
		return map[int64]int64{}, nil
	}

	tbl, q := models.ContentQuery.QueryContext(ctx)
	var rows []tenantStatRow
	err := q.Where(tbl.TenantID.In(tenantIDs...), tbl.Status.Eq(consts.ContentStatusPublished)).
		Select(tbl.TenantID, tbl.ALL.Count().As("total")).
		Group(tbl.TenantID).
		Scan(&rows)
	if err != nil {
		return nil, errorx.ErrDatabaseError.WithCause(err)
	}

	result := make(map[int64]int64, len(rows))
	for _, row := range rows {
		result[row.TenantID] = row.Total
	}
	return result, nil
}
