defmodule Mix.Tasks.PopulateEnglishTest do
  use ExUnit.Case, async: false

  import ExUnit.CaptureIO
  import Mix.Tasks.PopulateEnglish, only: [run: 1]

  setup do
    # Create temporary directory for test files
    temp_dir = Path.join(System.tmp_dir!(), "populate_english_test_#{System.unique_integer([:positive])}")
    po_dir = Path.join([temp_dir, "priv", "gettext", "en", "LC_MESSAGES"])
    File.mkdir_p!(po_dir)

    on_exit(fn ->
      File.rm_rf!(temp_dir)
    end)

    {:ok, temp_dir: temp_dir, po_dir: po_dir}
  end

  describe "run/1" do
    test "populates empty msgstr with msgid", %{po_dir: po_dir} do
      po_file = Path.join(po_dir, "test.po")

      File.write!(po_file, """
      msgid "Hello"
      msgstr ""

      msgid "Goodbye"
      msgstr ""
      """)

      # Change to temp directory so the task finds the files
      File.cd!(Path.dirname(Path.dirname(Path.dirname(Path.dirname(po_dir)))), fn ->
        capture_io(fn -> run([]) end)
      end)

      content = File.read!(po_file)
      assert content =~ ~s(msgid "Hello"\nmsgstr "Hello")
      assert content =~ ~s(msgid "Goodbye"\nmsgstr "Goodbye")
    end

    test "preserves existing non-empty msgstr", %{po_dir: po_dir} do
      po_file = Path.join(po_dir, "test.po")

      File.write!(po_file, """
      msgid "Hello"
      msgstr "Hola"

      msgid "Goodbye"
      msgstr ""
      """)

      File.cd!(Path.dirname(Path.dirname(Path.dirname(Path.dirname(po_dir)))), fn ->
        capture_io(fn -> run([]) end)
      end)

      content = File.read!(po_file)
      assert content =~ ~s(msgid "Hello"\nmsgstr "Hola")
      assert content =~ ~s(msgid "Goodbye"\nmsgstr "Goodbye")
    end

    test "handles multiline msgid and msgstr", %{po_dir: po_dir} do
      po_file = Path.join(po_dir, "test.po")

      File.write!(po_file, """
      msgid ""
      "This is a "
      "multiline string"
      msgstr ""
      """)

      File.cd!(Path.dirname(Path.dirname(Path.dirname(Path.dirname(po_dir)))), fn ->
        capture_io(fn -> run([]) end)
      end)

      content = File.read!(po_file)
      assert content =~ ~s(msgstr ""\n"This is a "\n"multiline string")
    end

    test "preserves comments and metadata", %{po_dir: po_dir} do
      po_file = Path.join(po_dir, "test.po")

      File.write!(po_file, """
      # Comment line
      ## Another comment

      msgid "Hello"
      msgstr ""
      """)

      File.cd!(Path.dirname(Path.dirname(Path.dirname(Path.dirname(po_dir)))), fn ->
        capture_io(fn -> run([]) end)
      end)

      content = File.read!(po_file)
      assert content =~ "# Comment line"
      assert content =~ "## Another comment"
      assert content =~ ~s(msgid "Hello"\nmsgstr "Hello")
    end

    test "skips plural forms", %{po_dir: po_dir} do
      po_file = Path.join(po_dir, "test.po")

      File.write!(po_file, """
      msgid "One item"
      msgid_plural "%{count} items"
      msgstr[0] ""
      msgstr[1] ""

      msgid "Regular message"
      msgstr ""
      """)

      File.cd!(Path.dirname(Path.dirname(Path.dirname(Path.dirname(po_dir)))), fn ->
        capture_io(fn -> run([]) end)
      end)

      content = File.read!(po_file)
      # Plural forms should not be modified
      assert content =~ ~s(msgstr[0] "")
      assert content =~ ~s(msgstr[1] "")
      # Regular message should be populated
      assert content =~ ~s(msgid "Regular message"\nmsgstr "Regular message")
    end

    test "handles empty msgid", %{po_dir: po_dir} do
      po_file = Path.join(po_dir, "test.po")

      File.write!(po_file, """
      msgid ""
      msgstr ""

      msgid "Hello"
      msgstr ""
      """)

      File.cd!(Path.dirname(Path.dirname(Path.dirname(Path.dirname(po_dir)))), fn ->
        capture_io(fn -> run([]) end)
      end)

      content = File.read!(po_file)
      # Empty msgid should not be populated
      lines = String.split(content, "\n")
      empty_msgid_index = Enum.find_index(lines, &(&1 == ~s(msgid "")))
      assert Enum.at(lines, empty_msgid_index + 1) == ~s(msgstr "")
      # Non-empty msgid should be populated
      assert content =~ ~s(msgid "Hello"\nmsgstr "Hello")
    end

    test "handles escaped characters in strings", %{po_dir: po_dir} do
      po_file = Path.join(po_dir, "test.po")

      File.write!(po_file, """
      msgid "Line one\\nLine two"
      msgstr ""

      msgid "Tab\\there"
      msgstr ""

      msgid "Quote \\"here\\""
      msgstr ""
      """)

      File.cd!(Path.dirname(Path.dirname(Path.dirname(Path.dirname(po_dir)))), fn ->
        capture_io(fn -> run([]) end)
      end)

      content = File.read!(po_file)
      assert content =~ ~s(msgid "Line one\\nLine two"\nmsgstr "Line one\\nLine two")
      assert content =~ ~s(msgid "Tab\\there"\nmsgstr "Tab\\there")
      assert content =~ ~s(msgid "Quote \\"here\\""\nmsgstr "Quote \\"here\\"")
    end

    test "processes multiple .po files", %{po_dir: po_dir} do
      po_file1 = Path.join(po_dir, "test1.po")
      po_file2 = Path.join(po_dir, "test2.po")

      File.write!(po_file1, """
      msgid "Hello"
      msgstr ""
      """)

      File.write!(po_file2, """
      msgid "Goodbye"
      msgstr ""
      """)

      File.cd!(Path.dirname(Path.dirname(Path.dirname(Path.dirname(po_dir)))), fn ->
        output = capture_io(fn -> run([]) end)
        assert output =~ "2 English translation file(s)"
      end)

      assert File.read!(po_file1) =~ ~s(msgid "Hello"\nmsgstr "Hello")
      assert File.read!(po_file2) =~ ~s(msgid "Goodbye"\nmsgstr "Goodbye")
    end

    test "reports no changes when file already populated", %{po_dir: po_dir} do
      po_file = Path.join(po_dir, "test.po")

      File.write!(po_file, """
      msgid "Hello"
      msgstr "Hello"
      """)

      File.cd!(Path.dirname(Path.dirname(Path.dirname(Path.dirname(po_dir)))), fn ->
        output = capture_io(fn -> run([]) end)
        assert output =~ "No changes: test.po"
      end)
    end

    test "reports updated when file is modified", %{po_dir: po_dir} do
      po_file = Path.join(po_dir, "test.po")

      File.write!(po_file, """
      msgid "Hello"
      msgstr ""
      """)

      File.cd!(Path.dirname(Path.dirname(Path.dirname(Path.dirname(po_dir)))), fn ->
        output = capture_io(fn -> run([]) end)
        assert output =~ "Updated: test.po"
      end)
    end

    test "handles missing directory gracefully" do
      temp_dir = Path.join(System.tmp_dir!(), "nonexistent_#{System.unique_integer([:positive])}")
      File.mkdir_p!(temp_dir)

      try do
        File.cd!(temp_dir, fn ->
          assert_raise Mix.Error, fn ->
            capture_io(fn -> run([]) end)
          end
        end)
      after
        File.rm_rf!(temp_dir)
      end
    end

    test "reports when no .po files found", %{po_dir: po_dir} do
      File.cd!(Path.dirname(Path.dirname(Path.dirname(Path.dirname(po_dir)))), fn ->
        output = capture_io(fn -> run([]) end)
        assert output =~ "No .po files found"
      end)
    end
  end
end
