#include "ReplayReader.h"

#include <fstream>
#include <sstream>
#include <string>

#include "Command.h"
#include "CommandSerializer.h"

namespace
{
void stripCarriageReturn(std::string& line)
{
    if (!line.empty() && line.back() == '\r')
    {
        line.pop_back();
    }
}

// Splits "key value with spaces" into (key, remainder). Remainder may be empty.
void splitKeyValue(const std::string& line, std::string& key, std::string& value)
{
    const std::string::size_type space = line.find(' ');
    if (space == std::string::npos)
    {
        key   = line;
        value.clear();
    }
    else
    {
        key   = line.substr(0, space);
        value = line.substr(space + 1);
    }
}
}  // namespace

std::optional<ParsedReplay> readReplayFile(const std::string& path)
{
    std::ifstream stream(path, std::ios::in);
    if (!stream.is_open())
    {
        return std::nullopt;
    }

    ParsedReplay replay;
    std::string  line;

    // --- Header (up to the "---" separator) ---
    bool sawSeparator = false;
    while (std::getline(stream, line))
    {
        stripCarriageReturn(line);
        if (line == "---")
        {
            sawSeparator = true;
            break;
        }
        if (line.empty() || line[0] == '#')
        {
            continue;  // banner / blank
        }

        std::string key;
        std::string value;
        splitKeyValue(line, key, value);
        try
        {
            if (key == "version")          { replay.header.version    = std::stoi(value); }
            else if (key == "build")       { replay.header.build      = value; }
            else if (key == "seed")        { replay.header.seed       = static_cast<unsigned int>(std::stoul(value)); }
            else if (key == "config_hash") { replay.header.configHash = value; }
            else if (key == "timestamp")   { replay.header.timestamp  = value; }
        }
        catch (const std::exception&)
        {
            return std::nullopt;
        }
    }

    if (!sawSeparator)
    {
        return std::nullopt;
    }

    // --- Command / checksum stream ---
    while (std::getline(stream, line))
    {
        stripCarriageReturn(line);
        if (line.empty())
        {
            continue;
        }

        if (line[0] == '#')
        {
            // "# checksum <tick> <hex>"
            std::istringstream in(line);
            std::string hash;
            std::string keyword;
            ReplayEntry entry;
            in >> hash >> keyword >> entry.tick >> hash;
            if (keyword != "checksum")
            {
                continue;  // unknown comment line — ignore
            }
            try
            {
                entry.fingerprint = std::stoull(hash, nullptr, 16);
            }
            catch (const std::exception&)
            {
                return std::nullopt;
            }
            entry.isCommand = false;
            replay.entries.push_back(std::move(entry));
            continue;
        }

        // "<tick> <serialized command>"
        const std::string::size_type space = line.find(' ');
        if (space == std::string::npos)
        {
            return std::nullopt;
        }
        ReplayEntry entry;
        try
        {
            entry.tick = std::stoll(line.substr(0, space));
        }
        catch (const std::exception&)
        {
            return std::nullopt;
        }
        std::shared_ptr<Command> command = parseCommand(line.substr(space + 1));
        if (!command)
        {
            return std::nullopt;
        }
        entry.isCommand = true;
        entry.command   = std::move(command);
        replay.entries.push_back(std::move(entry));
    }

    return replay;
}
