package model

import (
	"mvvasilev/last_light/engine"

	"github.com/gdamore/tcell/v2"
	"github.com/google/uuid"
)

type Direction int

const (
	DirectionNone Direction = iota
	North
	South
	West
	East
)

func DirectionName(dir Direction) string {
	switch dir {
	case North:
		return "North"
	case South:
		return "South"
	case West:
		return "West"
	case East:
		return "East"
	default:
		return "Unknown"
	}
}

func MovementDirectionOffset(dir Direction) (int, int) {
	switch dir {
	case North:
		return 0, -1
	case South:
		return 0, 1
	case West:
		return -1, 0
	case East:
		return 1, 0
	}

	return 0, 0
}

type Entity_NamedComponent struct {
	Name string
}

type Entity_DescribedComponent struct {
	Description string
}

type Entity_PresentableComponent struct {
	Rune  rune
	Style tcell.Style
}

type Entity_PositionedComponent struct {
	Position engine.Position
}

type Entity_EquippedComponent struct {
	Inventory *EquippedInventory
}

type Entity_StatsHolderComponent struct {
	BaseStats map[Stat]int
}

type Entity_BehaviorComponent struct {
	Speed    int
	Behavior func() (complete, requeue bool)
}

type Entity_HealthComponent struct {
	Health    int
	MaxHealth int
	IsDead    bool
}

type Entity_DropTableComponent struct {
	DropTable *LootTable
}

type Entity_ProjectileComponent struct {
	Source Entity
	Path   *engine.Path
}

type Entity interface {
	UniqueId() uuid.UUID

	Behavior() *Entity_BehaviorComponent
	Named() *Entity_NamedComponent
	Described() *Entity_DescribedComponent
	Presentable() *Entity_PresentableComponent
	Positioned() *Entity_PositionedComponent
	Equipped() *Entity_EquippedComponent
	Stats() *Entity_StatsHolderComponent
	HealthData() *Entity_HealthComponent
	DropTable() *Entity_DropTableComponent
	Projectile() *Entity_ProjectileComponent
}

type BaseEntity struct {
	id uuid.UUID

	behavior    *Entity_BehaviorComponent
	named       *Entity_NamedComponent
	described   *Entity_DescribedComponent
	presentable *Entity_PresentableComponent
	positioned  *Entity_PositionedComponent
	equipped    *Entity_EquippedComponent
	stats       *Entity_StatsHolderComponent
	damageable  *Entity_HealthComponent
	dropTable   *Entity_DropTableComponent
	projectile  *Entity_ProjectileComponent
}

func (be *BaseEntity) UniqueId() uuid.UUID {
	return be.id
}

func (be *BaseEntity) Named() *Entity_NamedComponent {
	return be.named
}

func (be *BaseEntity) Described() *Entity_DescribedComponent {
	return be.described
}

func (be *BaseEntity) Presentable() *Entity_PresentableComponent {
	return be.presentable
}

func (be *BaseEntity) Positioned() *Entity_PositionedComponent {
	return be.positioned
}

func (be *BaseEntity) Equipped() *Entity_EquippedComponent {
	return be.equipped
}

func (be *BaseEntity) Stats() *Entity_StatsHolderComponent {
	return be.stats
}

func (be *BaseEntity) HealthData() *Entity_HealthComponent {
	return be.damageable
}

func (be *BaseEntity) Behavior() *Entity_BehaviorComponent {
	return be.behavior
}

func (be *BaseEntity) DropTable() *Entity_DropTableComponent {
	return be.dropTable
}

func (be *BaseEntity) Projectile() *Entity_ProjectileComponent {
	return be.projectile
}

func CreateEntity(components ...func(*BaseEntity)) *BaseEntity {
	e := &BaseEntity{
		id: uuid.New(),
	}

	for _, comp := range components {
		comp(e)
	}

	return e
}

func WithName(name string) func(*BaseEntity) {
	return func(e *BaseEntity) {
		e.named = &Entity_NamedComponent{
			Name: name,
		}
	}
}

func WithDescription(description string) func(e *BaseEntity) {
	return func(e *BaseEntity) {
		e.described = &Entity_DescribedComponent{
			Description: description,
		}
	}
}

func WithPresentation(symbol rune, style tcell.Style) func(e *BaseEntity) {
	return func(e *BaseEntity) {
		e.presentable = &Entity_PresentableComponent{
			Rune:  symbol,
			Style: style,
		}
	}
}

func WithPosition(pos engine.Position) func(e *BaseEntity) {
	return func(e *BaseEntity) {
		e.positioned = &Entity_PositionedComponent{
			Position: pos,
		}
	}
}

func WithInventory(inv *EquippedInventory) func(e *BaseEntity) {
	return func(e *BaseEntity) {
		e.equipped = &Entity_EquippedComponent{
			Inventory: inv,
		}
	}
}

func WithStats(baseStats map[Stat]int, statModifiers ...StatModifier) func(e *BaseEntity) {
	return func(e *BaseEntity) {
		e.stats = &Entity_StatsHolderComponent{
			BaseStats: baseStats,
		}
	}
}

func WithHealthData(health, maxHealth int, isDead bool) func(e *BaseEntity) {
	return func(e *BaseEntity) {
		e.damageable = &Entity_HealthComponent{
			Health:    health,
			MaxHealth: maxHealth,
			IsDead:    isDead,
		}
	}
}

func WithBehavior(speed int, behavior func(npc Entity) (complete, requeue bool)) func(e *BaseEntity) {
	return func(e *BaseEntity) {
		e.behavior = &Entity_BehaviorComponent{
			Speed: speed,
			Behavior: func() (complete bool, requeue bool) {
				return behavior(e)
			},
		}
	}
}

func WithDropTable(table map[int]ItemSupplier) func(e *BaseEntity) {
	dropTable := CreateLootTable()

	for k, v := range table {
		dropTable.Add(k, v)
	}

	return func(e *BaseEntity) {
		e.dropTable = &Entity_DropTableComponent{
			DropTable: dropTable,
		}
	}
}

func WithProjectileData(source Entity, path *engine.Path) func(e *BaseEntity) {
	return func(e *BaseEntity) {
		e.projectile = &Entity_ProjectileComponent{
			Source: source,
			Path:   path,
		}
	}
}