#include "extend/grid-model.hpp"
#include "extend/action-model.hpp"
#include "extend/color-model.hpp"
#include "extend/empty-view-model.hpp"
#include "extend/image-model.hpp"
#include "extend/pagination-model.hpp"
#include "utils.hpp"
#include <qjsonvalue.h>
#include <ranges>
#include <qjsonarray.h>
#include <qjsonobject.h>

GridItemViewModel GridModelParser::parseListItem(const QJsonObject &instance, size_t index) {
  GridItemViewModel model;
  auto props = instance.value("props").toObject();
  auto children = instance.value("children").toArray();

  model.id = props["id"].toString(QString::number(index)).toStdString();
  model.title = props["title"].toString().toStdString();
  model.subtitle = props["subtitle"].toString().toStdString();

  auto content = props.value("content");

  if (content.isObject()) {
    auto obj = content.toObject();

    if (obj.contains("tooltip")) { model.tooltip = obj.value("tooltip").toString().toStdString(); }

    if (obj.contains("color")) {
      model.content = ColorLikeModelParser().parse(obj.value("color"));
    } else if (obj.contains("value")) {
      model.content = ImageModelParser().parse(obj.value("value"));
    } else {
      model.content = ImageModelParser().parse(content);
    }
  } else {
    model.content = ImageModelParser().parse(content);
  }

  if (props.contains("keywords")) {
    model.keywords = ranges_to<std::vector>(
        props.value("keywords").toArray() |
        std::views::transform([](const QJsonValue &&a) { return a.toString().toStdString(); }));
  }

  for (const auto &child : children) {
    auto obj = child.toObject();
    auto type = obj["type"].toString().toStdString();

    if (type == "action-panel") { model.actionPannel = ActionPannelParser().parse(obj); }
  }

  return model;
}

GridSectionModel GridModelParser::parseSection(const QJsonObject &instance) {
  GridSectionModel model;
  size_t index = 0;
  auto props = instance.value("props").toObject();
  auto arr = instance.value("children").toArray();

  model.title = props.value("title").toString().toStdString();
  model.subtitle = props.value("subtitle").toString().toStdString();

  if (props.contains("fit")) { model.fit = parseFit(props.value("fit").toString().toStdString()); }
  if (props.contains("aspectRatio")) { model.aspectRatio = props.value("aspectRatio").toDouble(1); }
  if (props.contains("columns")) { model.columns = props.value("columns").toInt(); }
  if (auto inset = props.value("inset"); inset.isString()) {
    model.inset = parseInset(inset.toString().toStdString());
  }

  model.children.reserve(arr.size());

  for (const auto &child : arr) {
    auto obj = child.toObject();
    auto type = obj.value("type").toString().toStdString();

    if (type == "grid-item") {
      auto item = parseListItem(obj, index);

      model.children.push_back(item);
    }

    ++index;
  }

  return model;
}

GridInset GridModelParser::parseInset(const std::string &s) {
  if (s == "zero") return GridInset::None;
  if (s == "small") return GridInset::Small;
  if (s == "medium") return GridInset::Medium;
  if (s == "large") return GridInset::Large;

  return GridInset::Small;
}

ObjectFit GridModelParser::parseFit(const std::string &fit) {
  if (fit == "fill") return ObjectFit::Fill;
  return ObjectFit::Contain;
}

GridModel GridModelParser::parse(const QJsonObject &instance) {
  GridModel model;
  auto props = instance.value("props").toObject();
  // no builtin filtering by default if onSearchTextChange handler is specified
  bool const defaultFiltering = !props.contains("onSearchTextChange");

  model.dirty = instance.value("dirty").toBool(true);
  model.isLoading = props["isLoading"].toBool(false);
  model.throttle = props["throttle"].toBool(false);
  model.fit = parseFit(props.value("fit").toString().toStdString());
  model.aspectRatio = props.value("aspectRatio").toDouble(1);
  model.searchPlaceholderText = props["searchBarPlaceholder"].toString().toStdString();
  model.filtering = props["filtering"].toBool(defaultFiltering);

  if (auto inset = props.value("inset"); inset.isString()) {
    model.inset = parseInset(inset.toString().toStdString());
  }
  if (auto cols = props.value("columns"); cols.isDouble()) { model.columns = cols.toInt(); }

  if (props.contains("navigationTitle")) {
    model.navigationTitle = props.value("navigationTitle").toString().toStdString();
  }

  if (props.contains("onSearchTextChange")) {
    model.onSearchTextChange = props.value("onSearchTextChange").toString().toStdString();
  }

  if (props.contains("onSelectionChange")) {
    model.onSelectionChanged = props.value("onSelectionChange").toString().toStdString();
  }

  if (props.contains("selectedItemId")) {
    model.selectedItemId = props.value("selectedItemId").toString().toStdString();
  }
  if (props.contains("searchText")) { model.searchText = props.value("searchText").toString().toStdString(); }
  if (props.contains("pagination")) {
    model.pagination = PaginationModel::fromJson(props.value("pagination").toObject());
  }

  size_t index = 0;

  for (const auto &child : instance.value("children").toArray()) {
    auto childObj = child.toObject();
    auto type = childObj.value("type").toString().toStdString();

    if (type == "action-panel") { model.actions = ActionPannelParser().parse(childObj); }

    if (type == "grid-item") {
      auto item = parseListItem(childObj, index);

      model.items.emplace_back(item);
    }

    if (type == "grid-section") {
      auto section = parseSection(childObj);

      model.items.emplace_back(section);
    }

    if (type == "dropdown") { model.searchBarAccessory = DropdownModel::fromJson(childObj); }

    if (type == "empty-view") { model.emptyView = EmptyViewModelParser().parse(childObj); }

    ++index;
  }

  return model;
}

GridModelParser::GridModelParser() = default;
