defmodule ToweropsWeb.DeviceLiveTest do
  use ToweropsWeb.ConnCase

  import Mox
  import Phoenix.LiveViewTest

  alias Towerops.Snmp.Device
  alias Towerops.Snmp.SnmpMock

  setup :register_and_log_in_user
  setup :verify_on_exit!

  # Set up SNMP mock globally to allow background tasks
  setup :set_mox_global

  setup do
    # Stub SNMP mock globally to allow background task calls
    Mox.stub(SnmpMock, :get, fn _target, _oid, _opts -> {:error, :timeout} end)
    Mox.stub(SnmpMock, :walk, fn _target, _oid, _opts -> {:error, :timeout} end)
    Mox.stub(SnmpMock, :get_bulk, fn _target, _oid, _opts -> {:error, :timeout} end)

    :ok
  end

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

    {:ok, site} =
      Towerops.Sites.create_site(%{
        name: "Test Site",
        organization_id: organization.id
      })

    %{organization: organization, site: site}
  end

  describe "Index" do
    test "lists all devices", %{conn: conn, organization: organization, site: site} do
      {:ok, device} =
        Towerops.Devices.create_device(%{
          name: "Router 1",
          ip_address: "192.168.1.1",
          description: "Main router",
          site_id: site.id
        })

      {:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/devices")

      assert html =~ "Device"
      assert html =~ device.name
      assert html =~ "192.168.1.1"
    end

    test "displays empty state when no device", %{conn: conn, organization: organization} do
      {:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/devices")

      assert html =~ "No devices"
    end

    test "has link to add new device", %{conn: conn, organization: organization} do
      {:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/devices")

      assert html =~ "New Device"
    end

    test "requires authentication", %{organization: organization} do
      conn = build_conn()
      {:error, redirect} = live(conn, ~p"/orgs/#{organization.slug}/devices")

      assert {:redirect, %{to: path}} = redirect
      assert path == ~p"/users/log-in"
    end
  end

  describe "Show" do
    setup %{site: site} do
      {:ok, device} =
        Towerops.Devices.create_device(%{
          name: "Router 1",
          ip_address: "192.168.1.1",
          description: "Main router",
          site_id: site.id,
          monitoring_enabled: true,
          check_interval_seconds: 300
        })

      %{device: device}
    end

    test "displays device details", %{
      conn: conn,
      organization: organization,
      device: device
    } do
      {:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/devices/#{device.id}")

      assert html =~ device.name
      assert html =~ "192.168.1.1"
      assert html =~ "Device Information"
    end

    test "displays device with tab parameter", %{
      conn: conn,
      organization: organization,
      device: device
    } do
      {:ok, _view, html} =
        live(conn, ~p"/orgs/#{organization.slug}/devices/#{device.id}?tab=interfaces")

      assert html =~ device.name
      assert html =~ "192.168.1.1"
    end

    test "shows device status when monitoring enabled", %{
      conn: conn,
      organization: organization,
      device: device
    } do
      # Create a monitoring check
      {:ok, _check} =
        Towerops.Monitoring.create_check(%{
          device_id: device.id,
          status: :success,
          response_time_ms: 25,
          checked_at: DateTime.utc_now()
        })

      {:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/devices/#{device.id}")

      # Should display monitoring information
      assert html =~ device.name
      assert html =~ "192.168.1.1"
    end

    test "shows SNMP device information when available", %{
      conn: conn,
      organization: organization,
      device: device
    } do
      # Create SNMP device record using Repo
      _device =
        %Device{}
        |> Device.changeset(%{
          device_id: device.id,
          sys_descr: "Test Device",
          sys_name: "test-device",
          manufacturer: "Test Manufacturer",
          model: "Model 1"
        })
        |> Towerops.Repo.insert!()

      {:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/devices/#{device.id}")

      # Verify the page loads successfully with equipment data
      assert html =~ device.name
      assert html =~ "192.168.1.1"
    end

    test "displays recent events", %{
      conn: conn,
      organization: organization,
      device: device
    } do
      # Create an event with all required fields (use valid event_type)
      {:ok, _event} =
        Towerops.Devices.create_event(%{
          device_id: device.id,
          event_type: "device_discovered",
          severity: "info",
          message: "Device was discovered",
          details: %{manufacturer: "Test", model: "Model 1"},
          occurred_at: DateTime.utc_now()
        })

      {:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/devices/#{device.id}")

      # Verify page loads with events section
      assert html =~ device.name
    end

    test "handles missing device gracefully", %{conn: conn, organization: organization} do
      fake_id = Ecto.UUID.generate()

      assert_raise Ecto.NoResultsError, fn ->
        live(conn, ~p"/orgs/#{organization.slug}/devices/#{fake_id}")
      end
    end

    test "deletes device", %{conn: conn, organization: organization, device: device} do
      {:ok, view, _html} =
        live(conn, ~p"/orgs/#{organization.slug}/devices/#{device.id}/edit")

      {:ok, _, html} =
        view
        |> element("button", "Delete Device")
        |> render_click()
        |> follow_redirect(conn, ~p"/orgs/#{organization.slug}/devices")

      assert html =~ "Device deleted successfully"
    end

    test "requires authentication", %{organization: organization, device: device} do
      conn = build_conn()
      {:error, redirect} = live(conn, ~p"/orgs/#{organization.slug}/devices/#{device.id}")

      assert {:redirect, %{to: path}} = redirect
      assert path == ~p"/users/log-in"
    end
  end

  describe "New" do
    test "renders new device form", %{conn: conn, organization: organization} do
      {:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/devices/new")

      assert html =~ "New Device"
      assert html =~ "Add new device to monitor"
    end

    test "creates new device and stays on add page with success message", %{
      conn: conn,
      organization: organization,
      site: site
    } do
      {:ok, view, _html} = live(conn, ~p"/orgs/#{organization.slug}/devices/new")

      # Default is SNMP & ICMP mode, so snmp fields should be visible
      html =
        view
        |> form("#device-form",
          device: %{
            name: "New Router",
            ip_address: "192.168.1.100",
            site_id: site.id,
            description: "Test router",
            monitoring_enabled: true,
            check_interval_seconds: 300,
            snmp_version: "2c",
            snmp_community: "public",
            snmp_port: 161
          }
        )
        |> render_submit()

      # Verify we stayed on the add page (no redirect)
      assert html =~ "New Device"
      assert html =~ "Add new device to monitor"

      # Verify success message is shown
      assert html =~ "Device created successfully"

      # Verify device was created
      device_list = Towerops.Devices.list_organization_devices(organization.id)
      refute Enum.empty?(device_list)

      new_device = Enum.find(device_list, &(&1.name == "New Router"))
      assert new_device
      assert new_device.ip_address == "192.168.1.100"
      assert new_device.snmp_enabled == true

      # Verify form is cleared for next device
      assert html =~ "name=\"device[name]\""
      refute html =~ "value=\"New Router\""
    end

    test "allows adding multiple devices in a row", %{conn: conn, organization: organization, site: site} do
      {:ok, view, _html} = live(conn, ~p"/orgs/#{organization.slug}/devices/new")

      # Add first device
      view
      |> form("#device-form",
        device: %{
          name: "Router 1",
          ip_address: "192.168.1.1",
          site_id: site.id,
          snmp_community: "public"
        }
      )
      |> render_submit()

      # Add second device (still on same view)
      html =
        view
        |> form("#device-form",
          device: %{
            name: "Router 2",
            ip_address: "192.168.1.2",
            site_id: site.id,
            snmp_community: "public"
          }
        )
        |> render_submit()

      # Verify both devices were created
      device_list = Towerops.Devices.list_organization_devices(organization.id)
      assert length(device_list) == 2
      assert Enum.any?(device_list, &(&1.name == "Router 1"))
      assert Enum.any?(device_list, &(&1.name == "Router 2"))

      # Verify success message for second device
      assert html =~ "Device created successfully"
    end

    test "validates required fields", %{conn: conn, organization: organization} do
      {:ok, view, _html} = live(conn, ~p"/orgs/#{organization.slug}/devices/new")

      html =
        view
        |> form("#device-form", device: %{name: "", ip_address: ""})
        |> render_submit()

      assert html =~ "can&#39;t be blank"
    end

    test "validates IP address format", %{conn: conn, organization: organization, site: site} do
      {:ok, view, _html} = live(conn, ~p"/orgs/#{organization.slug}/devices/new")

      html =
        view
        |> form("#device-form",
          device: %{
            name: "Router",
            ip_address: "invalid-ip",
            site_id: site.id
          }
        )
        |> render_submit()

      assert html =~ "must be a valid IPv4 or IPv6 address"
    end

    test "pre-selects site from query param", %{
      conn: conn,
      organization: organization,
      site: site
    } do
      {:ok, _view, html} =
        live(conn, ~p"/orgs/#{organization.slug}/devices/new?site_id=#{site.id}")

      assert html =~ "New Device"
    end

    test "requires authentication", %{organization: organization} do
      conn = build_conn()
      {:error, redirect} = live(conn, ~p"/orgs/#{organization.slug}/devices/new")

      assert {:redirect, %{to: path}} = redirect
      assert path == ~p"/users/log-in"
    end

    test "allows creating device without name when SNMP enabled", %{
      conn: conn,
      organization: organization,
      site: site
    } do
      {:ok, view, _html} = live(conn, ~p"/orgs/#{organization.slug}/devices/new")

      html =
        view
        |> form("#device-form",
          device: %{
            name: "",
            ip_address: "192.168.1.150",
            site_id: site.id,
            snmp_community: "public"
          }
        )
        |> render_submit()

      # Should succeed without validation error
      assert html =~ "Device created successfully"
      refute html =~ "can&#39;t be blank"

      # Verify device was created with empty name
      device_list = Towerops.Devices.list_organization_devices(organization.id)
      new_device = Enum.find(device_list, &(&1.ip_address == "192.168.1.150"))
      assert new_device
      assert new_device.name == nil or new_device.name == ""
    end

    test "shows helper text about SNMP name population when name is empty", %{
      conn: conn,
      organization: organization
    } do
      {:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/devices/new")

      # Should show helper text about name being populated from SNMP
      assert html =~ "Leave blank to use SNMP device name" or
               html =~ "populated from SNMP" or
               html =~ "sysName"
    end

    test "requires name when ICMP Only mode (no SNMP)", %{
      conn: conn,
      organization: organization,
      site: site
    } do
      {:ok, view, _html} = live(conn, ~p"/orgs/#{organization.slug}/devices/new")

      # Switch to ICMP Only mode
      view
      |> element("button", "ICMP Only")
      |> render_click()

      html =
        view
        |> form("#device-form",
          device: %{
            name: "",
            ip_address: "192.168.1.151",
            site_id: site.id
          }
        )
        |> render_submit()

      # Should show validation error for empty name in ICMP Only mode
      assert html =~ "can&#39;t be blank" or html =~ "required"
    end

    test "defaults to SNMP & ICMP monitoring mode", %{conn: conn, organization: organization} do
      {:ok, _view, html} = live(conn, ~p"/orgs/#{organization.slug}/devices/new")

      assert html =~ "SNMP &amp; ICMP"
      assert html =~ "ICMP Only"
      assert html =~ "SNMP Version"
      assert html =~ "Community String"
    end

    test "switching to ICMP Only mode hides SNMP configuration", %{
      conn: conn,
      organization: organization
    } do
      {:ok, view, _html} = live(conn, ~p"/orgs/#{organization.slug}/devices/new")

      html =
        view
        |> element("button", "ICMP Only")
        |> render_click()

      refute html =~ "SNMP Version"
      refute html =~ "Community String"
    end

    test "switching back to SNMP & ICMP mode shows SNMP configuration", %{
      conn: conn,
      organization: organization
    } do
      {:ok, view, _html} = live(conn, ~p"/orgs/#{organization.slug}/devices/new")

      # First switch to ICMP Only
      view
      |> element("button", "ICMP Only")
      |> render_click()

      # Then switch back to SNMP & ICMP
      html =
        view
        |> element("button", "SNMP & ICMP")
        |> render_click()

      assert html =~ "SNMP Version"
      assert html =~ "Community String"
    end

    test "creates device with ICMP only mode (snmp_enabled false)", %{
      conn: conn,
      organization: organization,
      site: site
    } do
      {:ok, view, _html} = live(conn, ~p"/orgs/#{organization.slug}/devices/new")

      # Switch to ICMP Only mode
      view
      |> element("button", "ICMP Only")
      |> render_click()

      view
      |> form("#device-form",
        device: %{
          name: "ICMP Only Device",
          ip_address: "192.168.1.200",
          site_id: site.id,
          monitoring_enabled: true
        }
      )
      |> render_submit()

      # Verify device was created with snmp_enabled = false
      device_list = Towerops.Devices.list_organization_devices(organization.id)
      new_device = Enum.find(device_list, &(&1.name == "ICMP Only Device"))

      assert new_device
      assert new_device.snmp_enabled == false
    end

    test "creates device with SNMP & ICMP mode (snmp_enabled true)", %{
      conn: conn,
      organization: organization,
      site: site
    } do
      {:ok, view, _html} = live(conn, ~p"/orgs/#{organization.slug}/devices/new")

      # SNMP & ICMP is the default, so no need to click tab

      view
      |> form("#device-form",
        device: %{
          name: "SNMP Device",
          ip_address: "192.168.1.201",
          site_id: site.id,
          monitoring_enabled: true,
          snmp_community: "public"
        }
      )
      |> render_submit()

      # Verify device was created with snmp_enabled = true
      device_list = Towerops.Devices.list_organization_devices(organization.id)
      new_device = Enum.find(device_list, &(&1.name == "SNMP Device"))

      assert new_device
      assert new_device.snmp_enabled == true
    end
  end

  describe "Edit" do
    setup %{site: site} do
      {:ok, device} =
        Towerops.Devices.create_device(%{
          name: "Router 1",
          ip_address: "192.168.1.1",
          site_id: site.id
        })

      %{device: device}
    end

    test "renders edit form", %{conn: conn, organization: organization, device: device} do
      {:ok, _view, html} =
        live(conn, ~p"/orgs/#{organization.slug}/devices/#{device.id}/edit")

      assert html =~ "Edit Device"
      assert html =~ "Update device details"
    end

    test "updates device", %{conn: conn, organization: organization, device: device} do
      {:ok, view, _html} =
        live(conn, ~p"/orgs/#{organization.slug}/devices/#{device.id}/edit")

      {:ok, _, html} =
        view
        |> form("#device-form",
          device: %{
            name: "Updated Router",
            ip_address: "192.168.1.2"
          }
        )
        |> render_submit()
        |> follow_redirect(conn, ~p"/orgs/#{organization.slug}/devices/#{device.id}")

      assert html =~ "Device updated successfully"
      assert html =~ "Updated Router"
    end

    test "requires authentication", %{organization: organization, device: device} do
      conn = build_conn()

      {:error, redirect} =
        live(conn, ~p"/orgs/#{organization.slug}/devices/#{device.id}/edit")

      assert {:redirect, %{to: path}} = redirect
      assert path == ~p"/users/log-in"
    end
  end
end
