local Query        = require "lib.spell.target_query"
local targetTest   = require "lib.spell.target_test"

--- @alias SpellPreview "default" Подсветка возможных целей
--- | "path" Подсветка пути до цели

--- @class Spell
--- @field tag string
--- @field baseCost integer Базовые затраты маны на каст
--- @field baseCooldown integer Базовый кулдаун в ходах
--- @field targetQuery SpellTargetQuery Селектор возможных целей
--- @field previewType SpellPreview Вид превью во время каста
--- @field distance? integer Сторона квадрата с центром в позиции кастера, в пределах которого должна находиться цель, либо отсутствие ограничения
--- @field update fun(self: Spell, caster: Character, dt: number): nil Изменяет состояние спелла
--- @field draw fun(self: Spell): nil Рисует превью каста, ничего не должна изменять в идеальном мире
--- @field cast fun(self: Spell, caster: Character, target: Vec3): Task<nil> | nil Вызывается в момент каста, изменяет мир.
local spell        = {}
spell.__index      = spell
spell.tag          = "spell_base"
spell.baseCost     = 1
spell.baseCooldown = 1
spell.targetQuery  = Query(targetTest.any)
spell.previewType  = "default"

function spell:update(caster, dt) end

function spell:draw() end

function spell:cast(caster, target) return end

--- Конструктор [Spell]
--- @param data {tag: string, baseCost: integer, baseCooldown: integer, targetQuery: SpellTargetQuery, distance: integer?, onCast: fun(caster: Character, target: Vec3): Task}
--- @return Spell
function spell.new(data)
    local newSpell = {
        tag = data.tag,
        baseCost = data.baseCost,
        baseCooldown = data.baseCooldown,
        targetQuery = data.targetQuery,
        distance = data.distance
    }

    function newSpell:cast(caster, target)
        -- проверка на расстояние до цели
        if self.distance and caster:try(Tree.behaviors.positioned, function(p)
                local dist = math.max(math.abs(p.position.x - target.x), math.abs(p.position.y - target.y))
                print("dist:", dist)
                return dist > self.distance
            end) then
            return
        end

        if not self.targetQuery.test(caster, target) then return end -- проверка корректности цели

        -- проверка на достаточное количество маны
        if caster:try(Tree.behaviors.stats, function(stats)
                return stats.mana < self.baseCost
            end) then
            return
        end

        caster:try(Tree.behaviors.stats, function(stats)
            stats.mana = stats.mana - self.baseCost
        end)

        return data.onCast(caster, target)
    end

    return setmetatable(newSpell, spell)
end

return spell
