package service

import (
	"context"
	"fmt"
	"slices"

	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
)

const defaultRoomSeatCount int32 = 15

var roomSeatCountCandidates = []int32{10, 15, 20, 25, 30}

func defaultRoomSeatConfig(appCode string) RoomSeatConfig {
	allowed := append([]int32(nil), roomSeatCountCandidates...)
	return RoomSeatConfig{
		AppCode:           appcode.Normalize(appCode),
		AllowedSeatCounts: allowed,
		DefaultSeatCount:  defaultRoomSeatCount,
	}
}

func (s *Service) resolveRoomSeatCount(ctx context.Context, requested int32) (int32, error) {
	if requested < 0 {
		// 负数没有业务语义，不能让它落到默认值分支掩盖客户端错误。
		return 0, xerr.New(xerr.InvalidArgument, "seat_count is invalid")
	}

	config, err := s.currentRoomSeatConfig(ctx)
	if err != nil {
		return 0, err
	}
	if requested == 0 {
		return config.DefaultSeatCount, nil
	}
	if !seatCountAllowed(config.AllowedSeatCounts, requested) {
		return 0, xerr.New(xerr.InvalidArgument, fmt.Sprintf("seat_count must be one of %v", config.AllowedSeatCounts))
	}

	return requested, nil
}

func (s *Service) validateRoomSeatCount(ctx context.Context, requested int32) (int32, error) {
	if requested <= 0 {
		return 0, xerr.New(xerr.InvalidArgument, "seat_count is invalid")
	}
	config, err := s.currentRoomSeatConfig(ctx)
	if err != nil {
		return 0, err
	}
	if !seatCountAllowed(config.AllowedSeatCounts, requested) {
		return 0, xerr.New(xerr.InvalidArgument, fmt.Sprintf("seat_count must be one of %v", config.AllowedSeatCounts))
	}
	return requested, nil
}

func (s *Service) currentRoomSeatConfig(ctx context.Context) (RoomSeatConfig, error) {
	config, exists, err := s.repository.GetRoomSeatConfig(ctx)
	if err != nil {
		return RoomSeatConfig{}, err
	}
	if !exists {
		// 本地新库会通过 initdb/Migrate 写默认行；该兜底保护测试或手动库缺行时仍能按产品默认创建房间。
		return defaultRoomSeatConfig(appcode.FromContext(ctx)), nil
	}
	normalized, err := normalizeRoomSeatConfig(config)
	if err != nil {
		return RoomSeatConfig{}, xerr.New(xerr.Unavailable, "room seat config is invalid")
	}
	return normalized, nil
}

func normalizeRoomSeatConfig(config RoomSeatConfig) (RoomSeatConfig, error) {
	seen := make(map[int32]bool, len(config.AllowedSeatCounts))
	allowed := make([]int32, 0, len(config.AllowedSeatCounts))
	for _, value := range config.AllowedSeatCounts {
		if seen[value] {
			continue
		}
		if !seatCountAllowed(roomSeatCountCandidates, value) {
			return RoomSeatConfig{}, fmt.Errorf("seat count candidate is invalid: %d", value)
		}
		seen[value] = true
		allowed = append(allowed, value)
	}
	slices.Sort(allowed)
	if len(allowed) == 0 {
		return RoomSeatConfig{}, fmt.Errorf("allowed seat counts are empty")
	}
	if !seatCountAllowed(allowed, config.DefaultSeatCount) {
		return RoomSeatConfig{}, fmt.Errorf("default seat count is not allowed: %d", config.DefaultSeatCount)
	}

	config.AllowedSeatCounts = allowed
	config.AppCode = appcode.Normalize(config.AppCode)
	return config, nil
}

func seatCountAllowed(values []int32, target int32) bool {
	return slices.Contains(values, target)
}
