#include "BookDetails.hpp"
#include <giomm/appinfo.h>
#include <giomm/file.h>
#include <iostream>

BookDetails::BookDetails(DatabaseManager& db, BookList& book_list) :
  Gtk::Box(Gtk::Orientation::VERTICAL, 12),
  m_db(db),
  m_book_list(book_list) {
  set_margin_top(24);
  set_margin_bottom(24);
  set_margin_start(24);
  set_margin_end(24);

  m_cover.set_pixel_size(200);
  append(m_cover);

  m_title.set_markup("<span weight='bold' size='large'></span>");
  append(m_title);

  m_author.set_markup("<span size='medium'></span>");
  append(m_author);

  m_open_button.set_label("Open Book");
  m_open_button.signal_clicked().connect(sigc::mem_fun(*this, &BookDetails::on_open_button_clicked));
  append(m_open_button);

  auto& tags_label = *Gtk::make_managed<Gtk::Label>("Tags:");
  tags_label.set_halign(Gtk::Align::START);
  tags_label.set_margin_top(12);
  append(tags_label);

  m_tags_box.set_selection_mode(Gtk::SelectionMode::NONE);
  m_tags_box.set_homogeneous(false);
  m_tags_box.set_max_children_per_line(10);
  append(m_tags_box);

  auto& tag_entry_box = *Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 6);
  m_tag_entry.set_placeholder_text("New tag...");
  tag_entry_box.append(m_tag_entry);
  m_add_tag_button.set_label("Add Tag");
  m_add_tag_button.signal_clicked().connect(sigc::mem_fun(*this, &BookDetails::on_add_tag_button_clicked));
  tag_entry_box.append(m_add_tag_button);
  append(tag_entry_box);

  // Connect to BookList signals to handle book removal/updates
  m_conn_removed = m_book_list.signalBookRemoved().connect(
    sigc::mem_fun(*this, &BookDetails::on_book_removed));
  m_conn_updated = m_book_list.signalBookUpdated().connect(
    [this](const Book& book) {
      if (book.id() == m_book_id) {
        refresh_display();
      }
    });
  m_conn_reset = m_book_list.signalReset().connect(
    sigc::mem_fun(*this, &BookDetails::on_book_list_changed));
}

BookDetails::~BookDetails() {
  m_conn_removed.disconnect();
  m_conn_updated.disconnect();
  m_conn_reset.disconnect();
}

Gtk::Widget* BookDetails::create_tag_chip(const std::string& tag_name) {
  auto& chip = *Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 4);
  chip.add_css_class("tag-chip");
  chip.set_margin_top(2);
  chip.set_margin_bottom(2);
  chip.set_margin_start(2);
  chip.set_margin_end(2);

  auto& label = *Gtk::make_managed<Gtk::Label>(tag_name);
  chip.append(label);

  auto& remove_btn = *Gtk::make_managed<Gtk::Button>();
  remove_btn.set_icon_name("window-close-symbolic");
  remove_btn.add_css_class("flat");
  remove_btn.add_css_class("circular");
  remove_btn.set_tooltip_text("Remove tag");
  remove_btn.signal_clicked().connect([this, tag_name]() {
    on_remove_tag_clicked(tag_name);
  });
  chip.append(remove_btn);

  return &chip;
}

void BookDetails::refresh_tags() {
  // Clear existing tags
  while (auto child = m_tags_box.get_child_at_index(0)) {
    m_tags_box.remove(*child);
  }

  if (m_book_id.empty()) return;

  // Reload tags from database
  auto tags = m_db.get_tags_for_book(m_book_id);
  for (const auto& tag : tags) {
    m_tags_box.append(*create_tag_chip(tag));
  }
}

void BookDetails::refresh_display() {
  if (m_book_id.empty()) return;

  auto book_opt = m_book_list.findById(m_book_id);
  if (!book_opt) {
    // Book no longer exists
    clear();
    m_signalBookRemoved.emit();
    return;
  }

  const auto& book = *book_opt;
  m_cover.set(book.cover());
  m_title.set_markup("<span weight='bold' size='large'>" + Glib::Markup::escape_text(book.title()) + "</span>");
  m_author.set_markup("<span size='medium'>" + Glib::Markup::escape_text(book.author()) + "</span>");
  refresh_tags();
}

void BookDetails::set_book(const Book* book) {
  if (book) {
    m_book_id = book->id();
    m_cover.set(book->cover());
    m_title.set_markup("<span weight='bold' size='large'>" + Glib::Markup::escape_text(book->title()) + "</span>");
    m_author.set_markup("<span size='medium'>" + Glib::Markup::escape_text(book->author()) + "</span>");
    refresh_tags();
  } else {
    clear();
  }
}

void BookDetails::clear() {
  m_book_id.clear();
  m_cover.clear();
  m_title.set_markup("<span weight='bold' size='large'></span>");
  m_author.set_markup("<span size='medium'></span>");
  while (auto child = m_tags_box.get_child_at_index(0)) {
    m_tags_box.remove(*child);
  }
}

void BookDetails::on_book_removed(const std::string& id) {
  if (id == m_book_id) {
    clear();
    m_signalBookRemoved.emit();
  }
}

void BookDetails::on_book_list_changed() {
  // After a full reset, check if our book still exists
  if (m_book_id.empty()) return;

  auto book_opt = m_book_list.findById(m_book_id);
  if (!book_opt) {
    clear();
    m_signalBookRemoved.emit();
  } else {
    refresh_display();
  }
}

void BookDetails::on_open_button_clicked() {
  if (m_book_id.empty()) return;

  auto book_opt = m_book_list.findById(m_book_id);
  if (!book_opt) {
    std::cerr << "Book no longer exists: " << m_book_id << "\n";
    return;
  }

  const auto& path = book_opt->filePath();
  if (path.empty()) {
    std::cerr << "Activated book has no file path: " << book_opt->title() << "\n";
    return;
  }
  try {
    auto file = Gio::File::create_for_path(path);
    if (!file->query_exists()) {
      std::cerr << "Book file is missing: " << path << "\n";
      return;
    }
    // Ask the desktop to open the file with the user's default application.
    const auto uri = Glib::filename_to_uri(path);
    Gio::AppInfo::launch_default_for_uri(uri);
  } catch (const Glib::Error& e) {
    std::cerr << "Failed to open book '" << book_opt->title() << "': "
              << e.what() << "\n";
  }
}

void BookDetails::on_add_tag_button_clicked() {
  if (m_book_id.empty()) return;

  auto tag_name = m_tag_entry.get_text();
  if (tag_name.empty()) return;

  // Trim whitespace
  auto trimmed = Glib::ustring(tag_name).lowercase();
  if (trimmed.empty()) return;

  m_db.add_tag_to_book(m_book_id, std::string(tag_name));
  m_tag_entry.set_text("");
  refresh_tags();
}

void BookDetails::on_remove_tag_clicked(const std::string& tag_name) {
  if (m_book_id.empty()) return;

  m_db.remove_tag_from_book(m_book_id, tag_name);
  refresh_tags();
}
