#pragma once

#include "types.h"
#include "backend.h"
#include "auth.h"
#include "preview.h"

#include <atomic>
#include <chrono>
#include <mutex>
#include <thread>

namespace mediadb {

// ---- MIME by extension ----
std::string mime_for_ext(const std::string& ext_in);

// ---- multipart parser utilities ----

struct MultipartPart {
    std::string name;
    std::string filename;
    std::string content_type;
    std::string data;
};

std::string extract_param(const std::string& header, const std::string& param);
std::vector<MultipartPart> parse_multipart(const std::string& body, const std::string& boundary);
std::string extract_boundary(const std::string& content_type);

class App {
public:
    App(MediaBackendApi& db,
        const std::string& authdb_url,
        const std::string& authdb_client_name,
        const std::string& authdb_client_secret,
        const std::string& html_dir,
        BlobCache& cache,
        std::size_t render_threads = 0);
    ~App();

    bool is_authorized(const HttpRequest& req) const;
    std::unique_ptr<ImportSession> begin_import();
    void finish_import(bool ok, const std::string& error = {});
    HttpResponse start_import(const std::uint8_t* data, std::size_t len);
    HttpResponse start_import_file(const std::string& path);
    HttpResponse handle(const HttpRequest& req);
    HttpResponse handle_upload_form(const HttpRequest& req, UploadData upload);
    HttpResponse handle_import_form(const HttpRequest& req, std::vector<std::uint8_t> file_data);

private:
    // helpers
    static bool starts_with(const std::string& s, const std::string& prefix);
    static bool ends_with(const std::string& s, const std::string& suffix);
    static bool contains(const std::string& s, const std::string& sub);
    static std::string segment(const std::string& path, std::size_t prefix_len);

    // session tracking
    void store_session(const std::string& token, const std::string& username,
                        const std::string& domain_label = {},
                        const std::vector<std::string>& group_ids = {});
    std::string get_session_username(const HttpRequest& req) const;
    std::string get_session_domain(const HttpRequest& req) const;
    std::vector<std::string> get_session_groups(const HttpRequest& req) const;
    std::vector<std::string> resolve_session_groups(authdb::client::Client& client);
    void repopulate_session(const HttpRequest& req);
    bool is_session_known(const HttpRequest& req) const;
    bool is_any_authorized(const HttpRequest& req);
    bool has_store_access(const HttpRequest& req, const std::string& store_id) const;
    bool has_store_write_access(const HttpRequest& req, const std::string& store_id) const;

    // handlers
    HttpResponse handle_login(const HttpRequest& req);
    HttpResponse handle_export(const HttpRequest& req);
    HttpResponse handle_import(const HttpRequest& req);
    HttpResponse handle_import_status(const HttpRequest& req);
    HttpResponse handle_vacuum(const HttpRequest& req);
    HttpResponse handle_cluster_status(const HttpRequest& req);
    HttpResponse handle_cluster_stores(const HttpRequest& req);
    HttpResponse handle_cluster_sync(const HttpRequest& req);
    HttpResponse handle_cluster_scrub(const HttpRequest& req);
    HttpResponse handle_cluster_rebalance(const HttpRequest& req);
    HttpResponse handle_cluster_repair(const HttpRequest& req);
    HttpResponse handle_list_groups(const HttpRequest& req);
    HttpResponse handle_list_public_albums(const HttpRequest& req);
    HttpResponse handle_create_store(const HttpRequest& req);
    HttpResponse handle_list_stores(const HttpRequest& req);
    HttpResponse handle_get_store(const HttpRequest& req);
    HttpResponse handle_delete_store(const HttpRequest& req);
    HttpResponse handle_rename_store(const HttpRequest& req);
    HttpResponse handle_list_store_domains(const HttpRequest& req);
    HttpResponse handle_add_store_domain(const HttpRequest& req);
    HttpResponse handle_remove_store_domain(const HttpRequest& req);
    HttpResponse handle_list_store_acls(const HttpRequest& req);
    HttpResponse handle_add_store_acl(const HttpRequest& req);
    HttpResponse handle_remove_store_acl(const HttpRequest& req);
    HttpResponse handle_store_login(const HttpRequest& req);
    HttpResponse handle_create_album(const HttpRequest& req);
    HttpResponse handle_list_albums(const HttpRequest& req);
    HttpResponse handle_get_album(const HttpRequest& req);
    HttpResponse handle_delete_album(const HttpRequest& req);
    HttpResponse handle_set_album_public(const HttpRequest& req);
    HttpResponse handle_add_media(const HttpRequest& req);
    HttpResponse handle_list_media(const HttpRequest& req);
    HttpResponse handle_get_media(const HttpRequest& req);
    HttpResponse handle_delete_media(const HttpRequest& req);
    HttpResponse handle_get_media_raw(const HttpRequest& req);
    HttpResponse handle_preview(const HttpRequest& req);
    HttpResponse handle_static(const HttpRequest& req) const;

    // JSON serializers
    static json_object* store_to_json(const StoreRecord& s);
    static json_object* album_to_json(const AlbumRecord& a);
    static json_object* media_to_json(const MediaRecord& m);
    static json_object* domain_to_json(const AuthDomain& d, bool hide_secret = true);
    static json_object* acl_to_json(const StoreACL& acl);
    static json_object* msg_json(const std::string& msg);
    static std::string normalize_fmt(const std::string& fmt);
    static int parse_int(const std::unordered_map<std::string, std::string>& query, const std::string& key, int fallback);
    static std::string parse_string(const std::unordered_map<std::string, std::string>& query, const std::string& key,
                                    const std::string& fallback);
    static HttpResponse error_json(int status, const std::string& message);

    MediaBackendApi& db_;
    AuthService auth_;
    PreviewService previews_;
    BlobCache& cache_;
    std::string html_dir_;

    // session tracking
    static constexpr int SESSION_TTL_SECONDS = 1800; // 30 minutes
    static constexpr std::size_t MAX_SESSIONS = 10000;
    struct SessionInfo {
        std::string username;
        std::string domain_label;
        std::vector<std::string> group_ids;
        std::chrono::steady_clock::time_point created_at;
    };
    mutable std::mutex session_mutex_;
    std::unordered_map<std::string, SessionInfo> sessions_;
    void evict_expired_sessions();

    // import state
    std::mutex import_mutex_;
    std::atomic<bool> import_running_{false};
    std::atomic<bool> import_ok_{false};
    std::string import_error_;
    std::thread import_thread_;
};

} // namespace mediadb
