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

  import ExUnit.CaptureIO

  alias Mix.Tasks.GenVendorTests

  @moduletag :mix_task

  setup do
    # Save original directory
    original_dir = File.cwd!()

    # Create temporary directories for vendor modules and tests
    temp_dir = Path.join(System.tmp_dir!(), "vendor_tests_#{System.unique_integer([:positive])}")
    vendors_dir = Path.join(temp_dir, "lib/towerops/snmp/profiles/vendors")
    tests_dir = Path.join(temp_dir, "test/towerops/snmp/profiles/vendors")
    File.mkdir_p!(vendors_dir)
    File.mkdir_p!(tests_dir)

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

    {:ok, temp_dir: temp_dir, vendors_dir: vendors_dir, tests_dir: tests_dir}
  end

  describe "run/1 with dry-run" do
    test "reports vendors without tests in dry-run mode", %{
      vendors_dir: vendors_dir,
      tests_dir: _tests_dir,
      temp_dir: temp_dir
    } do
      # Create vendor module
      File.write!(Path.join(vendors_dir, "mikrotik.ex"), """
      defmodule Towerops.Snmp.Profiles.Vendors.Mikrotik do
        @behaviour Towerops.Snmp.Profiles.Vendors.Vendor

        @impl true
        def profile_names, do: ["mikrotik"]
      end
      """)

      # Change to temp directory to run task
      File.cd!(temp_dir, fn ->
        output =
          capture_io(fn ->
            GenVendorTests.run(["--dry-run"])
          end)

        assert output =~ "Found 1 vendor modules"
        assert output =~ "Found 0 existing tests"
        assert output =~ "1 vendors need tests"
        assert output =~ "Would generate: test/towerops/snmp/profiles/vendors/mikrotik_test.exs"
      end)
    end

    test "reports no work needed when all vendors have tests", %{
      vendors_dir: vendors_dir,
      tests_dir: tests_dir,
      temp_dir: temp_dir
    } do
      # Create vendor module
      File.write!(Path.join(vendors_dir, "mikrotik.ex"), """
      defmodule Towerops.Snmp.Profiles.Vendors.Mikrotik do
        def profile_names, do: ["mikrotik"]
      end
      """)

      # Create corresponding test
      File.write!(Path.join(tests_dir, "mikrotik_test.exs"), """
      defmodule Towerops.Snmp.Profiles.Vendors.MikrotikTest do
        use ExUnit.Case
      end
      """)

      File.cd!(temp_dir, fn ->
        output =
          capture_io(fn ->
            GenVendorTests.run(["--dry-run"])
          end)

        assert output =~ "All vendor modules have tests!"
      end)
    end
  end

  describe "run/1 without dry-run" do
    test "generates test file for vendor without test", %{
      vendors_dir: vendors_dir,
      tests_dir: tests_dir,
      temp_dir: temp_dir
    } do
      # Create vendor module with profile_names
      File.write!(Path.join(vendors_dir, "cisco.ex"), """
      defmodule Towerops.Snmp.Profiles.Vendors.Cisco do
        @behaviour Towerops.Snmp.Profiles.Vendors.Vendor

        @impl true
        def profile_names, do: ["cisco-ios", "cisco-nxos"]
      end
      """)

      File.cd!(temp_dir, fn ->
        output =
          capture_io(fn ->
            GenVendorTests.run([])
          end)

        assert output =~ "Generated: test/towerops/snmp/profiles/vendors/cisco_test.exs"
        assert output =~ "Generated 1 test files"

        # Verify test file was created
        test_file = Path.join(tests_dir, "cisco_test.exs")
        assert File.exists?(test_file)

        content = File.read!(test_file)
        assert content =~ "defmodule Towerops.Snmp.Profiles.Vendors.CiscoTest do"
        assert content =~ "use Towerops.DataCase, async: true"
        assert content =~ "alias Towerops.Snmp.Profiles.Vendors.Cisco"
        assert content =~ ~s{profile_names() == ["cisco-ios", "cisco-nxos"]}
        assert content =~ "describe \"profile_names/0\" do"
        assert content =~ "describe \"detect_hardware/1\" do"
        assert content =~ "describe \"wireless_oid_defs/0\" do"
        assert content =~ "describe \"discover_wireless_sensors/1\" do"
      end)
    end

    test "generates tests for multiple vendors", %{vendors_dir: vendors_dir, tests_dir: tests_dir, temp_dir: temp_dir} do
      # Create multiple vendor modules
      File.write!(Path.join(vendors_dir, "mikrotik.ex"), """
      defmodule Towerops.Snmp.Profiles.Vendors.Mikrotik do
        def profile_names, do: ["mikrotik"]
      end
      """)

      File.write!(Path.join(vendors_dir, "ubiquiti.ex"), """
      defmodule Towerops.Snmp.Profiles.Vendors.Ubiquiti do
        def profile_names, do: ["ubnt"]
      end
      """)

      File.write!(Path.join(vendors_dir, "cisco.ex"), """
      defmodule Towerops.Snmp.Profiles.Vendors.Cisco do
        def profile_names, do: ["cisco"]
      end
      """)

      File.cd!(temp_dir, fn ->
        output =
          capture_io(fn ->
            GenVendorTests.run([])
          end)

        assert output =~ "Generated 3 test files"
        assert File.exists?(Path.join(tests_dir, "mikrotik_test.exs"))
        assert File.exists?(Path.join(tests_dir, "ubiquiti_test.exs"))
        assert File.exists?(Path.join(tests_dir, "cisco_test.exs"))
      end)
    end

    test "skips vendors that already have tests", %{vendors_dir: vendors_dir, tests_dir: tests_dir, temp_dir: temp_dir} do
      # Create vendor modules
      File.write!(Path.join(vendors_dir, "mikrotik.ex"), """
      defmodule Towerops.Snmp.Profiles.Vendors.Mikrotik do
        def profile_names, do: ["mikrotik"]
      end
      """)

      File.write!(Path.join(vendors_dir, "cisco.ex"), """
      defmodule Towerops.Snmp.Profiles.Vendors.Cisco do
        def profile_names, do: ["cisco"]
      end
      """)

      # Create test for mikrotik (but not cisco)
      File.write!(Path.join(tests_dir, "mikrotik_test.exs"), """
      defmodule Towerops.Snmp.Profiles.Vendors.MikrotikTest do
        use ExUnit.Case
      end
      """)

      File.cd!(temp_dir, fn ->
        output =
          capture_io(fn ->
            GenVendorTests.run([])
          end)

        assert output =~ "Found 2 vendor modules"
        assert output =~ "Found 1 existing tests"
        assert output =~ "1 vendors need tests"
        assert output =~ "Generated 1 test files"

        # Only cisco test should be generated
        assert File.exists?(Path.join(tests_dir, "cisco_test.exs"))

        # Mikrotik test should be the old one (not regenerated)
        mikrotik_test = File.read!(Path.join(tests_dir, "mikrotik_test.exs"))
        assert mikrotik_test =~ "use ExUnit.Case"
        refute mikrotik_test =~ "use Towerops.DataCase"
      end)
    end

    test "excludes registry and vendor files from generation", %{
      vendors_dir: vendors_dir,
      tests_dir: tests_dir,
      temp_dir: temp_dir
    } do
      # Create vendor modules
      File.write!(Path.join(vendors_dir, "registry.ex"), """
      defmodule Towerops.Snmp.Profiles.Vendors.Registry do
      end
      """)

      File.write!(Path.join(vendors_dir, "vendor.ex"), """
      defmodule Towerops.Snmp.Profiles.Vendors.Vendor do
      end
      """)

      File.write!(Path.join(vendors_dir, "mikrotik.ex"), """
      defmodule Towerops.Snmp.Profiles.Vendors.Mikrotik do
        def profile_names, do: ["mikrotik"]
      end
      """)

      File.cd!(temp_dir, fn ->
        output =
          capture_io(fn ->
            GenVendorTests.run([])
          end)

        # Should only count mikrotik as vendor module
        assert output =~ "Found 1 vendor modules"
        assert output =~ "Generated 1 test files"

        # Only mikrotik test should be generated
        assert File.exists?(Path.join(tests_dir, "mikrotik_test.exs"))
        refute File.exists?(Path.join(tests_dir, "registry_test.exs"))
        refute File.exists?(Path.join(tests_dir, "vendor_test.exs"))
      end)
    end

    test "handles vendor module without profile_names function", %{
      vendors_dir: vendors_dir,
      tests_dir: tests_dir,
      temp_dir: temp_dir
    } do
      # Create vendor module without profile_names
      File.write!(Path.join(vendors_dir, "custom.ex"), """
      defmodule Towerops.Snmp.Profiles.Vendors.Custom do
        # No profile_names function
      end
      """)

      File.cd!(temp_dir, fn ->
        output =
          capture_io(fn ->
            GenVendorTests.run([])
          end)

        assert output =~ "Generated 1 test files"

        # Test should be generated with empty profile list
        test_file = Path.join(tests_dir, "custom_test.exs")
        assert File.exists?(test_file)

        content = File.read!(test_file)
        assert content =~ "assert Custom.profile_names() == []"
      end)
    end

    test "generates proper module name for snake_case files", %{
      vendors_dir: vendors_dir,
      tests_dir: tests_dir,
      temp_dir: temp_dir
    } do
      # Create vendor module with snake_case filename
      File.write!(Path.join(vendors_dir, "palo_alto.ex"), """
      defmodule Towerops.Snmp.Profiles.Vendors.PaloAlto do
        def profile_names, do: ["panos"]
      end
      """)

      File.cd!(temp_dir, fn ->
        _output =
          capture_io(fn ->
            GenVendorTests.run([])
          end)

        # Test file should be palo_alto_test.exs
        test_file = Path.join(tests_dir, "palo_alto_test.exs")
        assert File.exists?(test_file)

        # Module should be PaloAltoTest
        content = File.read!(test_file)
        assert content =~ "defmodule Towerops.Snmp.Profiles.Vendors.PaloAltoTest do"
        assert content =~ "alias Towerops.Snmp.Profiles.Vendors.PaloAlto"
      end)
    end

    test "generates tests with proper expect counts for wireless_oid_defs", %{
      vendors_dir: vendors_dir,
      tests_dir: tests_dir,
      temp_dir: temp_dir
    } do
      # Create vendor module with multiple OID defs
      File.write!(Path.join(vendors_dir, "test_vendor.ex"), """
      defmodule Towerops.Snmp.Profiles.Vendors.TestVendor do
        def profile_names, do: ["test"]

        def wireless_oid_defs do
          [
            %{oid: "1.2.3.4.0", sensor_descr: "Temp", sensor_type: "temperature", sensor_unit: "C", sensor_divisor: 1},
            %{oid: "1.2.3.5.0", sensor_descr: "Voltage", sensor_type: "voltage", sensor_unit: "V", sensor_divisor: 1},
            %{oid: "1.2.3.6.0", sensor_descr: "Power", sensor_type: "power", sensor_unit: "W", sensor_divisor: 1}
          ]
        end
      end
      """)

      File.cd!(temp_dir, fn ->
        capture_io(fn ->
          GenVendorTests.run([])
        end)

        test_file = Path.join(tests_dir, "test_vendor_test.exs")
        content = File.read!(test_file)

        # Should verify list has >= 1 items (not hardcoded count)
        assert content =~ "assert length(defs) >= 1"
      end)
    end
  end
end
