/*********************************************************************
 * \file   BigFile.hpp
 *
 * \author Romain BOULLARD
 * \date   October 2025
 *********************************************************************/
#ifndef BIGFOOT_ENGINE_BIGFILE_BIGFILE_HPP
#define BIGFOOT_ENGINE_BIGFILE_BIGFILE_HPP
#include <System/File.hpp>

#include <Utils/TaggedType.hpp>

#include <EASTL/span.h>
#include <sqlite3.h>

namespace Bigfoot
{
class BigFile
{
  public:
    /**
     * Constructor
     *
     * \param p_file The file targetting the BigFile DB
     */
    BigFile(const File& p_file);

    BigFile(const BigFile& p_bigFile) = delete;
    BigFile(BigFile&& p_bigFile) = delete;

    /**
     * Begin a transaction
     *
     */
    void BeginTransaction();

    /**
     * Commit a transaction
     *
     */
    void CommitTransaction();

    /**
     * Rollback a transaction
     *
     */
    void RollbackTransaction();

    ~BigFile();

    BigFile& operator=(const BigFile& p_bigFile) = delete;
    BigFile& operator=(BigFile&& p_bigFile) = delete;

    class Request
    {
      public:
        /*
         * Constructor
         *
         * \param p_bigFile The bigfile
         * \param p_request The SQL request
         */
        Request(const BigFile& p_bigFile, const eastl::string_view p_request);

        Request(const Request& p_request) = delete;
        Request(Request&& p_request) = delete;

        using CopyValue = TaggedType<bool>;

        /*
         * Bind a int32 value to the Request at index
         *
         * \param p_index The index to bind to
         * \param p_value The value
         */
        void Bind(const std::uint32_t p_index, const std::int32_t p_value);

        /*
         * Bind a uint32 value to the Request at index
         *
         * \param p_index The index to bind to
         * \param p_value The value
         */
        void Bind(const std::uint32_t p_index, const std::uint32_t p_value);

        /*
         * Bind a int64 value to the Request at index
         *
         * \param p_index The index to bind to
         * \param p_value The value
         */
        void Bind(const std::uint32_t p_index, const std::int64_t p_value);

        /*
         * Bind a uint64 value to the Request at index
         *
         * \param p_index The index to bind to
         * \param p_value The value
         */
        void Bind(const std::uint32_t p_index, const std::uint64_t p_value);

        /*
         * Bind a float value to the Request at index
         *
         * \param p_index The index to bind to
         * \param p_value The value
         */
        void Bind(const std::uint32_t p_index, const float p_value);

        /*
         * Bind a double value to the Request at index
         *
         * \param p_index The index to bind to
         * \param p_value The value
         */
        void Bind(const std::uint32_t p_index, const double p_value);

        /*
         * Bind a string value to the Request at index
         *
         * \param p_index The index to bind to
         * \param p_value The value
         * \param p_copy Should the memory be copied so that caller does not have to retain it
         */
        void Bind(const std::uint32_t p_index,
                  const eastl::string_view p_value,
                  const CopyValue p_copy = CopyValue {false});

        /*
         * Bind a blob value to the Request at index
         *
         * \param p_index The index to bind to
         * \param p_value The value
         * \param p_copy Should the memory be copied so that caller does not have to retain it
         */
        void Bind(const std::uint32_t p_index,
                  const eastl::span<const std::byte> p_value,
                  const CopyValue p_copy = CopyValue {false});

        /*
         * Step through the request
         *
         * \return True if the request can continue (get next row), false otherwise
         */
        [[nodiscard]]
        bool Step();

        /*
         * Execute the request once
         *
         * The number of modified rows
         */
        [[maybe_unused]]
        std::uint32_t Execute();

        class Column
        {
          public:
            /*
             * Constructor
             *
             * \param p_request The request to get the column of
             * \param p_index Column index
             */
            Column(const Request& p_request, const std::uint32_t p_index);

            Column(const Column& p_column) = default;
            Column(Column&& p_column) = default;

            ~Column() = default;

            Column& operator=(const Column& p_column) = default;
            Column& operator=(Column&& p_column) = default;

            /*
             * Get value as a int32
             *
             * \return The value
             */
            operator std::int32_t() const;

            /*
             * Get value as a uint32
             *
             * \return The value
             */
            operator std::uint32_t() const;

            /*
             * Get value as a int64
             *
             * \return The value
             */
            operator std::int64_t() const;

            /*
             * Get value as a uint64
             *
             * \return The value
             */
            operator std::uint64_t() const;

            /*
             * Get value as a float
             *
             * \return The value
             */
            operator float() const;

            /*
             * Get value as a double
             *
             * \return The value
             */
            operator double() const;

            /*
             * Get value as a string
             *
             * \return The value
             */
            operator eastl::string_view() const;

            /*
             * Get value as a blob
             *
             * \return The value
             */
            operator eastl::span<const std::byte>() const;

          private:
            /*
             * The statement
             */
            sqlite3_stmt* m_statement;

            /*
             * The column index
             */
            std::uint32_t m_index;
        };

        /*
         * Get a column
         *
         * \param p_index Column index
         */
        [[nodiscard]]
        Column Get(const std::uint32_t p_index) const;

        ~Request();

        Request& operator=(const Request& p_request) = delete;
        Request& operator=(Request&& p_request) = delete;

      private:
        /*
         * The database
         */
        sqlite3* m_db;

        /*
         * The statement
         */
        sqlite3_stmt* m_statement;
    };

  private:
    /**
     * The BigFile DB
     */
    sqlite3* m_db;
};
}   // namespace Bigfoot

#endif
