package domain

import (
	"math"
	"net/url"
)

type BaseTribe struct {
	id          int
	name        string
	tag         string
	numMembers  int
	numVillages int
	points      int
	allPoints   int
	rank        int
	od          OpponentsDefeated
	profileURL  *url.URL
}

const baseTribeModelName = "BaseTribe"

//nolint:gocyclo
func NewBaseTribe(
	id int,
	name string,
	tag string,
	numMembers int,
	numVillages int,
	points int,
	allPoints int,
	rank int,
	od OpponentsDefeated,
	profileURL *url.URL,
) (BaseTribe, error) {
	if err := validateIntInRange(id, 1, math.MaxInt); err != nil {
		return BaseTribe{}, ValidationError{
			Model: baseTribeModelName,
			Field: "id",
			Err:   err,
		}
	}

	if err := validateStringLen(name, tribeNameMinLength, tribeNameMaxLength); err != nil {
		return BaseTribe{}, ValidationError{
			Model: baseTribeModelName,
			Field: "name",
			Err:   err,
		}
	}

	// we convert tag to []rune to get the correct length
	// e.g. for tags such as Орловы, which takes 12 bytes rather than 6
	// explanation: https://golangbyexample.com/number-characters-string-golang/
	if err := validateSliceLen([]rune(tag), tribeTagMinLength, tribeTagMaxLength); err != nil {
		return BaseTribe{}, ValidationError{
			Model: baseTribeModelName,
			Field: "tag",
			Err:   err,
		}
	}

	if err := validateIntInRange(numMembers, 0, math.MaxInt); err != nil {
		return BaseTribe{}, ValidationError{
			Model: baseTribeModelName,
			Field: "numMembers",
			Err:   err,
		}
	}

	if err := validateIntInRange(numVillages, 0, math.MaxInt); err != nil {
		return BaseTribe{}, ValidationError{
			Model: baseTribeModelName,
			Field: "numVillages",
			Err:   err,
		}
	}

	if err := validateIntInRange(points, 0, math.MaxInt); err != nil {
		return BaseTribe{}, ValidationError{
			Model: baseTribeModelName,
			Field: "points",
			Err:   err,
		}
	}

	if err := validateIntInRange(allPoints, 0, math.MaxInt); err != nil {
		return BaseTribe{}, ValidationError{
			Model: baseTribeModelName,
			Field: "allPoints",
			Err:   err,
		}
	}

	if err := validateIntInRange(rank, 0, math.MaxInt); err != nil {
		return BaseTribe{}, ValidationError{
			Model: baseTribeModelName,
			Field: "rank",
			Err:   err,
		}
	}

	if profileURL == nil {
		return BaseTribe{}, ValidationError{
			Model: baseTribeModelName,
			Field: "profileURL",
			Err:   ErrNil,
		}
	}

	return BaseTribe{
		id:          id,
		name:        name,
		tag:         tag,
		numMembers:  numMembers,
		numVillages: numVillages,
		points:      points,
		allPoints:   allPoints,
		rank:        rank,
		od:          od,
		profileURL:  profileURL,
	}, nil
}

func (t BaseTribe) ID() int {
	return t.id
}

func (t BaseTribe) Name() string {
	return t.name
}

func (t BaseTribe) Tag() string {
	return t.tag
}

func (t BaseTribe) NumMembers() int {
	return t.numMembers
}

func (t BaseTribe) NumVillages() int {
	return t.numVillages
}

func (t BaseTribe) Points() int {
	return t.points
}

func (t BaseTribe) AllPoints() int {
	return t.allPoints
}

func (t BaseTribe) Rank() int {
	return t.rank
}

func (t BaseTribe) OD() OpponentsDefeated {
	return t.od
}

func (t BaseTribe) ProfileURL() *url.URL {
	return t.profileURL
}

func (t BaseTribe) IsZero() bool {
	return t == BaseTribe{}
}

type BaseTribes []BaseTribe
