package domaintest

import (
	"time"

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

type TribeChangeCursorConfig struct {
	ID        int
	ServerKey string
	CreatedAt time.Time
}

func NewTribeChangeCursor(tb TestingTB, opts ...func(cfg *TribeChangeCursorConfig)) domain.TribeChangeCursor {
	tb.Helper()

	cfg := &TribeChangeCursorConfig{
		ID:        RandID(),
		ServerKey: RandServerKey(),
		CreatedAt: gofakeit.Date(),
	}

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

	tcc, err := domain.NewTribeChangeCursor(
		cfg.ID,
		cfg.ServerKey,
		cfg.CreatedAt,
	)
	require.NoError(tb, err)

	return tcc
}

type TribeChangeConfig struct {
	ID         int
	ServerKey  string
	PlayerID   int
	OldTribeID int
	NewTribeID int
	CreatedAt  time.Time
}

func NewTribeChange(tb TestingTB, opts ...func(cfg *TribeChangeConfig)) domain.TribeChange {
	tb.Helper()

	cfg := &TribeChangeConfig{
		ID:         RandID(),
		ServerKey:  RandServerKey(),
		PlayerID:   RandID(),
		OldTribeID: RandID(),
		NewTribeID: RandID(),
		CreatedAt:  time.Now(),
	}

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

	tc, err := domain.UnmarshalTribeChangeFromDatabase(
		cfg.ID,
		cfg.ServerKey,
		cfg.PlayerID,
		cfg.OldTribeID,
		cfg.NewTribeID,
		cfg.CreatedAt,
	)
	require.NoError(tb, err)

	return tc
}

type TribeChangeWithRelationsConfig struct {
	TribeChangeOptions []func(cfg *TribeChangeConfig)
	PlayerOptions      []func(cfg *PlayerConfig)
	OldTribeOptions    []func(cfg *TribeConfig)
	NewTribeOptions    []func(cfg *TribeConfig)
}

//nolint:gocyclo
func NewTribeChangeWithRelations(
	tb TestingTB,
	opts ...func(cfg *TribeChangeWithRelationsConfig),
) domain.TribeChangeWithRelations {
	tb.Helper()

	cfg := &TribeChangeWithRelationsConfig{}

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

	tc := NewTribeChange(tb, cfg.TribeChangeOptions...)

	if tc.PlayerID() > 0 {
		cfg.PlayerOptions = append([]func(cfg *PlayerConfig){
			func(cfg *PlayerConfig) {
				cfg.ID = tc.PlayerID()
			},
		}, cfg.PlayerOptions...)
	}

	if tc.OldTribeID() > 0 {
		cfg.OldTribeOptions = append([]func(cfg *TribeConfig){
			func(cfg *TribeConfig) {
				cfg.ID = tc.OldTribeID()
			},
		}, cfg.OldTribeOptions...)
	}

	if tc.NewTribeID() > 0 {
		cfg.NewTribeOptions = append([]func(cfg *TribeConfig){
			func(cfg *TribeConfig) {
				cfg.ID = tc.NewTribeID()
			},
		}, cfg.NewTribeOptions...)
	}

	var player domain.PlayerMeta
	if len(cfg.PlayerOptions) > 0 {
		player = NewPlayer(tb, cfg.PlayerOptions...).Meta()
	}

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

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

	return tc.WithRelations(
		player,
		domain.NullTribeMeta{
			V:     oldTribe,
			Valid: !oldTribe.IsZero(),
		},
		domain.NullTribeMeta{
			V:     newTribe,
			Valid: !newTribe.IsZero(),
		},
	)
}
