import discourseComputed, { on } from "discourse-common/utils/decorators";
import BufferedMixin from "ember-buffered-proxy/mixin";
import BufferedProxy from "ember-buffered-proxy/proxy";
import Controller from "@ember/controller";
import EmberObjectProxy from "@ember/object/proxy";
import Evented from "@ember/object/evented";
import ModalFunctionality from "discourse/mixins/modal-functionality";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { sort } from "@ember/object/computed";

export default Controller.extend(ModalFunctionality, Evented, {
  init() {
    this._super(...arguments);
    this.categoriesSorting = ["position"];
  },

  @discourseComputed("site.categories.[]")
  categoriesBuffered(categories) {
    const bufProxy = EmberObjectProxy.extend(BufferedMixin || BufferedProxy);
    return (categories || []).map((c) => bufProxy.create({ content: c }));
  },

  categoriesOrdered: sort("categoriesBuffered", "categoriesSorting"),

  /**
   * 1. Make sure all categories have unique position numbers.
   * 2. Place sub-categories after their parent categories while maintaining
   *    the same relative order.
   *
   *    e.g.
   *      parent/c2/c1          parent
   *      parent/c1             parent/c1
   *      parent          =>    parent/c2
   *      other                 parent/c2/c1
   *      parent/c2             other
   *
   **/
  @on("init")
  reorder() {
    const reorderChildren = (categoryId, depth, index) => {
      this.categoriesOrdered.forEach((category) => {
        if (
          (categoryId === null && !category.get("parent_category_id")) ||
          category.get("parent_category_id") === categoryId
        ) {
          category.setProperties({ depth, position: index++ });
          index = reorderChildren(category.get("id"), depth + 1, index);
        }
      });

      return index;
    };

    reorderChildren(null, 0, 0);

    this.categoriesBuffered.forEach((bc) => {
      if (bc.get("hasBufferedChanges")) {
        bc.applyBufferedChanges();
      }
    });

    this.notifyPropertyChange("categoriesBuffered");
  },

  move(category, direction) {
    let otherCategory;

    if (direction === -1) {
      // First category above current one
      const categoriesOrderedDesc = this.categoriesOrdered.reverse();
      otherCategory = categoriesOrderedDesc.find(
        (c) =>
          category.get("parent_category_id") === c.get("parent_category_id") &&
          c.get("position") < category.get("position")
      );
    } else if (direction === 1) {
      // First category under current one
      otherCategory = this.categoriesOrdered.find(
        (c) =>
          category.get("parent_category_id") === c.get("parent_category_id") &&
          c.get("position") > category.get("position")
      );
    } else {
      // Find category occupying target position
      otherCategory = this.categoriesOrdered.find(
        (c) => c.get("position") === category.get("position") + direction
      );
    }

    if (otherCategory) {
      // Try to swap positions of the two categories
      if (category.get("id") !== otherCategory.get("id")) {
        const currentPosition = category.get("position");
        category.set("position", otherCategory.get("position"));
        otherCategory.set("position", currentPosition);
      }
    } else if (direction < 0) {
      category.set("position", -1);
    } else if (direction > 0) {
      category.set("position", this.categoriesOrdered.length);
    }

    this.reorder();
  },

  actions: {
    change(category, event) {
      let newPosition = parseInt(event.target.value, 10);
      newPosition = Math.min(
        Math.max(newPosition, 0),
        this.categoriesOrdered.length - 1
      );

      this.move(category, newPosition - category.get("position"));
    },

    moveUp(category) {
      this.move(category, -1);
    },

    moveDown(category) {
      this.move(category, 1);
    },

    save() {
      this.reorder();

      const data = {};
      this.categoriesBuffered.forEach((cat) => {
        data[cat.get("id")] = cat.get("position");
      });

      ajax("/categories/reorder", {
        type: "POST",
        data: { mapping: JSON.stringify(data) },
      })
        .then(() => window.location.reload())
        .catch(popupAjaxError);
    },
  },
});
