defmodule ToweropsWeb.AgentLive.IndexTest do
  use ToweropsWeb.ConnCase, async: true

  import Phoenix.LiveViewTest
  import Towerops.AccountsFixtures

  alias Towerops.Agents

  setup do
    user = user_fixture()
    {:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)

    %{user: user, organization: organization}
  end

  describe "mount" do
    test "loads agent tokens for organization", %{conn: conn, user: user, organization: organization} do
      # Create some agent tokens
      {:ok, agent_token1, _token1} = Agents.create_agent_token(organization.id, "Agent 1")
      {:ok, agent_token2, _token2} = Agents.create_agent_token(organization.id, "Agent 2")

      conn =
        conn
        |> log_in_user(user)
        |> get("/agents")

      {:ok, _view, html} = live(conn)

      assert html =~ "Remote Agents"
      assert html =~ agent_token1.name
      assert html =~ agent_token2.name
    end

    test "shows empty state when no agents exist", %{conn: conn, user: user, organization: _organization} do
      conn =
        conn
        |> log_in_user(user)
        |> get("/agents")

      {:ok, _view, html} = live(conn)

      assert html =~ "Remote Agents"
    end

    test "requires authentication", %{conn: conn} do
      conn = get(conn, "/agents")
      assert redirected_to(conn) == ~p"/users/log-in"
    end
  end

  describe "create_agent event" do
    test "creates new agent token and shows modal", %{conn: conn, user: user, organization: organization} do
      conn =
        conn
        |> log_in_user(user)
        |> get("/agents")

      {:ok, view, _html} = live(conn)

      # Trigger create_agent event with nested params
      html = render_submit(view, "create_agent", %{"agent_form" => %{"name" => "New Agent"}})

      assert html =~ "Agent created successfully"
      assert html =~ "New Agent"

      # Verify agent was actually created
      agent_tokens = Agents.list_organization_agent_tokens(organization.id)
      assert length(agent_tokens) == 1
      assert hd(agent_tokens).name == "New Agent"
    end

    test "creates agent with flat params format", %{conn: conn, user: user, organization: _organization} do
      conn =
        conn
        |> log_in_user(user)
        |> get("/agents")

      {:ok, view, _html} = live(conn)

      # Trigger create_agent event with flat params
      html = render_submit(view, "create_agent", %{"name" => "Flat Agent"})

      assert html =~ "Agent created successfully"
      assert html =~ "Flat Agent"
    end

    test "shows error on creation failure", %{conn: conn, user: user, organization: _organization} do
      conn =
        conn
        |> log_in_user(user)
        |> get("/agents")

      {:ok, view, _html} = live(conn)

      # Trigger create_agent with invalid params (empty name)
      html = render_submit(view, "create_agent", %{"agent_form" => %{"name" => ""}})

      assert html =~ "Failed to create agent"
    end
  end

  describe "close_token_modal event" do
    test "closes the token modal", %{conn: conn, user: user, organization: _organization} do
      conn =
        conn
        |> log_in_user(user)
        |> get("/agents")

      {:ok, view, _html} = live(conn)

      # First create an agent to show the modal
      render_submit(view, "create_agent", %{"name" => "Test Agent"})

      # Now close the modal
      _html = render_click(view, "close_token_modal", %{})

      # Modal should be closed - verify by checking that we can still interact with the view
      assert render(view) =~ "Test Agent"
    end
  end

  describe "delete_agent event" do
    test "deletes agent token successfully", %{conn: conn, user: user, organization: organization} do
      {:ok, agent_token, _token} = Agents.create_agent_token(organization.id, "Agent to Delete")

      conn =
        conn
        |> log_in_user(user)
        |> get("/agents")

      {:ok, view, _html} = live(conn)

      html = render_click(view, "delete_agent", %{"id" => agent_token.id})

      assert html =~ "Agent deleted successfully"

      # Verify agent was deleted (no longer in list)
      agent_tokens = Agents.list_organization_agent_tokens(organization.id)
      assert Enum.empty?(agent_tokens)
    end
  end

  describe "agent status display" do
    test "shows Never connected for agents without last_seen_at", %{
      conn: conn,
      user: user,
      organization: organization
    } do
      {:ok, agent_token, _token} = Agents.create_agent_token(organization.id, "Never Seen")

      # Verify the agent has no last_seen_at
      assert is_nil(agent_token.last_seen_at)

      conn =
        conn
        |> log_in_user(user)
        |> get("/agents")

      {:ok, _view, html} = live(conn)

      assert html =~ "Never Seen"
      assert html =~ "Never connected" or html =~ "Never"
    end

    test "shows agent status information in the page", %{
      conn: conn,
      user: user,
      organization: organization
    } do
      {:ok, agent_token, _token} = Agents.create_agent_token(organization.id, "Status Agent")

      # Update the agent to have a recent last_seen_at
      agent_token
      |> Ecto.Changeset.change(%{last_seen_at: DateTime.truncate(DateTime.utc_now(), :second)})
      |> Towerops.Repo.update!()

      conn =
        conn
        |> log_in_user(user)
        |> get("/agents")

      {:ok, _view, html} = live(conn)

      assert html =~ "Status Agent"
      # The status should show something about timing (Online, seconds ago, etc.)
      assert html =~ "ago" or html =~ "Online"
    end
  end

  describe "handle_params" do
    test "handles :index action", %{conn: conn, user: user, organization: _organization} do
      conn =
        conn
        |> log_in_user(user)
        |> get("/agents")

      {:ok, view, _html} = live(conn)

      # Verify we can navigate to the index action
      assert render(view) =~ "Remote Agents"
    end
  end

  describe "cloud poller creation - superadmin only" do
    setup %{organization: organization} do
      # Create a superadmin user
      superadmin = user_fixture()

      superadmin =
        superadmin
        |> Ecto.Changeset.change(%{is_superuser: true})
        |> Towerops.Repo.update!()

      # Add superadmin to the organization
      {:ok, _membership} =
        Towerops.Organizations.create_membership(%{
          organization_id: organization.id,
          user_id: superadmin.id,
          role: "admin"
        })

      %{superadmin: superadmin}
    end

    test "superadmin can create cloud poller", %{
      conn: conn,
      superadmin: superadmin,
      organization: _organization
    } do
      conn =
        conn
        |> log_in_user(superadmin)
        |> get("/agents")

      {:ok, view, html} = live(conn)

      # Superadmin should see the cloud poller checkbox
      assert html =~ "Cloud Poller"

      # Create a cloud poller
      html =
        render_submit(view, "create_agent", %{
          "agent_form" => %{"name" => "Test Cloud Poller", "is_cloud_poller" => "true"}
        })

      assert html =~ "Cloud poller created successfully"
      assert html =~ "Test Cloud Poller"

      # Verify cloud poller was created
      cloud_pollers = Agents.list_cloud_pollers()
      assert length(cloud_pollers) == 1
      assert hd(cloud_pollers).name == "Test Cloud Poller"
      assert hd(cloud_pollers).is_cloud_poller == true
      assert hd(cloud_pollers).organization_id == nil
    end

    test "non-superadmin cannot create cloud poller", %{
      conn: conn,
      user: user,
      organization: organization
    } do
      conn =
        conn
        |> log_in_user(user)
        |> get("/agents")

      {:ok, view, html} = live(conn)

      # Regular user should NOT see the cloud poller checkbox
      refute html =~ "Cloud Poller (Application-wide agent"

      # Attempt to create a cloud poller (bypassing UI)
      html =
        render_submit(view, "create_agent", %{
          "agent_form" => %{"name" => "Unauthorized Cloud Poller", "is_cloud_poller" => "true"}
        })

      assert html =~ "Only superadmins can create cloud pollers"

      # Verify cloud poller was NOT created
      cloud_pollers = Agents.list_cloud_pollers()
      assert Enum.empty?(cloud_pollers)

      # Verify organization agent was NOT created
      org_agents = Agents.list_organization_agent_tokens(organization.id)
      assert Enum.empty?(org_agents)
    end

    test "superadmin can create regular agent by not checking cloud poller", %{
      conn: conn,
      superadmin: superadmin,
      organization: organization
    } do
      conn =
        conn
        |> log_in_user(superadmin)
        |> get("/agents")

      {:ok, view, _html} = live(conn)

      # Create a regular agent (is_cloud_poller = false)
      html =
        render_submit(view, "create_agent", %{
          "agent_form" => %{"name" => "Regular Agent", "is_cloud_poller" => "false"}
        })

      assert html =~ "Agent created successfully"
      assert html =~ "Regular Agent"

      # Verify regular agent was created
      org_agents = Agents.list_organization_agent_tokens(organization.id)
      assert length(org_agents) == 1
      assert hd(org_agents).name == "Regular Agent"
      assert hd(org_agents).is_cloud_poller == false
      assert hd(org_agents).organization_id == organization.id

      # Verify cloud poller was NOT created
      cloud_pollers = Agents.list_cloud_pollers()
      assert Enum.empty?(cloud_pollers)
    end
  end

  describe "cloud poller visibility" do
    setup %{organization: organization} do
      # Create a superadmin user
      superadmin = user_fixture()

      superadmin =
        superadmin
        |> Ecto.Changeset.change(%{is_superuser: true})
        |> Towerops.Repo.update!()

      # Add superadmin to the organization
      {:ok, _membership} =
        Towerops.Organizations.create_membership(%{
          organization_id: organization.id,
          user_id: superadmin.id,
          role: "admin"
        })

      # Create some cloud pollers
      {:ok, cloud1, _} = Agents.create_cloud_poller("Cloud Poller 1")
      {:ok, cloud2, _} = Agents.create_cloud_poller("Cloud Poller 2")

      # Create organization agents
      {:ok, org_agent, _} = Agents.create_agent_token(organization.id, "Org Agent")

      %{superadmin: superadmin, cloud1: cloud1, cloud2: cloud2, org_agent: org_agent}
    end

    test "superadmin sees cloud pollers in separate section", %{
      conn: conn,
      superadmin: superadmin,
      organization: _organization,
      cloud1: cloud1,
      cloud2: cloud2,
      org_agent: org_agent
    } do
      conn =
        conn
        |> log_in_user(superadmin)
        |> get("/agents")

      {:ok, _view, html} = live(conn)

      # Should see cloud pollers section header
      assert html =~ "Cloud Pollers"

      # Should see both cloud pollers
      assert html =~ cloud1.name
      assert html =~ cloud2.name

      # Should see organization agent in main section
      assert html =~ org_agent.name

      # Should see visual distinction for cloud pollers
      assert html =~ "Cloud Poller" or html =~ "cloud-poller"
    end

    test "non-superadmin does not see cloud pollers section", %{
      conn: conn,
      user: user,
      organization: _organization,
      cloud1: cloud1,
      cloud2: cloud2,
      org_agent: org_agent
    } do
      conn =
        conn
        |> log_in_user(user)
        |> get("/agents")

      {:ok, _view, html} = live(conn)

      # Should NOT see cloud pollers section header
      refute html =~ "Cloud Pollers"

      # Should NOT see cloud poller names
      refute html =~ cloud1.name
      refute html =~ cloud2.name

      # Should see organization agent in main section
      assert html =~ org_agent.name
    end
  end

  describe "cloud poller management" do
    setup %{organization: organization} do
      # Create a superadmin user
      superadmin = user_fixture()

      superadmin =
        superadmin
        |> Ecto.Changeset.change(%{is_superuser: true})
        |> Towerops.Repo.update!()

      # Add superadmin to the organization
      {:ok, _membership} =
        Towerops.Organizations.create_membership(%{
          organization_id: organization.id,
          user_id: superadmin.id,
          role: "admin"
        })

      {:ok, cloud_poller, _} = Agents.create_cloud_poller("Test Cloud Poller")

      %{superadmin: superadmin, cloud_poller: cloud_poller}
    end

    test "superadmin can delete cloud poller", %{
      conn: conn,
      superadmin: superadmin,
      organization: _organization,
      cloud_poller: cloud_poller
    } do
      conn =
        conn
        |> log_in_user(superadmin)
        |> get("/agents")

      {:ok, view, _html} = live(conn)

      html = render_click(view, "delete_agent", %{"id" => cloud_poller.id})

      assert html =~ "Agent deleted successfully"

      # Verify cloud poller was deleted
      cloud_pollers = Agents.list_cloud_pollers()
      assert Enum.empty?(cloud_pollers)
    end

    test "superadmin can regenerate cloud poller token", %{
      conn: conn,
      superadmin: superadmin,
      organization: _organization,
      cloud_poller: cloud_poller
    } do
      conn =
        conn
        |> log_in_user(superadmin)
        |> get("/agents")

      {:ok, view, _html} = live(conn)

      html = render_click(view, "regenerate_token", %{"id" => cloud_poller.id})

      assert html =~ "New token generated successfully"
      assert html =~ "Test Cloud Poller"
    end

    test "superadmin can view cloud poller setup instructions", %{
      conn: conn,
      superadmin: superadmin,
      organization: _organization,
      cloud_poller: cloud_poller
    } do
      conn =
        conn
        |> log_in_user(superadmin)
        |> get("/agents")

      {:ok, view, _html} = live(conn)

      html = render_click(view, "show_setup", %{"id" => cloud_poller.id})

      assert html =~ "Test Cloud Poller"
      # Setup modal should show token and instructions
      assert html =~ cloud_poller.token or html =~ "docker run" or html =~ "Setup"
    end
  end

  describe "global default cloud poller management" do
    setup %{organization: organization} do
      # Create a superadmin user
      superadmin = user_fixture()

      superadmin =
        superadmin
        |> Ecto.Changeset.change(%{is_superuser: true})
        |> Towerops.Repo.update!()

      # Add superadmin to the organization
      {:ok, _membership} =
        Towerops.Organizations.create_membership(%{
          organization_id: organization.id,
          user_id: superadmin.id,
          role: "admin"
        })

      {:ok, cloud_poller, _} = Agents.create_cloud_poller("Test Cloud Poller")

      %{superadmin: superadmin, cloud_poller: cloud_poller}
    end

    test "superadmin can set global default cloud poller with validation", %{
      conn: conn,
      superadmin: superadmin,
      organization: _organization,
      cloud_poller: cloud_poller
    } do
      conn =
        conn
        |> log_in_user(superadmin)
        |> get("/agents")

      {:ok, view, html} = live(conn)

      # Should see global default section
      assert html =~ "Global Default Cloud Poller"

      # Update dropdown selection (doesn't save yet)
      render_change(view, "update_selected_global_default", %{
        "agent_token_id" => cloud_poller.id
      })

      # Click save button
      html = render_click(view, "save_global_default", %{})

      assert html =~ "Global default cloud poller set successfully"

      # Verify it was saved
      assert Towerops.Settings.get_global_default_cloud_poller() == cloud_poller.id
    end

    test "superadmin cannot save non-existent agent as global default", %{
      conn: conn,
      superadmin: superadmin,
      organization: _organization
    } do
      conn =
        conn
        |> log_in_user(superadmin)
        |> get("/agents")

      {:ok, view, _html} = live(conn)

      fake_id = Ecto.UUID.generate()

      # Update dropdown to fake ID
      render_change(view, "update_selected_global_default", %{"agent_token_id" => fake_id})

      # Attempt to save
      html = render_click(view, "save_global_default", %{})

      assert html =~ "Selected agent no longer exists"

      # Verify it was NOT saved
      refute Towerops.Settings.get_global_default_cloud_poller() == fake_id
    end

    test "superadmin can clear global default cloud poller", %{
      conn: conn,
      superadmin: superadmin,
      organization: _organization,
      cloud_poller: cloud_poller
    } do
      # First set a global default
      Towerops.Settings.set_global_default_cloud_poller(cloud_poller.id)

      conn =
        conn
        |> log_in_user(superadmin)
        |> get("/agents")

      {:ok, view, html} = live(conn)

      # Should show current default
      assert html =~ "Currently using"

      # Select "No global default"
      render_change(view, "update_selected_global_default", %{"agent_token_id" => ""})

      # Save
      html = render_click(view, "save_global_default", %{})

      assert html =~ "Global default cloud poller cleared"

      # Verify it was cleared
      assert Towerops.Settings.get_global_default_cloud_poller() == nil
    end

    test "deleting cloud poller clears it from global default", %{
      conn: conn,
      superadmin: superadmin,
      organization: _organization,
      cloud_poller: cloud_poller
    } do
      # Set as global default
      Towerops.Settings.set_global_default_cloud_poller(cloud_poller.id)

      conn =
        conn
        |> log_in_user(superadmin)
        |> get("/agents")

      {:ok, view, _html} = live(conn)

      # Delete the cloud poller
      html = render_click(view, "delete_agent", %{"id" => cloud_poller.id})

      assert html =~ "Agent deleted successfully"

      # Verify global default was cleared (CASCADE)
      assert Towerops.Settings.get_global_default_cloud_poller() == nil

      # Verify the UI reflects this
      assert html =~ "No global default" or html =~ "direct Phoenix"
    end

    test "non-superadmin cannot see global default section", %{
      conn: conn,
      user: user,
      organization: _organization
    } do
      conn =
        conn
        |> log_in_user(user)
        |> get("/agents")

      {:ok, _view, html} = live(conn)

      # Should NOT see global default section
      refute html =~ "Global Default Cloud Poller"
    end
  end
end
