package domaintest

import (
	"math"
	"time"

	"gitea.dwysokinski.me/twhelp/corev3/internal/domain"
	"github.com/brianvoe/gofakeit/v7"
	"github.com/stretchr/testify/require"
)

type PlayerCursorConfig struct {
	ID           int
	ServerKey    string
	ODScoreAtt   int
	ODScoreDef   int
	ODScoreSup   int
	ODScoreTotal int
	Points       int
	DeletedAt    time.Time
}

func NewPlayerCursor(tb TestingTB, opts ...func(cfg *PlayerCursorConfig)) domain.PlayerCursor {
	tb.Helper()

	cfg := &PlayerCursorConfig{
		ID:           RandID(),
		ServerKey:    RandServerKey(),
		ODScoreAtt:   gofakeit.IntRange(0, math.MaxInt),
		ODScoreDef:   gofakeit.IntRange(0, math.MaxInt),
		ODScoreSup:   gofakeit.IntRange(0, math.MaxInt),
		ODScoreTotal: gofakeit.IntRange(0, math.MaxInt),
		Points:       gofakeit.IntRange(0, math.MaxInt),
		DeletedAt:    time.Time{},
	}

	for _, opt := range opts {
		opt(cfg)
	}

	pc, err := domain.NewPlayerCursor(
		cfg.ID,
		cfg.ServerKey,
		cfg.ODScoreAtt,
		cfg.ODScoreDef,
		cfg.ODScoreSup,
		cfg.ODScoreTotal,
		cfg.Points,
		cfg.DeletedAt,
	)
	require.NoError(tb, err)

	return pc
}

type PlayerConfig struct {
	ID             int
	Points         int
	NumVillages    int
	ServerKey      string
	TribeID        int
	OD             domain.OpponentsDefeated
	BestRank       int
	BestRankAt     time.Time
	MostPoints     int
	MostPointsAt   time.Time
	MostVillages   int
	MostVillagesAt time.Time
	LastActivityAt time.Time
	DeletedAt      time.Time
}

func NewPlayer(tb TestingTB, opts ...func(cfg *PlayerConfig)) domain.Player {
	tb.Helper()

	now := time.Now()

	cfg := &PlayerConfig{
		ID:             RandID(),
		ServerKey:      RandServerKey(),
		Points:         gofakeit.IntRange(1, 10000),
		NumVillages:    gofakeit.IntRange(1, 10000),
		TribeID:        RandID(),
		OD:             NewOpponentsDefeated(tb),
		BestRank:       gofakeit.IntRange(1, 10000),
		BestRankAt:     now,
		MostPoints:     gofakeit.IntRange(1, 10000),
		MostPointsAt:   now,
		MostVillages:   gofakeit.IntRange(1, 10000),
		MostVillagesAt: now,
		LastActivityAt: now,
		DeletedAt:      time.Time{},
	}

	for _, opt := range opts {
		opt(cfg)
	}

	p, err := domain.UnmarshalPlayerFromDatabase(
		cfg.ID,
		cfg.ServerKey,
		gofakeit.LetterN(50),
		cfg.NumVillages,
		cfg.Points,
		gofakeit.IntRange(1, 10000),
		cfg.TribeID,
		cfg.OD,
		gofakeit.URL(),
		cfg.BestRank,
		cfg.BestRankAt,
		cfg.MostPoints,
		cfg.MostPointsAt,
		cfg.MostVillages,
		cfg.MostVillagesAt,
		cfg.LastActivityAt,
		now,
		cfg.DeletedAt,
	)
	require.NoError(tb, err)

	return p
}

type PlayerWithRelationsConfig struct {
	ServerOptions []func(cfg *ServerConfig)
	PlayerOptions []func(cfg *PlayerConfig)
	TribeOptions  []func(cfg *TribeConfig)
}

func NewPlayerWithRelations(tb TestingTB, opts ...func(cfg *PlayerWithRelationsConfig)) domain.PlayerWithRelations {
	tb.Helper()

	cfg := &PlayerWithRelationsConfig{}

	for _, opt := range opts {
		opt(cfg)
	}

	p := NewPlayer(tb, cfg.PlayerOptions...)

	cfg.ServerOptions = append([]func(cfg *ServerConfig){
		func(cfg *ServerConfig) {
			cfg.Key = p.ServerKey()
		},
	}, cfg.ServerOptions...)

	if p.TribeID() > 0 {
		cfg.TribeOptions = append([]func(cfg *TribeConfig){
			func(cfg *TribeConfig) {
				cfg.ID = p.ID()
			},
		}, cfg.TribeOptions...)
	}

	var tribe domain.TribeMeta
	if len(cfg.TribeOptions) > 0 {
		tribe = NewTribe(tb, cfg.TribeOptions...).Meta()
	}

	return p.WithRelations(
		NewServer(tb, cfg.ServerOptions...).Meta(),
		domain.NullTribeMeta{
			V:     tribe,
			Valid: !tribe.IsZero(),
		},
	)
}
