defmodule ToweropsWeb.GraphLive.ShowTest do
  use ToweropsWeb.ConnCase

  import Phoenix.LiveViewTest

  alias Towerops.Monitoring
  alias Towerops.Organizations
  alias Towerops.Repo
  alias Towerops.Sites
  alias Towerops.Snmp
  alias Towerops.Snmp.Device
  alias Towerops.Snmp.Interface
  alias Towerops.Snmp.Sensor

  setup :register_and_log_in_user

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

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

    {:ok, device} =
      Towerops.Devices.create_device(%{
        name: "Test Router",
        ip_address: "192.168.1.1",
        site_id: site.id,
        organization_id: organization.id,
        snmp_enabled: true
      })

    # Create SNMP device for sensor tests
    snmp_device =
      %Device{}
      |> Device.changeset(%{
        device_id: device.id,
        sys_name: "test-router",
        sys_descr: "Test Router",
        sys_object_id: "1.3.6.1.4.1.9"
      })
      |> Repo.insert!()

    %{organization: organization, site: site, device: device, snmp_device: snmp_device}
  end

  describe "Latency graph" do
    test "renders latency graph page with correct content", %{
      conn: conn,
      device: device,
      organization: _org
    } do
      # Create monitoring checks
      Monitoring.create_check(%{
        device_id: device.id,
        status: :success,
        response_time_ms: 10.5,
        checked_at: DateTime.utc_now()
      })

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

      # Verify page renders with correct content
      assert html =~ "Ping Latency"
      assert html =~ device.name
      assert html =~ "24 Hours"
    end

    test "uses current_organization in back link", %{
      conn: conn,
      device: device,
      organization: _org
    } do
      {:ok, _view, html} = live(conn, ~p"/devices/#{device.id}/graph/latency")

      # Verify back link uses organization slug (this was the bug)
      assert html =~ ~s[/devices/#{device.id}]
    end

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

      assert html =~ "No sensor data available"
    end
  end

  describe "Processor graph" do
    test "renders processor graph page", %{
      conn: conn,
      device: device,
      organization: _org,
      snmp_device: snmp_device
    } do
      # Create a CPU sensor
      sensor =
        %Sensor{}
        |> Sensor.changeset(%{
          snmp_device_id: snmp_device.id,
          sensor_type: "cpu_load",
          sensor_index: "1",
          sensor_oid: "1.3.6.1.4.1.9.9.109.1.1.1.1.8.1",
          sensor_descr: "CPU 1",
          sensor_unit: "%"
        })
        |> Repo.insert!()

      # Create sensor reading
      Snmp.create_sensor_reading(%{
        sensor_id: sensor.id,
        value: 45.2,
        checked_at: DateTime.utc_now()
      })

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

      assert html =~ "Processor Usage"
      assert html =~ device.name
    end
  end

  describe "Memory graph" do
    test "renders memory graph page", %{
      conn: conn,
      device: device,
      organization: _org,
      snmp_device: snmp_device
    } do
      # Create a memory sensor
      sensor =
        %Sensor{}
        |> Sensor.changeset(%{
          snmp_device_id: snmp_device.id,
          sensor_type: "memory_usage",
          sensor_index: "1",
          sensor_oid: "1.3.6.1.4.1.9.9.109.1.1.1.1.12.1",
          sensor_descr: "Memory",
          sensor_unit: "%"
        })
        |> Repo.insert!()

      # Create sensor reading
      Snmp.create_sensor_reading(%{
        sensor_id: sensor.id,
        value: 75.5,
        checked_at: DateTime.utc_now()
      })

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

      assert html =~ "Memory Usage"
      assert html =~ device.name
    end
  end

  describe "Traffic graph" do
    test "renders overall traffic graph page", %{
      conn: conn,
      device: device,
      organization: _org,
      snmp_device: snmp_device
    } do
      # Create an interface
      interface =
        %Interface{}
        |> Interface.changeset(%{
          snmp_device_id: snmp_device.id,
          if_index: 1,
          if_descr: "eth0",
          if_name: "eth0",
          if_oper_status: "up",
          if_speed: 1_000_000_000
        })
        |> Repo.insert!()

      # Create interface stats
      Snmp.create_interface_stat(%{
        interface_id: interface.id,
        if_in_octets: 1000,
        if_out_octets: 2000,
        checked_at: DateTime.utc_now()
      })

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

      assert html =~ "Overall Traffic"
      assert html =~ device.name
    end

    test "renders specific interface traffic graph", %{
      conn: conn,
      device: device,
      organization: _org,
      snmp_device: snmp_device
    } do
      # Create an interface
      interface =
        %Interface{}
        |> Interface.changeset(%{
          snmp_device_id: snmp_device.id,
          if_index: 1,
          if_descr: "eth0",
          if_name: "eth0",
          if_oper_status: "up",
          if_speed: 1_000_000_000
        })
        |> Repo.insert!()

      # Create interface stats
      Snmp.create_interface_stat(%{
        interface_id: interface.id,
        if_in_octets: 1000,
        if_out_octets: 2000,
        checked_at: DateTime.utc_now()
      })

      {:ok, _view, html} =
        live(
          conn,
          ~p"/devices/#{device.id}/graph/traffic?interface_id=#{interface.id}"
        )

      assert html =~ "Overall Traffic"
      assert html =~ device.name
    end

    test "handles nil if_in_octets gracefully", %{
      conn: conn,
      device: device,
      organization: _org,
      snmp_device: snmp_device
    } do
      # Create an interface
      interface =
        %Interface{}
        |> Interface.changeset(%{
          snmp_device_id: snmp_device.id,
          if_index: 1,
          if_descr: "eth0",
          if_name: "eth0",
          if_oper_status: "up",
          if_speed: 1_000_000_000
        })
        |> Repo.insert!()

      # Create interface stats with nil if_in_octets
      base_time = DateTime.utc_now()

      Snmp.create_interface_stat(%{
        interface_id: interface.id,
        if_in_octets: nil,
        if_out_octets: 1000,
        checked_at: DateTime.add(base_time, -60, :second)
      })

      Snmp.create_interface_stat(%{
        interface_id: interface.id,
        if_in_octets: nil,
        if_out_octets: 2000,
        checked_at: base_time
      })

      # Should not crash
      {:ok, _view, html} = live(conn, ~p"/devices/#{device.id}/graph/traffic")

      assert html =~ "Overall Traffic"
      assert html =~ device.name
    end

    test "handles nil if_out_octets gracefully", %{
      conn: conn,
      device: device,
      organization: _org,
      snmp_device: snmp_device
    } do
      # Create an interface
      interface =
        %Interface{}
        |> Interface.changeset(%{
          snmp_device_id: snmp_device.id,
          if_index: 1,
          if_descr: "eth0",
          if_name: "eth0",
          if_oper_status: "up",
          if_speed: 1_000_000_000
        })
        |> Repo.insert!()

      # Create interface stats with nil if_out_octets
      base_time = DateTime.utc_now()

      Snmp.create_interface_stat(%{
        interface_id: interface.id,
        if_in_octets: 1000,
        if_out_octets: nil,
        checked_at: DateTime.add(base_time, -60, :second)
      })

      Snmp.create_interface_stat(%{
        interface_id: interface.id,
        if_in_octets: 2000,
        if_out_octets: nil,
        checked_at: base_time
      })

      # Should not crash
      {:ok, _view, html} = live(conn, ~p"/devices/#{device.id}/graph/traffic")

      assert html =~ "Overall Traffic"
      assert html =~ device.name
    end

    test "handles mixed nil and valid octets values", %{
      conn: conn,
      device: device,
      organization: _org,
      snmp_device: snmp_device
    } do
      # Create an interface
      interface =
        %Interface{}
        |> Interface.changeset(%{
          snmp_device_id: snmp_device.id,
          if_index: 1,
          if_descr: "eth0",
          if_name: "eth0",
          if_oper_status: "up",
          if_speed: 1_000_000_000
        })
        |> Repo.insert!()

      # Create interface stats with mixed nil and valid values
      base_time = DateTime.utc_now()

      # First stat: nil values
      Snmp.create_interface_stat(%{
        interface_id: interface.id,
        if_in_octets: nil,
        if_out_octets: nil,
        checked_at: DateTime.add(base_time, -120, :second)
      })

      # Second stat: valid values
      Snmp.create_interface_stat(%{
        interface_id: interface.id,
        if_in_octets: 1000,
        if_out_octets: 2000,
        checked_at: DateTime.add(base_time, -60, :second)
      })

      # Third stat: valid values
      Snmp.create_interface_stat(%{
        interface_id: interface.id,
        if_in_octets: 2000,
        if_out_octets: 4000,
        checked_at: base_time
      })

      # Should not crash and should render valid data points
      {:ok, _view, html} = live(conn, ~p"/devices/#{device.id}/graph/traffic")

      assert html =~ "Overall Traffic"
      assert html =~ device.name
    end
  end

  describe "Time range selection" do
    test "defaults to 24h range", %{conn: conn, device: device, organization: _org} do
      {:ok, _view, html} = live(conn, ~p"/devices/#{device.id}/graph/latency")

      assert html =~ "24 Hours"
    end

    test "accepts custom range parameter", %{conn: conn, device: device, organization: _org} do
      {:ok, _view, html} = live(conn, ~p"/devices/#{device.id}/graph/latency?range=1h")

      assert html =~ "1 Hour"
    end

    test "changes range when user clicks range button", %{
      conn: conn,
      device: device,
      organization: _org
    } do
      {:ok, view, html} = live(conn, ~p"/devices/#{device.id}/graph/latency")

      # Initially 24h
      assert html =~ "24 Hours"

      # Change to 1h
      html =
        view
        |> element("button", "1 Hour")
        |> render_click()

      assert html =~ "1 Hour"
    end

    test "preserves interface_id when changing range", %{
      conn: conn,
      device: device,
      organization: _org,
      snmp_device: snmp_device
    } do
      # Create an interface
      interface =
        %Interface{}
        |> Interface.changeset(%{
          snmp_device_id: snmp_device.id,
          if_index: 1,
          if_descr: "eth0",
          if_name: "eth0",
          if_oper_status: "up",
          if_speed: 1_000_000_000
        })
        |> Repo.insert!()

      {:ok, view, _html} =
        live(
          conn,
          ~p"/devices/#{device.id}/graph/traffic?interface_id=#{interface.id}"
        )

      # Change range and verify page still works
      html =
        view
        |> element("button", "1 Hour")
        |> render_click()

      assert html =~ "1 Hour"
      assert html =~ "Overall Traffic"
    end
  end

  describe "Temperature graph" do
    test "renders temperature graph page", %{
      conn: conn,
      device: device,
      organization: _org,
      snmp_device: snmp_device
    } do
      sensor =
        %Sensor{}
        |> Sensor.changeset(%{
          snmp_device_id: snmp_device.id,
          sensor_type: "temperature",
          sensor_index: "1",
          sensor_oid: "1.3.6.1.4.1.9.9.13.1.3.1.3.1",
          sensor_descr: "Chassis Temp",
          sensor_unit: "C"
        })
        |> Repo.insert!()

      Snmp.create_sensor_reading(%{
        sensor_id: sensor.id,
        value: 42.0,
        checked_at: DateTime.utc_now()
      })

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

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

  describe "Storage graph" do
    test "renders storage graph page", %{conn: conn, device: device, organization: _org} do
      {:ok, _view, html} = live(conn, ~p"/devices/#{device.id}/graph/storage")

      assert html =~ "Storage Usage"
      assert html =~ device.name
    end
  end

  describe "Sensor-specific graph" do
    test "renders graph for specific sensor by ID", %{
      conn: conn,
      device: device,
      organization: _org,
      snmp_device: snmp_device
    } do
      sensor =
        %Sensor{}
        |> Sensor.changeset(%{
          snmp_device_id: snmp_device.id,
          sensor_type: "voltage",
          sensor_index: "1",
          sensor_oid: "1.3.6.1.4.1.9.9.13.1.2.1.3.1",
          sensor_descr: "PSU Voltage",
          sensor_unit: "V"
        })
        |> Repo.insert!()

      Snmp.create_sensor_reading(%{
        sensor_id: sensor.id,
        value: 12.1,
        checked_at: DateTime.utc_now()
      })

      {:ok, _view, html} =
        live(conn, ~p"/devices/#{device.id}/graph/voltage?sensor_id=#{sensor.id}")

      assert html =~ "Voltage"
      assert html =~ "PSU Voltage"
    end
  end

  describe "handle_info events" do
    test "handles device_status_changed", %{conn: conn, device: device, organization: _org} do
      {:ok, view, _html} = live(conn, ~p"/devices/#{device.id}/graph/latency")

      send(view.pid, {:device_status_changed, device.id, :up, 10})

      assert render(view)
    end

    test "handles state_sensors_updated", %{conn: conn, device: device, organization: _org} do
      {:ok, view, _html} = live(conn, ~p"/devices/#{device.id}/graph/latency")

      send(view.pid, {:state_sensors_updated, device.id})

      assert render(view)
    end

    test "handles neighbors_updated", %{conn: conn, device: device, organization: _org} do
      {:ok, view, _html} = live(conn, ~p"/devices/#{device.id}/graph/latency")

      send(view.pid, {:neighbors_updated, device.id})

      assert render(view)
    end

    test "handles live_poll when not in live mode", %{
      conn: conn,
      device: device,
      organization: _org
    } do
      {:ok, view, _html} = live(conn, ~p"/devices/#{device.id}/graph/latency")

      send(view.pid, :live_poll)

      assert render(view)
    end
  end

  describe "access control" do
    test "redirects when device not found", %{conn: conn} do
      fake_id = Ecto.UUID.generate()
      assert {:error, {:live_redirect, %{to: "/devices"}}} = live(conn, ~p"/devices/#{fake_id}/graph/latency")
    end
  end
end
