/*
 * Copyright 2025 Bloomberg Finance LP
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef INCLUDED_BUILDBOXCASD_INODECACHE_H
#define INCLUDED_BUILDBOXCASD_INODECACHE_H

#include <buildboxcommon_protos.h>

#include <mutex>
#include <optional>
#include <string_view>
#include <sys/stat.h>
#include <unordered_map>

#if __APPLE__
#define st_mtim st_mtimespec
#define st_ctim st_ctimespec
#endif
namespace buildboxcasd {

class InodeCache final {
  public:
    struct Key {
        dev_t st_dev;
        ino_t st_ino;
        off_t st_size;
        struct timespec st_mtim;
        struct timespec st_ctim;

        void setFromStat(const struct stat &st);

        bool operator==(const Key &other) const
        {
            return st_dev == other.st_dev && st_ino == other.st_ino &&
                   st_size == other.st_size &&
                   st_mtim.tv_sec == other.st_mtim.tv_sec &&
                   st_mtim.tv_nsec == other.st_mtim.tv_nsec &&
                   st_ctim.tv_sec == other.st_ctim.tv_sec &&
                   st_ctim.tv_nsec == other.st_ctim.tv_nsec;
        }
    };

    struct KeyHash {
        std::size_t operator()(const Key &k) const noexcept
        {
            std::string_view sv(reinterpret_cast<const char *>(&k), sizeof(k));
            return std::hash<std::string_view>()(sv);
        }
    };

    InodeCache();

    friend void swap(InodeCache &a, InodeCache &b)
    {
        const std::lock_guard<std::mutex> lockA(a.d_mutex);
        const std::lock_guard<std::mutex> lockB(b.d_mutex);

        std::swap(a.d_inodeDigestMap, b.d_inodeDigestMap);
    }

    std::optional<buildboxcommon::Digest> getDigest(const struct stat &st);
    std::optional<buildboxcommon::Digest> getDigest(const Key &k);

    void addInode(const struct stat &st, const buildboxcommon::Digest &digest);

  private:
    std::mutex d_mutex;
    std::unordered_map<Key, buildboxcommon::Digest, KeyHash> d_inodeDigestMap;
};

} // namespace buildboxcasd
#endif
