#include "lumacs/buffer_manager.hpp"
#include "lumacs/logger.hpp"
#include <spdlog/spdlog.h>
#include <algorithm>

namespace lumacs {

BufferManager::BufferManager(IEditorNotifier& notifier, IWindowManager& window_manager) 
    : notifier_(notifier), window_manager_(window_manager) {
}

std::shared_ptr<Buffer> BufferManager::create_buffer_no_window(std::string name) {
    // Check if a buffer with this name already exists
    auto existing_buf = get_buffer_by_name(name);
    if (existing_buf) {
        return existing_buf;
    }

    auto new_buf = std::make_shared<Buffer>(std::move(name));
    buffers_.push_back(new_buf);
    return new_buf;
}

void BufferManager::new_buffer(std::string name) {
    auto new_buf = create_buffer_no_window(std::move(name));
    
    // Set this new buffer in the active window
    if (window_manager_.active_window()) {
        window_manager_.active_window()->set_buffer(new_buf);
    } else {
        // This case should ideally not happen if EditorCore initializes properly
        // For robustness, create a default window if none exists
        spdlog::error("No active window to set new buffer. This should not happen.");
    }

    notifier_.emit_event(EditorEvent::BufferModified);
    notifier_.emit_event(EditorEvent::CursorMoved);
    notifier_.emit_event(EditorEvent::ViewportChanged);
}

bool BufferManager::load_file(const std::filesystem::path& path) {
    std::filesystem::path abs_path = std::filesystem::absolute(path);

    // Check if already open
    for (const auto& buf : buffers_) {
        if (buf->file_path() && std::filesystem::equivalent(*buf->file_path(), abs_path)) {
            if (window_manager_.active_window()) {
                window_manager_.active_window()->set_buffer(buf);
            }
            notifier_.emit_event(EditorEvent::BufferModified);
            notifier_.emit_event(EditorEvent::CursorMoved);
            notifier_.emit_event(EditorEvent::ViewportChanged);
            return true;
        }
    }

    auto new_buffer_opt = Buffer::from_file(abs_path);
    if (!new_buffer_opt) {
        return false;
    }
    
    auto new_buffer = std::make_shared<Buffer>(std::move(*new_buffer_opt));
    buffers_.push_back(new_buffer);

    if (window_manager_.active_window()) {
        window_manager_.active_window()->set_buffer(new_buffer);
    }

    notifier_.emit_event(EditorEvent::BufferModified);
    notifier_.emit_event(EditorEvent::CursorMoved);
    notifier_.emit_event(EditorEvent::ViewportChanged);
    return true;
}

bool BufferManager::switch_buffer_in_window(const std::string& name) {
    auto buf = get_buffer_by_name(name);
    if (!buf) {
        return false;
    }

    if (window_manager_.active_window()) {
        window_manager_.active_window()->set_buffer(buf);
    }
    notifier_.emit_event(EditorEvent::BufferModified);
    notifier_.emit_event(EditorEvent::CursorMoved);
    notifier_.emit_event(EditorEvent::ViewportChanged);
    return true;
}

bool BufferManager::close_buffer(const std::string& name) {
    auto buf_to_close = get_buffer_by_name(name);
    if (!buf_to_close) {
        return false;
    }

    // Cannot close buffer if it's the only one
    if (buffers_.size() <= 1) {
        notifier_.set_message("Cannot close last buffer");
        return false;
    }

    // Check if buffer is displayed in any window and switch it
    std::vector<std::shared_ptr<Window>> windows;
    // Need a way for BufferManager to access all windows.
    // EditorCore should provide this functionality.
    window_manager_.collect_windows(window_manager_.root_layout().get(), windows); // Assuming EditorCore has this helper

    for (const auto& win : windows) {
        if (win->buffer_ptr() == buf_to_close) {
            // Find another buffer to switch to
            auto other_buf_it = std::find_if(buffers_.begin(), buffers_.end(),
                                             [&](const std::shared_ptr<Buffer>& b) { return b != buf_to_close; });
            if (other_buf_it != buffers_.end()) {
                win->set_buffer(*other_buf_it);
            } else {
                // This scenario should be prevented by the buffers_.size() <= 1 check
                spdlog::error("No other buffer found to switch to after closing.");
                return false; 
            }
        }
    }

    // Remove buffer from list
    buffers_.remove(buf_to_close);

    notifier_.emit_event(EditorEvent::BufferModified);
    notifier_.emit_event(EditorEvent::CursorMoved);
    notifier_.emit_event(EditorEvent::ViewportChanged);
    return true;
}

std::shared_ptr<Buffer> BufferManager::active_buffer() {
    if (window_manager_.active_window()) {
        return window_manager_.active_window()->buffer_ptr();
    }
    return nullptr; // Should not happen in a well-initialized editor
}

std::shared_ptr<Buffer> BufferManager::get_buffer_by_name(const std::string& name) const {
    for (const auto& buf : buffers_) {
        if (buf->name() == name) {
            return buf;
        }
    }
    return nullptr;
}

std::vector<std::string> BufferManager::get_buffer_names() const {
    std::vector<std::string> names;
    names.reserve(buffers_.size());
    for (const auto& buf : buffers_) {
        names.push_back(buf->name());
    }
    return names;
}

std::vector<BufferManager::BufferInfo> BufferManager::get_all_buffer_info() const {
    std::vector<BufferInfo> info;
    info.reserve(buffers_.size());

    for (const auto& buf : buffers_) {
        BufferInfo bi;
        bi.name = buf->name();
        bi.size = buf->line_count();
        bi.modified = buf->is_modified();
        bi.mode = "fundamental-mode"; // TODO: Get actual mode from buffer
        bi.filepath = buf->file_path();
        info.push_back(bi);
    }

    return info;
}

} // namespace lumacs
