package model

import (
	"errors"
	"fmt"
	"hamster-tycoon/randomizer"
	"time"

	uuid "github.com/satori/go.uuid"
)

const (
	Full = iota
	AlmostFull
	Middle
	AlmostEmpty
	Empty
)

const (
	TotalGestationPeriod = 15
	MinChild             = 5
	MaxChild             = 9
	GestationCooldown    = (3 * 7) + 2
	GestationMinAge      = 10 * 6
	MaxAge               = 365 * 2
	GestationLuck        = 70
	FEMALE               = "F"
	MALE                 = "M"
)

var GlobalHamsterNumber = 1

type Hamster struct {
	ID                string     `json:"id"`
	Name              string     `json:"name"`
	Number            int        `json:"number"`
	Sexe              string     `json:"sexe"`
	Age               int        `json:"age"`
	Father            *Hamster   `json:"father"`
	Mother            *Hamster   `json:"mother"`
	HungerLevel       int8       `json:"hunger_level"`
	ThirstLevel       int8       `json:"thirst_level"`
	Weight            float64    `json:"weight"`
	Height            float64    `json:"height"`
	Alive             bool       `json:"alive"`
	Sold              bool       `json:"sold"`
	Golden            bool       `json:"golden"`
	Gestation         bool       `json:"gestation"`
	GestationPeriod   int8       `json:"gestation_period"`
	GestationCooldown int8       `json:"gestation_cooldown"`
	GestationFather   *Hamster   `json:"gestation_father"`
	Child             []*Hamster `json:"childs"`
	CreatedAt         time.Time  `json:"createdAt"`
	UpdatedAt         *time.Time `json:"updatedAt"`
	Cage              *Cage      `json:"cage"`
}

func (h *Hamster) Die() {
	h.Alive = false
}

func (h *Hamster) DeterminatePrice() int {
	if h.Age >= 600 {
		return 0
	}

	var price = 50

	if h.Golden {
		price += 100
	}
	if h.Age >= 60 && h.Age <= 300 {
		var diffAge, quotient int
		diffAge = 300 - h.Age
		quotient = diffAge / 25
		price += quotient * 5
	}
	if h.Sexe == FEMALE {
		price += 20
	} else {
		price -= 20
	}

	if h.Gestation {
		if h.Age >= 500 {
			price += 5
		} else {
			price += 20
		}
	}

	if h.Age > 300 {
		var diffAge, quotient int
		diffAge = h.Age - 300
		quotient = diffAge / 100
		price += quotient * 10
	}

	switch h.ThirstLevel {
	case Empty:
		price -= 20
	case AlmostEmpty:
		price -= 10
	case AlmostFull:
		price += 10
	case Full:
		price += 20
	}

	switch h.HungerLevel {
	case Empty:
		price -= 20
	case AlmostEmpty:
		price -= 10
	case AlmostFull:
		price += 10
	case Full:
		price += 20
	}
	switch {
	case h.GestationCooldown > 0 && h.GestationCooldown <= 7:
		price -= 10
	case h.GestationCooldown >= 8 && h.GestationCooldown <= 14:
		price -= 5
	case h.GestationCooldown > 15:
		price -= 2
	}
	return price

}

func (h *Hamster) Grow() {
	if h.Age >= MaxAge {
		if randNumber(h.Age-MaxAge, 365) > 364 {
			h.Die()
			return
		}
	}
	h.Age++
	if h.Gestation {
		h.GestationPeriod++
	} else {
		if h.GestationCooldown > 0 {
			h.GestationCooldown--
		}
	}
	fmt.Println(h.GestationCooldown)
}

func (h *Hamster) Fuck(another *Hamster) (bool, error) {
	if h.Sexe == another.Sexe {
		return false, errors.New("can't fuck together")
	}

	if h.Age < GestationMinAge || another.Age < GestationMinAge {
		return false, errors.New("one of the hamster is too young")
	}

	rand := randNumber(1, 100)

	if rand <= GestationLuck {
		female, male := func() (*Hamster, *Hamster) {
			if h.Sexe == FEMALE {
				return h, another
			}
			return another, h
		}()
		female.GestationFather = male
		female.Gestation = true
		female.GestationPeriod = 0
		female.GestationCooldown = TotalGestationPeriod + GestationCooldown
	}
	return rand <= GestationLuck, nil
}

func Born(father *Hamster, mother *Hamster) ([]*Hamster, error) {
	if !mother.Alive || mother.Sold {
		return nil, errors.New("the mother is not here")
	}
	if mother.GestationPeriod != TotalGestationPeriod {
		return nil, errors.New("not the time to make child")
	}

	mother.GestationCooldown = 0
	mother.Gestation = false
	mother.GestationPeriod = 0

	numberOfChild := randNumberChild()
	child := make([]*Hamster, numberOfChild)
	for i := 0; i < numberOfChild; i++ {
		child[i] = &Hamster{
			ID:                uuid.NewV4().String(),
			Name:              fmt.Sprintf("Hamster %d", GlobalHamsterNumber),
			Number:            GlobalHamsterNumber,
			Age:               1,
			Sexe:              randSexe(),
			Father:            father,
			Mother:            mother,
			HungerLevel:       Full,
			ThirstLevel:       Full,
			Alive:             true,
			Sold:              false,
			Golden:            false,
			Gestation:         false,
			GestationPeriod:   0,
			GestationCooldown: 30,
		}
	}
	mother.Child = append(mother.Child, child...)
	father.Child = append(father.Child, child...)

	var goldenRoll = randNumber(1, 4096)
	if goldenRoll == 42 {
		var goldenChild = randNumber(0, numberOfChild-1)
		child[goldenChild].Golden = true
	}

	if mother.Age >= 500 {
		var dieRoll = randNumber(0, 100)
		if (mother.Age < 525 && dieRoll <= 5) ||
			(mother.Age >= 525 && mother.Age < 550 && dieRoll <= 10) ||
			(mother.Age >= 550 && mother.Age < 575 && dieRoll <= 25) ||
			(mother.Age >= 575 && dieRoll <= 75) {
			mother.Die()
		}
	}
	return child, nil
}

func randNumberChild() int {
	return randNumber(MinChild, MaxChild)
}

func randSexe() string {
	return func() string {
		if randNumber(1, 2) == 2 {
			return MALE
		}
		return FEMALE
	}()
}

func randNumber(min int, max int) int {
	return min + randomizer.Rand(max)
}
