using UUIDs
using Statistics
using Dates

mutable struct ReplayBuffer
    uuid::UUID
    #@todo change "buffer" to "games"
    games::Union{Nothing,Vector{MuZeroGame}}
    step_min::Integer
    step_max::Integer
    step_mean::Integer
    reanalyzed::UInt16
    saved_at::Union{Nothing, String}
    play_start::Union{Nothing, String}
    play_finish::Union{Nothing, String}
    play_duration::Union{Nothing, Int32}
    reanalyze_start::Union{Nothing, String}
    reanalyze_finish::Union{Nothing, String}
    reanalyze_duration::Union{Nothing, Int32}
    single_play_duration::Union{Nothing, Int32}
    single_reanalyze_duration::Union{Nothing, Int32}
end

gettotalreanalyses(x::ReplayBuffer) = x.reanalyzed * length(x)

function finishplay!(x::ReplayBuffer)
    x.play_finish = string(now())
    x.play_duration = Dates.value(
        now() - DateTime(x.play_start)
    )
    x.single_play_duration = Int32(round(x.play_duration / length(x)))
end

function updatenetstep!(b::ReplayBuffer)
    if !isempty(b)
        steps = map(x->x.net_step, b)
        b.step_min = steps |> minimum
        b.step_max = steps |> maximum
        b.step_mean = steps |> mean
    end
end

incrementreanalyzed!(b::ReplayBuffer) = b.reanalyzed += 1

function ReplayBuffer(config::MuZeroConfig)
    ReplayBuffer(
        uuid4(), 
        nothing, 
        0,
        0,
        0,
        0,
        nothing,
        string(now()),
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
        nothing,
    )
end

function atcapacity(x::ReplayBuffer, config::MuZeroConfig)
    length(x) >= config.buffer_capacity
end

Base.length(x::ReplayBuffer) = isnothing(x.games) ? 0 : length(x.games)
Base.lastindex(x::ReplayBuffer) = length(x)
Base.iterate(x::ReplayBuffer, state=1) = state > length(x) ? nothing : (x[state], state+1)
Base.getindex(x::ReplayBuffer, idx::Integer) = x.games[idx]

# using Random

# function Base.rand(x::ReplayBuffer)
#     if isempty(x)
#         return nothing
#     end
#     sample_idx = rand(1:length(x))
#     x[sample_idx]
# end

# function randtrainingbatch(x::ReplayBuffer, config::MuZeroConfig)
#     while isempty(x)
#     end
#     batch_size = config.batch_size
#     games = [rand(x) for _ in 1:batch_size]
#     TrainingBatch(
#         config.game_type,
#         config.action_space_size, 
#         [TrainingSample(config, g) for g in games])
# end


function getstatspath(x::ReplayBuffer, parent_dir)
    "$(getdir(x, parent_dir))/stats.toml"
end

getdir(x::ReplayBuffer, parent_dir) = "$parent_dir/$(x.uuid)"

function getpath(x::ReplayBuffer, parent_dir)
    "$(getdir(x, parent_dir))/buffer.bin"
end
