import {
  CREATE_TOPIC,
  EDIT,
  PRIVATE_MESSAGE,
  REPLY,
} from "discourse/models/composer";
import { currentUser } from "discourse/tests/helpers/qunit-helpers";
import AppEvents from "discourse/services/app-events";
import { module, test } from "qunit";
import { getOwner } from "discourse-common/lib/get-owner";
import { setupTest } from "ember-qunit";

function createComposer(opts = {}) {
  opts.user ??= currentUser();
  opts.appEvents = AppEvents.create();
  const store = getOwner(this).lookup("service:store");
  return store.createRecord("composer", opts);
}

function openComposer(opts) {
  const composer = createComposer.call(this, opts);
  composer.open(opts);
  return composer;
}

module("Unit | Model | composer", function (hooks) {
  setupTest(hooks);

  hooks.beforeEach(function () {
    this.siteSettings = getOwner(this).lookup("service:site-settings");
  });

  test("replyLength", function (assert) {
    const replyLength = function (val, expectedLength) {
      const composer = createComposer.call(this, { reply: val });
      assert.strictEqual(composer.replyLength, expectedLength);
    };

    replyLength("basic reply", 11, "basic reply length");
    replyLength(" \nbasic reply\t", 11, "trims whitespaces");
    replyLength("ba sic\n\nreply", 12, "count only significant whitespaces");
    replyLength(
      "1[quote=]not counted[/quote]2[quote=]at all[/quote]3",
      3,
      "removes quotes"
    );
    replyLength(
      "1[quote=]not[quote=]counted[/quote]yay[/quote]2",
      2,
      "handles nested quotes correctly"
    );
    replyLength("<!-- a comment -->", 0, "remove comments");

    replyLength(
      "<!-- a comment -->\n more text \n<!-- a comment -->",
      9,
      "remove multiple comments"
    );

    replyLength(
      "<!-- <!-- a comment --> -->more text",
      12,
      "remove multiple comments"
    );
  });

  test("missingReplyCharacters", function (assert) {
    this.siteSettings.min_first_post_length = 40;

    function missingReplyCharacters(val, isPM, isFirstPost, expected, message) {
      let action;

      if (isFirstPost) {
        action = CREATE_TOPIC;
      } else if (isPM) {
        action = PRIVATE_MESSAGE;
      } else {
        action = REPLY;
      }

      const composer = createComposer.call(this, { reply: val, action });
      assert.strictEqual(composer.missingReplyCharacters, expected, message);
    }

    missingReplyCharacters(
      "hi",
      false,
      false,
      this.siteSettings.min_post_length - 2,
      "too short public post"
    );
    missingReplyCharacters(
      "hi",
      false,
      true,
      this.siteSettings.min_first_post_length - 2,
      "too short first post"
    );
    missingReplyCharacters(
      "hi",
      true,
      false,
      this.siteSettings.min_personal_message_post_length - 2,
      "too short private message"
    );

    const link = "http://imgur.com/gallery/grxX8";
    this.siteSettings.topic_featured_link_enabled = true;
    this.siteSettings.topic_featured_link_allowed_category_ids = 12345;
    const composer = createComposer.call(this, {
      title: link,
      categoryId: 12345,
      featuredLink: link,
      action: CREATE_TOPIC,
      reply: link,
    });

    assert.strictEqual(
      composer.missingReplyCharacters,
      0,
      "don't require any post content"
    );
  });

  test("missingTitleCharacters", function (assert) {
    const missingTitleCharacters = function (val, isPM, expected, message) {
      const composer = createComposer.call(this, {
        title: val,
        action: isPM ? PRIVATE_MESSAGE : REPLY,
      });
      assert.strictEqual(composer.missingTitleCharacters, expected, message);
    };

    missingTitleCharacters(
      "hi",
      false,
      this.siteSettings.min_topic_title_length - 2,
      "too short post title"
    );
    missingTitleCharacters(
      "z",
      true,
      this.siteSettings.min_personal_message_title_length - 1,
      "too short pm title"
    );
  });

  test("replyDirty", function (assert) {
    const composer = createComposer();
    assert.ok(!composer.replyDirty, "by default it's false");

    composer.setProperties({
      originalText: "hello",
      reply: "hello",
    });

    assert.ok(
      !composer.replyDirty,
      "it's false when the originalText is the same as the reply"
    );
    composer.set("reply", "hello world");
    assert.ok(composer.replyDirty, "it's true when the reply changes");
  });

  test("appendText", function (assert) {
    const composer = createComposer();

    assert.blank(composer.reply, "the reply is blank by default");

    composer.appendText("hello");
    assert.strictEqual(composer.reply, "hello", "it appends text to nothing");
    composer.appendText(" world");
    assert.strictEqual(
      composer.reply,
      "hello world",
      "it appends text to existing text"
    );

    composer.clearState();
    composer.appendText("a\n\n\n\nb");
    composer.appendText("c", 3, { block: true });

    assert.strictEqual(composer.reply, "a\n\nc\n\nb");

    composer.clearState();
    composer.appendText("ab");
    composer.appendText("c", 1, { block: true });

    assert.strictEqual(composer.reply, "a\n\nc\n\nb");

    composer.clearState();
    composer.appendText("\nab");
    composer.appendText("c", 0, { block: true });

    assert.strictEqual(composer.reply, "c\n\nab");
  });

  test("prependText", function (assert) {
    const composer = createComposer();

    assert.blank(composer.reply, "the reply is blank by default");

    composer.prependText("hello");
    assert.strictEqual(composer.reply, "hello", "it prepends text to nothing");

    composer.prependText("world ");
    assert.strictEqual(
      composer.reply,
      "world hello",
      "it prepends text to existing text"
    );

    composer.prependText("before new line", { new_line: true });
    assert.strictEqual(
      composer.reply,
      "before new line\n\nworld hello",
      "it prepends text with new line to existing text"
    );
  });

  test("Title length for regular topics", function (assert) {
    this.siteSettings.min_topic_title_length = 5;
    this.siteSettings.max_topic_title_length = 10;
    const composer = createComposer();

    composer.set("title", "asdf");
    assert.ok(!composer.titleLengthValid, "short titles are not valid");

    composer.set("title", "this is a long title");
    assert.ok(!composer.titleLengthValid, "long titles are not valid");

    composer.set("title", "just right");
    assert.ok(composer.titleLengthValid, "in the range is okay");
  });

  test("Title length for private messages", function (assert) {
    this.siteSettings.min_personal_message_title_length = 5;
    this.siteSettings.max_topic_title_length = 10;
    const composer = createComposer.call(this, { action: PRIVATE_MESSAGE });

    composer.set("title", "asdf");
    assert.ok(!composer.titleLengthValid, "short titles are not valid");

    composer.set("title", "this is a long title");
    assert.ok(!composer.titleLengthValid, "long titles are not valid");

    composer.set("title", "just right");
    assert.ok(composer.titleLengthValid, "in the range is okay");
  });

  test("Post length for private messages with non human users", function (assert) {
    const store = getOwner(this).lookup("service:store");
    const topic = store.createRecord("topic", { pm_with_non_human_user: true });
    const composer = createComposer.call(this, {
      topic,
    });

    assert.strictEqual(composer.minimumPostLength, 1);
  });

  test("editingFirstPost", function (assert) {
    const composer = createComposer();
    assert.ok(!composer.editingFirstPost, "it's false by default");

    const store = getOwner(this).lookup("service:store");
    const post = store.createRecord("post", { id: 123, post_number: 2 });
    composer.setProperties({ post, action: EDIT });
    assert.ok(
      !composer.editingFirstPost,
      "it's false when not editing the first post"
    );

    post.set("post_number", 1);
    assert.ok(
      composer.editingFirstPost,
      "it's true when editing the first post"
    );
  });

  test("clearState", function (assert) {
    const store = getOwner(this).lookup("service:store");
    const composer = createComposer.call(this, {
      originalText: "asdf",
      reply: "asdf2",
      post: store.createRecord("post", { id: 1 }),
      title: "wat",
    });

    composer.clearState();

    assert.blank(composer.originalText);
    assert.blank(composer.reply);
    assert.blank(composer.post);
    assert.blank(composer.title);
  });

  test("initial category when uncategorized is allowed", function (assert) {
    this.siteSettings.allow_uncategorized_topics = true;
    const composer = openComposer.call(this, {
      action: CREATE_TOPIC,
      draftKey: "abcd",
      draftSequence: 1,
    });
    assert.ok(!composer.categoryId, "Uncategorized by default");
  });

  test("initial category when uncategorized is not allowed", function (assert) {
    this.siteSettings.allow_uncategorized_topics = false;
    const composer = openComposer.call(this, {
      action: CREATE_TOPIC,
      draftKey: "abcd",
      draftSequence: 1,
    });
    assert.ok(
      !composer.categoryId,
      "Uncategorized by default. Must choose a category."
    );
  });

  test("open with a quote", function (assert) {
    const quote =
      '[quote="neil, post:5, topic:413"]\nSimmer down you two.\n[/quote]';
    const newComposer = function () {
      return openComposer.call(this, {
        action: REPLY,
        draftKey: "abcd",
        draftSequence: 1,
        quote,
      });
    };

    assert.strictEqual(
      newComposer().originalText,
      quote,
      "originalText is the quote"
    );
    assert.strictEqual(
      newComposer().replyDirty,
      false,
      "replyDirty is initially false with a quote"
    );
  });

  test("Title length for static page topics as admin", function (assert) {
    this.siteSettings.min_topic_title_length = 5;
    this.siteSettings.max_topic_title_length = 10;
    const composer = createComposer();

    const store = getOwner(this).lookup("service:store");
    const post = store.createRecord("post", {
      id: 123,
      post_number: 2,
      static_doc: true,
    });
    composer.setProperties({ post, action: EDIT });

    composer.set("title", "asdf");
    assert.ok(composer.titleLengthValid, "admins can use short titles");

    composer.set("title", "this is a long title");
    assert.ok(composer.titleLengthValid, "admins can use long titles");

    composer.set("title", "just right");
    assert.ok(composer.titleLengthValid, "in the range is okay");

    composer.set("title", "");
    assert.ok(
      !composer.titleLengthValid,
      "admins must set title to at least 1 character"
    );
  });

  test("title placeholder depends on what you're doing", function (assert) {
    this.siteSettings.topic_featured_link_enabled = false;
    let composer = createComposer.call(this, { action: CREATE_TOPIC });
    assert.strictEqual(
      composer.titlePlaceholder,
      "composer.title_placeholder",
      "placeholder for normal topic"
    );

    composer = createComposer.call(this, { action: PRIVATE_MESSAGE });
    assert.strictEqual(
      composer.titlePlaceholder,
      "composer.title_placeholder",
      "placeholder for private message"
    );

    this.siteSettings.topic_featured_link_enabled = true;

    composer = createComposer.call(this, { action: CREATE_TOPIC });
    assert.strictEqual(
      composer.titlePlaceholder,
      "composer.title_or_link_placeholder",
      "placeholder invites you to paste a link"
    );

    composer = createComposer.call(this, { action: PRIVATE_MESSAGE });
    assert.strictEqual(
      composer.titlePlaceholder,
      "composer.title_placeholder",
      "placeholder for private message with topic links enabled"
    );
  });

  test("allows featured link before choosing a category", function (assert) {
    this.siteSettings.topic_featured_link_enabled = true;
    this.siteSettings.allow_uncategorized_topics = false;
    const composer = createComposer.call(this, { action: CREATE_TOPIC });
    assert.strictEqual(
      composer.titlePlaceholder,
      "composer.title_or_link_placeholder",
      "placeholder invites you to paste a link"
    );
    assert.ok(composer.canEditTopicFeaturedLink, "can paste link");
  });

  test("targetRecipientsArray contains types", function (assert) {
    const composer = createComposer.call(this, {
      targetRecipients: "test,codinghorror,staff,foo@bar.com",
    });
    assert.deepEqual(composer.targetRecipientsArray, [
      { name: "test", type: "user" },
      { name: "codinghorror", type: "user" },
      { name: "staff", type: "group" },
      { name: "foo@bar.com", type: "email" },
    ]);
  });
});
