// Package shop 提供店铺业务员归属与资金概况读取投影。
package shop

import (
	"context"
	"strings"

	"gorm.io/gorm"

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

// BusinessOwnerQuery 提供店铺业务员归属读取能力。
type BusinessOwnerQuery struct {
	db *gorm.DB
}

// NewBusinessOwnerQuery 创建店铺业务员归属 Query。
func NewBusinessOwnerQuery(db *gorm.DB) *BusinessOwnerQuery {
	return &BusinessOwnerQuery{db: db}
}

// List 查询调用者数据范围内的店铺，并批量投影上级和业务员摘要。
func (q *BusinessOwnerQuery) List(ctx context.Context, request dto.ShopListRequest) ([]*dto.ShopResponse, int64, error) {
	base := middleware.ApplyShopIDFilter(ctx, q.db.WithContext(ctx).Model(&model.Shop{}))
	base = applyShopFilters(base, request)
	var total int64
	if err := base.Count(&total).Error; err != nil {
		return nil, 0, errors.Wrap(errors.CodeDatabaseError, err, "查询店铺总数失败")
	}
	var shops []*model.Shop
	offset := (request.Page - 1) * request.PageSize
	if err := base.Order("created_at DESC, id DESC").Offset(offset).Limit(request.PageSize).Find(&shops).Error; err != nil {
		return nil, 0, errors.Wrap(errors.CodeDatabaseError, err, "查询店铺列表失败")
	}
	responses, err := q.project(ctx, shops)
	if err != nil {
		return nil, 0, err
	}
	return responses, total, nil
}

// Detail 查询调用者数据范围内的一家店铺详情。
func (q *BusinessOwnerQuery) Detail(ctx context.Context, shopID uint) (*dto.ShopResponse, error) {
	if shopID == 0 {
		return nil, errors.New(errors.CodeInvalidParam)
	}
	var shop model.Shop
	db := middleware.ApplyShopIDFilter(ctx, q.db.WithContext(ctx).Model(&model.Shop{}))
	if err := db.Where("id = ?", shopID).First(&shop).Error; err != nil {
		return nil, errors.New(errors.CodeForbidden, "无权限操作该资源或资源不存在")
	}
	responses, err := q.project(ctx, []*model.Shop{&shop})
	if err != nil {
		return nil, err
	}
	return responses[0], nil
}

// Candidates 查询当前可人工绑定的平台业务员最小投影。
func (q *BusinessOwnerQuery) Candidates(ctx context.Context, request dto.ShopBusinessOwnerCandidateRequest) ([]dto.ShopBusinessOwnerCandidate, int64, int, int, error) {
	userType := middleware.GetUserTypeFromContext(ctx)
	if userType != constants.UserTypeSuperAdmin && userType != constants.UserTypePlatform {
		return nil, 0, 0, 0, errors.New(errors.CodeForbidden, "无权限查询业务员候选")
	}
	page, pageSize := request.Page, request.PageSize
	if page == 0 {
		page = constants.DefaultPage
	}
	if pageSize == 0 {
		pageSize = constants.DefaultPageSize
	}
	base := q.db.WithContext(ctx).Model(&model.Account{}).
		Where("user_type = ? AND status = ?", constants.UserTypePlatform, constants.StatusEnabled)
	if request.Keyword != "" {
		keyword := "%" + request.Keyword + "%"
		base = base.Where("username ILIKE ? OR phone ILIKE ?", keyword, keyword)
	}
	var total int64
	if err := base.Count(&total).Error; err != nil {
		return nil, 0, 0, 0, errors.Wrap(errors.CodeDatabaseError, err, "查询业务员候选总数失败")
	}
	var accounts []model.Account
	if err := base.Order("id ASC").Offset((page - 1) * pageSize).Limit(pageSize).Find(&accounts).Error; err != nil {
		return nil, 0, 0, 0, errors.Wrap(errors.CodeDatabaseError, err, "查询业务员候选失败")
	}
	items := make([]dto.ShopBusinessOwnerCandidate, 0, len(accounts))
	for _, account := range accounts {
		items = append(items, dto.ShopBusinessOwnerCandidate{
			ID: account.ID, Username: account.Username, PhoneSummary: maskPhone(account.Phone),
		})
	}
	return items, total, page, pageSize, nil
}

func applyShopFilters(db *gorm.DB, request dto.ShopListRequest) *gorm.DB {
	if request.ShopName != "" {
		db = db.Where("shop_name LIKE ?", "%"+request.ShopName+"%")
	}
	if request.ShopCode != "" {
		db = db.Where("shop_code = ?", request.ShopCode)
	}
	if request.ContactPhone != "" {
		db = db.Where("contact_phone = ?", request.ContactPhone)
	}
	if request.BusinessOwnerAccountID != nil {
		db = db.Where("business_owner_account_id = ?", *request.BusinessOwnerAccountID)
	}
	if request.ParentID != nil {
		db = db.Where("parent_id = ?", *request.ParentID)
	}
	if request.Level != nil {
		db = db.Where("level = ?", *request.Level)
	}
	if request.Status != nil {
		db = db.Where("status = ?", *request.Status)
	}
	return db
}

func (q *BusinessOwnerQuery) project(ctx context.Context, shops []*model.Shop) ([]*dto.ShopResponse, error) {
	parentIDs, ownerIDs := collectProjectionIDs(shops)
	parentNames, err := q.loadParentNames(ctx, parentIDs)
	if err != nil {
		return nil, err
	}
	owners, err := q.loadBusinessOwners(ctx, ownerIDs)
	if err != nil {
		return nil, err
	}
	responses := make([]*dto.ShopResponse, 0, len(shops))
	for _, shop := range shops {
		response := &dto.ShopResponse{
			ID: shop.ID, ShopName: shop.ShopName, ShopCode: shop.ShopCode, ParentID: shop.ParentID,
			BusinessOwnerAccountID: shop.BusinessOwnerAccountID, Level: shop.Level,
			ContactName: shop.ContactName, ContactPhone: shop.ContactPhone, Province: shop.Province,
			City: shop.City, District: shop.District, Address: shop.Address, Status: shop.Status,
			ClientLoginDisabled: shop.ClientLoginDisabled,
			StatusName:          constants.GetStatusName(shop.Status), CreatedAt: shop.CreatedAt.Format("2006-01-02 15:04:05"),
			UpdatedAt: shop.UpdatedAt.Format("2006-01-02 15:04:05"),
		}
		if shop.ParentID != nil {
			response.ParentShopName = parentNames[*shop.ParentID]
		}
		if shop.BusinessOwnerAccountID != nil {
			if owner, exists := owners[*shop.BusinessOwnerAccountID]; exists {
				response.BusinessOwnerUsername = owner.Username
				response.BusinessOwnerPhoneSummary = maskPhone(owner.Phone)
				response.BusinessOwnerAvailable = owner.UserType == constants.UserTypePlatform && owner.Status == constants.StatusEnabled && !owner.DeletedAt.Valid
			}
		}
		responses = append(responses, response)
	}
	return responses, nil
}

func collectProjectionIDs(shops []*model.Shop) ([]uint, []uint) {
	parents := make(map[uint]struct{})
	owners := make(map[uint]struct{})
	for _, shop := range shops {
		if shop.ParentID != nil {
			parents[*shop.ParentID] = struct{}{}
		}
		if shop.BusinessOwnerAccountID != nil {
			owners[*shop.BusinessOwnerAccountID] = struct{}{}
		}
	}
	return mapKeys(parents), mapKeys(owners)
}

func (q *BusinessOwnerQuery) loadParentNames(ctx context.Context, ids []uint) (map[uint]string, error) {
	result := make(map[uint]string, len(ids))
	if len(ids) == 0 {
		return result, nil
	}
	var shops []model.Shop
	db := middleware.ApplyShopIDFilter(ctx, q.db.WithContext(ctx).Model(&model.Shop{}))
	if err := db.Select("id", "shop_name").Where("id IN ?", ids).Find(&shops).Error; err != nil {
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询上级店铺摘要失败")
	}
	for _, shop := range shops {
		result[shop.ID] = shop.ShopName
	}
	return result, nil
}

func (q *BusinessOwnerQuery) loadBusinessOwners(ctx context.Context, ids []uint) (map[uint]model.Account, error) {
	result := make(map[uint]model.Account, len(ids))
	if len(ids) == 0 {
		return result, nil
	}
	var accounts []model.Account
	if err := q.db.WithContext(ctx).Unscoped().Where("id IN ?", ids).Find(&accounts).Error; err != nil {
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询业务员摘要失败")
	}
	for _, account := range accounts {
		result[account.ID] = account
	}
	return result, nil
}

func mapKeys(values map[uint]struct{}) []uint {
	keys := make([]uint, 0, len(values))
	for key := range values {
		keys = append(keys, key)
	}
	return keys
}

func maskPhone(phone string) string {
	phone = strings.TrimSpace(phone)
	if len(phone) < 7 {
		return ""
	}
	return phone[:3] + "****" + phone[len(phone)-4:]
}
