defmodule BDS.UI.SidebarTest do
  use ExUnit.Case, async: false

  import Ecto.Query

  alias BDS.AI
  alias BDS.ImportDefinitions
  alias BDS.Media
  alias BDS.Posts
  alias BDS.Posts.Post
  alias BDS.Projects
  alias BDS.Repo
  alias BDS.Scripts
  alias BDS.Templates
  alias BDS.UI.Sidebar

  setup do
    :ok = Ecto.Adapters.SQL.Sandbox.checkout(BDS.Repo)

    temp_dir = Path.join(System.tmp_dir!(), "bds-sidebar-#{System.unique_integer([:positive])}")
    File.mkdir_p!(temp_dir)

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

    {:ok, project} = Projects.create_project(%{name: "Sidebar", data_path: temp_dir})
    %{project: project, temp_dir: temp_dir}
  end

  test "database-backed sidebar views follow the old app ordering keys", %{
    project: project,
    temp_dir: temp_dir
  } do
    assert {:ok, draft_old} = Posts.create_post(%{project_id: project.id, title: "Draft Old"})
    assert {:ok, draft_new} = Posts.create_post(%{project_id: project.id, title: "Draft New"})

    assert {:ok, published_old} =
             Posts.create_post(%{project_id: project.id, title: "Published Old"})

    assert {:ok, published_new} =
             Posts.create_post(%{project_id: project.id, title: "Published New"})

    assert {:ok, archived_old} =
             Posts.create_post(%{project_id: project.id, title: "Archived Old"})

    assert {:ok, archived_new} =
             Posts.create_post(%{project_id: project.id, title: "Archived New"})

    update_post_sidebar_row(draft_old.id, created_at: 1_000, updated_at: 9_000)
    update_post_sidebar_row(draft_new.id, created_at: 2_000, updated_at: 1_000)

    update_post_sidebar_row(
      published_old.id,
      status: :published,
      created_at: 3_000,
      updated_at: 9_000,
      published_at: 3_000,
      file_path: "posts/published-old.md"
    )

    update_post_sidebar_row(
      published_new.id,
      status: :published,
      created_at: 4_000,
      updated_at: 1_000,
      published_at: 4_000,
      file_path: "posts/published-new.md"
    )

    update_post_sidebar_row(archived_old.id,
      status: :archived,
      created_at: 5_000,
      updated_at: 9_000
    )

    update_post_sidebar_row(archived_new.id,
      status: :archived,
      created_at: 6_000,
      updated_at: 1_000
    )

    old_media_path = Path.join(temp_dir, "old-media.txt")
    new_media_path = Path.join(temp_dir, "new-media.txt")
    File.write!(old_media_path, "old media")
    File.write!(new_media_path, "new media")

    assert {:ok, old_media} =
             Media.import_media(%{
               project_id: project.id,
               source_path: old_media_path,
               title: "Old Media"
             })

    assert {:ok, new_media} =
             Media.import_media(%{
               project_id: project.id,
               source_path: new_media_path,
               title: "New Media"
             })

    update_media_sidebar_row(old_media.id, created_at: 7_000, updated_at: 9_000)
    update_media_sidebar_row(new_media.id, created_at: 8_000, updated_at: 1_000)

    assert {:ok, old_script} =
             Scripts.create_script(%{
               project_id: project.id,
               title: "Old Script",
               kind: :utility,
               content: "print('old')",
               entrypoint: "main"
             })

    assert {:ok, new_script} =
             Scripts.create_script(%{
               project_id: project.id,
               title: "New Script",
               kind: :utility,
               content: "print('new')",
               entrypoint: "main"
             })

    update_script_sidebar_row(old_script.id, 9_000)
    update_script_sidebar_row(new_script.id, 10_000)

    assert {:ok, old_template} =
             Templates.create_template(%{
               project_id: project.id,
               title: "Old Template",
               kind: :post,
               content: "old"
             })

    assert {:ok, new_template} =
             Templates.create_template(%{
               project_id: project.id,
               title: "New Template",
               kind: :post,
               content: "new"
             })

    update_template_sidebar_row(old_template.id, 11_000)
    update_template_sidebar_row(new_template.id, 12_000)

    assert {:ok, old_chat} = AI.start_chat(%{title: "Old Chat"})
    assert {:ok, new_chat} = AI.start_chat(%{title: "New Chat"})
    update_chat_sidebar_row(old_chat.id, 13_000)
    update_chat_sidebar_row(new_chat.id, 14_000)

    assert {:ok, old_definition} =
             ImportDefinitions.create_definition(%{project_id: project.id, name: "Old Import"})

    assert {:ok, new_definition} =
             ImportDefinitions.create_definition(%{project_id: project.id, name: "New Import"})

    update_import_sidebar_row(old_definition.id, 15_000)
    update_import_sidebar_row(new_definition.id, 16_000)

    posts_view = Sidebar.view(project.id, "posts")
    assert titles_in_section(posts_view, "Drafts") == ["Draft New", "Draft Old"]
    assert titles_in_section(posts_view, "Published") == ["Published New", "Published Old"]
    assert titles_in_section(posts_view, "Archived") == ["Archived New", "Archived Old"]

    media_view = Sidebar.view(project.id, "media")
    assert Enum.map(media_view.items, & &1.title) == ["New Media", "Old Media"]

    scripts_view = Sidebar.view(project.id, "scripts")
    assert Enum.map(scripts_view.items, & &1.title) == ["New Script", "Old Script"]

    templates_view = Sidebar.view(project.id, "templates")
    assert Enum.map(templates_view.items, & &1.title) == ["New Template", "Old Template"]

    chat_view = Sidebar.view(project.id, "chat")
    assert Enum.map(chat_view.items, & &1.title) == ["New Chat", "Old Chat"]

    import_view = Sidebar.view(project.id, "import")
    assert Enum.map(import_view.items, & &1.title) == ["New Import", "Old Import"]
  end

  defp titles_in_section(view, title) do
    view.sections
    |> Enum.find(&(&1.title == title))
    |> Map.fetch!(:items)
    |> Enum.map(& &1.title)
  end

  defp update_post_sidebar_row(post_id, updates) do
    Repo.update_all(from(post in Post, where: post.id == ^post_id), set: updates)
  end

  defp update_media_sidebar_row(media_id, updates) do
    Repo.update_all(from(media in BDS.Media.Media, where: media.id == ^media_id), set: updates)
  end

  defp update_script_sidebar_row(script_id, updated_at) do
    Repo.update_all(from(script in BDS.Scripts.Script, where: script.id == ^script_id),
      set: [updated_at: updated_at]
    )
  end

  defp update_template_sidebar_row(template_id, updated_at) do
    Repo.update_all(from(template in BDS.Templates.Template, where: template.id == ^template_id),
      set: [updated_at: updated_at]
    )
  end

  defp update_chat_sidebar_row(conversation_id, updated_at) do
    Repo.update_all(
      from(conversation in BDS.AI.ChatConversation, where: conversation.id == ^conversation_id),
      set: [updated_at: updated_at]
    )
  end

  defp update_import_sidebar_row(definition_id, updated_at) do
    Repo.update_all(
      from(definition in BDS.ImportDefinitions.ImportDefinition,
        where: definition.id == ^definition_id
      ),
      set: [updated_at: updated_at]
    )
  end
end
