package controller

import (
	"strings"

	e "galaxy/error"

	"galaxy/game/internal/model/game"

	"github.com/google/uuid"
)

// RaceID returns ID of race with given actor's name or error when race not found or extinct
func (c Controller) RaceID(actor string) (uuid.UUID, error) {
	ri, err := c.Cache.validRace(actor)
	if err != nil {
		return uuid.Nil, err
	}
	return c.Cache.g.Race[ri].ID, nil
}

func (c Controller) RaceBanish(actor string) error {
	ri, err := c.Cache.validRace(actor)
	if err != nil {
		return err
	}
	c.Cache.g.Race[ri].Extinct = true
	return nil
}

func (c Controller) RaceQuit(actor string) error {
	ri, err := c.Cache.validRace(actor)
	if err != nil {
		return err
	}
	c.Cache.g.Race[ri].TTL = 3
	return nil
}

func (c Controller) RaceVote(actor, acceptor string) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	rec, err := c.Cache.validRace(acceptor)
	if err != nil {
		return err
	}
	c.Cache.g.Race[ri].VoteFor = c.Cache.g.Race[rec].ID
	return nil
}

func (c Controller) RaceRelation(actor, acceptor string, v string) error {
	rel, ok := game.ParseRelation(v)
	if !ok {
		return e.NewUnknownRelationError(v)
	}
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	other, err := c.Cache.validRace(acceptor)
	if err != nil {
		return err
	}
	return c.Cache.UpdateRelation(ri, other, rel)
}

func (c *Controller) ShipClassCreate(actor, typeName string, drive float64, ammo int, weapons, shileds, cargo float64) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	return c.Cache.ShipClassCreate(ri, typeName, drive, ammo, weapons, shileds, cargo)
}

func (c *Controller) ShipClassMerge(actor, name, targetName string) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	return c.Cache.shipClassMerge(ri, name, targetName)
}

func (c *Controller) ShipClassRemove(actor, typeName string) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	return c.Cache.shipClassRemove(ri, typeName)
}

func (c *Controller) ShipGroupLoad(actor string, groupID uuid.UUID, cargoType string, quantity float64) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	ct, ok := game.CargoTypeSet[strings.ToLower(cargoType)]
	if !ok {
		return e.NewCargoTypeInvalidError(cargoType)
	}
	return c.Cache.shipGroupLoad(ri, groupID, ct, quantity)
}

func (c *Controller) ShipGroupUnload(actor string, groupID uuid.UUID, quantity float64) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	return c.Cache.shipGroupUnload(ri, groupID, quantity)
}

func (c *Controller) ShipGroupSend(actor string, groupID uuid.UUID, planetNumber uint) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	return c.Cache.shipGroupSend(ri, groupID, planetNumber)
}

func (c *Controller) ShipGroupUpgrade(actor string, groupID uuid.UUID, techInput string, limitLevel float64) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	return c.Cache.shipGroupUpgrade(ri, groupID, techInput, limitLevel)
}

func (c *Controller) ShipGroupMerge(actor string) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	c.Cache.shipGroupMerge(ri)
	return nil
}

func (c *Controller) ShipGroupBreak(actor string, groupID, newID uuid.UUID, quantity uint) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	return c.Cache.ShipGroupBreak(ri, groupID, newID, quantity)
}

func (c *Controller) ShipGroupDismantle(actor string, groupID uuid.UUID) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	return c.Cache.shipGroupDismantle(ri, groupID)
}

func (c *Controller) ShipGroupTransfer(actor, acceptor string, groupID uuid.UUID) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	riAccept, err := c.Cache.validRace(acceptor)
	if err != nil {
		return err
	}
	return c.Cache.shipGroupTransfer(ri, riAccept, groupID)
}

func (c *Controller) ShipGroupJoinFleet(actor, fleetName string, groupID uuid.UUID) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	return c.Cache.ShipGroupJoinFleet(ri, fleetName, groupID)
}

func (c *Controller) FleetMerge(actor, fleetSourceName, fleetTargetName string) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	return c.Cache.fleetMerge(ri, fleetSourceName, fleetTargetName)
}

func (c *Controller) FleetSend(actor, fleetName string, planetNumber uint) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	fi, ok := c.Cache.fleetIndex(ri, fleetName)
	if !ok {
		return e.NewEntityNotExistsError("fleet %q", fleetName)
	}
	return c.Cache.FleetSend(ri, fi, planetNumber)
}

func (c *Controller) ScienceCreate(actor, typeName string, drive, weapons, shields, cargo float64) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	return c.Cache.ScienceCreate(ri, typeName, drive, weapons, shields, cargo)
}

func (c *Controller) ScienceRemove(actor, typeName string) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	return c.Cache.ScienceRemove(ri, typeName)
}

func (c *Controller) PlanetRename(actor string, planetNumber int, name string) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	return c.Cache.PlanetRename(ri, planetNumber, name)
}

func (c *Controller) PlanetProduce(actor string, planetNumber int, prodType, subject string) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	var prod game.ProductionType
	switch game.ProductionType(strings.ToUpper(prodType)) {
	case game.ProductionMaterial:
		prod = game.ProductionMaterial
	case game.ProductionCapital:
		prod = game.ProductionCapital
	case game.ResearchDrive:
		prod = game.ResearchDrive
	case game.ResearchWeapons:
		prod = game.ResearchWeapons
	case game.ResearchShields:
		prod = game.ResearchShields
	case game.ResearchCargo:
		prod = game.ResearchCargo
	case game.ResearchScience:
		prod = game.ResearchScience
	case game.ProductionShip:
		prod = game.ProductionShip
	default:
		return e.NewProductionInvalidError(prodType)
	}
	return c.Cache.PlanetProduce(ri, planetNumber, prod, subject)
}

func (c *Controller) PlanetRouteSet(actor, loadType string, origin, destination uint) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	rt, ok := game.RouteTypeSet[strings.ToLower(loadType)]
	if !ok {
		return e.NewCargoTypeInvalidError(loadType)
	}
	return c.Cache.PlanetRouteSet(ri, rt, origin, destination)
}

func (c *Controller) PlanetRouteRemove(actor, loadType string, origin uint) error {
	ri, err := c.Cache.validActor(actor)
	if err != nil {
		return err
	}
	rt, ok := game.RouteTypeSet[strings.ToLower(loadType)]
	if !ok {
		return e.NewCargoTypeInvalidError(loadType)
	}
	return c.Cache.PlanetRouteRemove(ri, rt, origin)
}
