#include "ui/wherigo_dialog.h"
#include "lua/game_engine.h"
#include "lua/media_manager.h"

#include <wx/button.h>
#include <wx/html/htmlwin.h>
#include <wx/image.h>
#include <wx/log.h>
#include <wx/mstream.h>
#include <wx/sizer.h>
#include <wx/statbmp.h>
#include <wx/stattext.h>

namespace wherigo {

WherigoMessageDialog::WherigoMessageDialog(wxWindow *parent,
                                           const wxString &text,
                                           const wxString &title,
                                           const std::vector<wxString> &buttons,
                                           const wxString &mediaName)
    : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize,
               wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {

  auto *sizer = new wxBoxSizer(wxVERTICAL);

  // Media/Image (if provided)
  if (!mediaName.IsEmpty()) {
    const auto mediaData =
        MediaManager::getInstance().getMediaByName(mediaName.ToStdString());

    if (!mediaData.empty()) {
      wxMemoryInputStream stream(mediaData.data(), mediaData.size());

      if (wxImage image(stream); image.IsOk()) {
        // Get screen DPI for scaling
        const wxWindow *topWindow = wxTheApp->GetTopWindow();
        double contentScaleFactor = 1.0;
        if (topWindow) {
          contentScaleFactor = topWindow->GetContentScaleFactor();
        }

        // Scale image - doubled size from original (800x600 instead of 400x300)
        // Adjust for DPI/Retina displays
        const int maxWidth = static_cast<int>(800 * contentScaleFactor);

        if (const int maxHeight = static_cast<int>(600 * contentScaleFactor);
            image.GetWidth() > maxWidth || image.GetHeight() > maxHeight) {
          const double scaleX =
              static_cast<double>(maxWidth) / image.GetWidth();
          const double scaleY =
              static_cast<double>(maxHeight) / image.GetHeight();
          const double scale = std::min(scaleX, scaleY);
          image.Rescale(static_cast<int>(image.GetWidth() * scale),
                        static_cast<int>(image.GetHeight() * scale),
                        wxIMAGE_QUALITY_HIGH);
        }

        wxBitmap bitmap(image);
        auto *imageCtrl = new wxStaticBitmap(this, wxID_ANY, bitmap);
        sizer->Add(imageCtrl, 0, wxALL | wxALIGN_CENTER, 10);
      } else {
        wxLogDebug("Failed to load image: %s", mediaName);
        auto *mediaLabel = new wxStaticText(
            this, wxID_ANY, wxString::Format("[Media: %s]", mediaName));
        mediaLabel->SetForegroundColour(*wxLIGHT_GREY);
        sizer->Add(mediaLabel, 0, wxALL | wxALIGN_CENTER, 10);
      }
    } else {
      wxLogDebug("No media data for: %s", mediaName);
      auto *mediaLabel = new wxStaticText(
          this, wxID_ANY, wxString::Format("[Media: %s]", mediaName));
      mediaLabel->SetForegroundColour(*wxLIGHT_GREY);
      sizer->Add(mediaLabel, 0, wxALL | wxALIGN_CENTER, 10);
    }
  }

  // Helper lambda to convert plain text to HTML
  auto textToHtml = [](const wxString &plainText) -> wxString {
    wxString result = plainText;
    // Check if this looks like HTML (has tags)
    if (plainText.Find("<") == wxNOT_FOUND) {
      // Plain text - need to be careful with & to avoid breaking HTML entities
      // Replace & but preserve HTML entities like &nbsp; &amp; etc.
      wxString escaped;
      for (size_t i = 0; i < plainText.length(); i++) {
        if (plainText[i] == '&') {
          // Look ahead to see if this is an HTML entity
          size_t end = plainText.find(';', i);
          if (end != wxString::npos && end - i < 12) {
            // Likely an entity, copy as-is
            escaped += plainText.substr(i, end - i + 1);
            i = end;
          } else {
            // Not an entity, escape the &
            escaped += "&amp;";
          }
        } else if (plainText[i] == '\n') {
          escaped += "<br/>";
        } else {
          escaped += plainText[i];
        }
      }
      result = escaped;
    }
    return result;
  };

  wxColour bgColor = GetBackgroundColour();
  wxColour fgColor = GetForegroundColour();

  // Helper lambda to create and configure HTML window with auto-sizing
  auto createHtmlWindow = [this, &sizer, &textToHtml, bgColor,
                           fgColor](const wxString &message) {
    auto *htmlCtrl = new wxHtmlWindow(this, wxID_ANY);
    htmlCtrl->SetBorders(0);

    const wxString htmlContent = wxString::Format(
        "<html><body style=\"margin:0; padding:5px; "
        "background-color:#%02x%02x%02x; color:#%02x%02x%02x; "
        "font-family:sans-serif; font-size:11pt; line-height:1.5; "
        "user-select:none; -webkit-user-select:none; "
        "-moz-user-select:none;\">%s</body></html>",
        bgColor.Red(), bgColor.Green(), bgColor.Blue(), fgColor.Red(),
        fgColor.Green(), fgColor.Blue(), textToHtml(message));

    htmlCtrl->SetPage(htmlContent);
    // htmlCtrl->SetBackgroundColour(bgColor); // Removed as it might not exist
    // or be needed

    // Set a reasonable fixed size - will expand based on content
    // Width: fixed at 600px for consistent layout
    // Height: let wxHtmlWindow calculate based on content, with limits
    constexpr int contentWidth = 600;

    // Calculate approximate height based on line count (rough estimate)
    int lineCount = 1;
    for (size_t i = 0; i < message.length(); i++) {
      if (message[i] == '\n')
        lineCount++;
    }

    // Estimate: ~20px per line + 40px padding
    int estimatedHeight = std::max(80, lineCount * 20 + 40);
    estimatedHeight = std::clamp(estimatedHeight, 80, 400);

    htmlCtrl->SetMinSize(wxSize(contentWidth, estimatedHeight));

    sizer->Add(htmlCtrl, 1, wxALL | wxEXPAND, 10);
  };

  // Text (only show if not just a placeholder like ".")
  if (!text.IsEmpty() && text != ".") {
    createHtmlWindow(text);
  } else if (mediaName.IsEmpty()) {
    // No media and empty/placeholder text - show something
    createHtmlWindow(text);
  }

  // Buttons
  auto *buttonSizer = new wxBoxSizer(wxHORIZONTAL);
  wxButton *defaultButton = nullptr;

  if (buttons.empty()) {
    auto *okButton = new wxButton(this, wxID_OK, "OK");
    buttonSizer->Add(okButton, 0, wxALL, 5);
    okButton->Bind(wxEVT_BUTTON, &WherigoMessageDialog::onButton, this);
    defaultButton = okButton;
  } else {
    for (size_t i = 0; i < buttons.size(); i++) {
      auto *btn = new wxButton(this, 1000 + i, buttons[i]);
      buttonSizer->Add(btn, 0, wxALL, 5);
      btn->Bind(wxEVT_BUTTON, &WherigoMessageDialog::onButton, this);

      // Set first button as default
      if (i == 0) {
        defaultButton = btn;
      }
    }
  }

  // Set the default button (will be activated by Enter)
  if (defaultButton) {
    defaultButton->SetDefault();
  }

  sizer->Add(buttonSizer, 0, wxALIGN_CENTER | wxBOTTOM, 10);

  SetSizer(sizer);
  sizer->Layout();

  // Calculate dialog size based on content
  wxSize minSize = sizer->GetMinSize();

  // Set reasonable dialog size
  const int dialogWidth = std::clamp(minSize.GetWidth() + 40, 500, 800);
  const int dialogHeight = std::clamp(minSize.GetHeight() + 40, 300, 700);

  SetSize(dialogWidth, dialogHeight);
  wxTopLevelWindowBase::SetMinSize(wxSize(400, 250));
  CenterOnParent();
}

void WherigoMessageDialog::onButton(const wxCommandEvent &event) {
  if (const int id = event.GetId(); id == wxID_OK) {
    m_selectedButton = 0;
  } else {
    m_selectedButton = id - 1000;
  }
  EndModal(wxID_OK);
}

WherigoDialogRunner &WherigoDialogRunner::getInstance() {
  static WherigoDialogRunner instance;
  return instance;
}

void WherigoDialogRunner::showMessageBox(const wxString &text,
                                         const wxString &title,
                                         std::function<void(int)> callback) {
  WherigoMessageDialog dlg(nullptr, text, title);
  dlg.ShowModal();

  if (callback) {
    callback(dlg.getSelectedButton());
  }
}

void WherigoDialogRunner::showDialog(const std::vector<DialogEntry> &entries,
                                     std::function<void(int)> callback) {
  for (size_t i = 0; i < entries.size(); ++i) {
    const auto &entry = entries[i];

    std::vector<wxString> buttons;
    buttons.reserve(entry.buttons.size() + 1);
    for (const auto &btn : entry.buttons) {
      buttons.emplace_back(wxString::FromUTF8(btn));
    }

    if (buttons.empty()) {
      buttons.emplace_back(i + 1 < entries.size() ? "Weiter" : "OK");
    }

    WherigoMessageDialog dlg(nullptr, wxString::FromUTF8(entry.text), "Wherigo",
                             buttons, wxString::FromUTF8(entry.mediaName));
    dlg.ShowModal();

    // For the last entry, call the callback with the selected button
    if (i + 1 == entries.size() && callback) {
      callback(dlg.getSelectedButton());
    }

    // Notify state change after each dialog to ensure UI updates
    // This is important for goto events triggered during dialogs
    GameEngine::getInstance().notifyStateChanged();
  }
}

} // namespace wherigo
