defmodule Towerops.Organizations.OrganizationTest do
  use Towerops.DataCase

  alias Towerops.Organizations.Organization

  describe "changeset/2 - basic" do
    test "valid changeset with just name" do
      changeset = Organization.changeset(%Organization{}, %{name: "Acme Corp"})

      assert changeset.valid?
      assert get_field(changeset, :name) == "Acme Corp"
    end

    test "requires name" do
      changeset = Organization.changeset(%Organization{}, %{})

      refute changeset.valid?
      assert %{name: ["can't be blank"]} = errors_on(changeset)
    end

    test "validates name minimum length of 2" do
      changeset = Organization.changeset(%Organization{}, %{name: "A"})

      refute changeset.valid?
      assert %{name: [error]} = errors_on(changeset)
      assert error =~ "at least"
    end

    test "validates name maximum length of 100" do
      long_name = String.duplicate("a", 101)
      changeset = Organization.changeset(%Organization{}, %{name: long_name})

      refute changeset.valid?
      assert %{name: [error]} = errors_on(changeset)
      assert error =~ "at most"
    end

    test "auto-generates slug" do
      changeset = Organization.changeset(%Organization{}, %{name: "Acme Corp"})

      assert changeset.valid?
      slug = get_field(changeset, :slug)
      assert slug
      assert is_binary(slug)
      assert String.length(slug) > 0
    end

    test "does not override existing slug" do
      org = %Organization{slug: "existing-slug"}
      changeset = Organization.changeset(org, %{name: "Acme Corp"})

      assert changeset.valid?
      assert get_field(changeset, :slug) == "existing-slug"
    end

    test "slug is lowercase alphanumeric" do
      changeset = Organization.changeset(%Organization{}, %{name: "Acme Corp"})

      slug = get_field(changeset, :slug)
      assert slug =~ ~r/^[a-z0-9]+$/
    end

    test "validates subscription_plan inclusion" do
      changeset = Organization.changeset(%Organization{}, %{name: "Acme Corp", subscription_plan: "premium"})

      refute changeset.valid?
      assert %{subscription_plan: _} = errors_on(changeset)
    end

    test "accepts free subscription plan" do
      changeset = Organization.changeset(%Organization{}, %{name: "Acme Corp", subscription_plan: "free"})

      assert changeset.valid?
    end

    test "defaults snmp_version to 2c" do
      changeset = Organization.changeset(%Organization{}, %{name: "Acme Corp"})

      assert changeset.valid?
      assert get_field(changeset, :snmp_version) == "2c"
    end
  end

  describe "changeset/2 - SNMP version validation" do
    test "accepts version 1" do
      changeset = Organization.changeset(%Organization{}, %{name: "Acme Corp", snmp_version: "1"})

      assert changeset.valid?
    end

    test "accepts version 2c" do
      changeset = Organization.changeset(%Organization{}, %{name: "Acme Corp", snmp_version: "2c"})

      assert changeset.valid?
    end

    test "accepts version 3 with required fields" do
      attrs = %{
        name: "Acme Corp",
        snmp_version: "3",
        snmpv3_username: "snmpuser"
      }

      changeset = Organization.changeset(%Organization{snmpv3_auth_protocol: nil, snmpv3_priv_protocol: nil}, attrs)

      assert changeset.valid?
    end

    test "rejects invalid snmp_version" do
      changeset = Organization.changeset(%Organization{}, %{name: "Acme Corp", snmp_version: "4"})

      refute changeset.valid?
      assert %{snmp_version: ["must be 1, 2c, or 3"]} = errors_on(changeset)
    end
  end

  describe "changeset/2 - SNMP port validation" do
    test "rejects port 0" do
      changeset = Organization.changeset(%Organization{}, %{name: "Acme Corp", snmp_port: 0})

      refute changeset.valid?
      assert %{snmp_port: _} = errors_on(changeset)
    end

    test "rejects port 65536" do
      changeset = Organization.changeset(%Organization{}, %{name: "Acme Corp", snmp_port: 65_536})

      refute changeset.valid?
      assert %{snmp_port: _} = errors_on(changeset)
    end

    test "accepts valid port" do
      changeset = Organization.changeset(%Organization{}, %{name: "Acme Corp", snmp_port: 161})

      assert changeset.valid?
    end
  end

  describe "changeset/2 - SNMPv3 validation" do
    test "requires snmpv3_username when snmp_version is 3" do
      changeset =
        Organization.changeset(%Organization{snmpv3_auth_protocol: nil, snmpv3_priv_protocol: nil}, %{
          name: "Acme Corp",
          snmp_version: "3"
        })

      refute changeset.valid?
      assert %{snmpv3_username: ["can't be blank"]} = errors_on(changeset)
    end

    test "derives security_level noAuthNoPriv when no auth protocol" do
      changeset =
        Organization.changeset(%Organization{snmpv3_auth_protocol: nil, snmpv3_priv_protocol: nil}, %{
          name: "Acme Corp",
          snmp_version: "3",
          snmpv3_username: "snmpuser",
          snmpv3_auth_protocol: nil
        })

      assert changeset.valid?
      assert get_field(changeset, :snmpv3_security_level) == "noAuthNoPriv"
    end

    test "derives security_level authNoPriv when auth protocol but no priv" do
      changeset =
        Organization.changeset(%Organization{snmpv3_priv_protocol: nil}, %{
          name: "Acme Corp",
          snmp_version: "3",
          snmpv3_username: "snmpuser",
          snmpv3_auth_protocol: "SHA-256",
          snmpv3_auth_password: "longpassword",
          snmpv3_priv_protocol: nil
        })

      assert changeset.valid?
      assert get_field(changeset, :snmpv3_security_level) == "authNoPriv"
    end

    test "derives security_level authPriv when both auth and priv protocols" do
      changeset =
        Organization.changeset(%Organization{}, %{
          name: "Acme Corp",
          snmp_version: "3",
          snmpv3_username: "snmpuser",
          snmpv3_auth_protocol: "SHA-256",
          snmpv3_auth_password: "longpassword",
          snmpv3_priv_protocol: "AES",
          snmpv3_priv_password: "longpassword"
        })

      assert changeset.valid?
      assert get_field(changeset, :snmpv3_security_level) == "authPriv"
    end

    test "requires auth_password when auth_protocol is set" do
      changeset =
        Organization.changeset(%Organization{snmpv3_priv_protocol: nil}, %{
          name: "Acme Corp",
          snmp_version: "3",
          snmpv3_username: "snmpuser",
          snmpv3_auth_protocol: "SHA-256"
        })

      refute changeset.valid?
      assert %{snmpv3_auth_password: ["required when auth protocol is selected"]} = errors_on(changeset)
    end

    test "requires priv_password when priv_protocol is set" do
      changeset =
        Organization.changeset(%Organization{}, %{
          name: "Acme Corp",
          snmp_version: "3",
          snmpv3_username: "snmpuser",
          snmpv3_auth_protocol: "SHA-256",
          snmpv3_auth_password: "longpassword",
          snmpv3_priv_protocol: "AES"
        })

      refute changeset.valid?
      assert %{snmpv3_priv_password: ["required when privacy protocol is selected"]} = errors_on(changeset)
    end

    test "rejects auth_password shorter than 8 characters" do
      changeset =
        Organization.changeset(%Organization{snmpv3_priv_protocol: nil}, %{
          name: "Acme Corp",
          snmp_version: "3",
          snmpv3_username: "snmpuser",
          snmpv3_auth_protocol: "SHA-256",
          snmpv3_auth_password: "short"
        })

      refute changeset.valid?
      assert %{snmpv3_auth_password: ["must be at least 8 characters"]} = errors_on(changeset)
    end

    test "rejects priv_password shorter than 8 characters" do
      changeset =
        Organization.changeset(%Organization{}, %{
          name: "Acme Corp",
          snmp_version: "3",
          snmpv3_username: "snmpuser",
          snmpv3_auth_protocol: "SHA-256",
          snmpv3_auth_password: "longpassword",
          snmpv3_priv_protocol: "AES",
          snmpv3_priv_password: "short"
        })

      refute changeset.valid?
      assert %{snmpv3_priv_password: ["must be at least 8 characters"]} = errors_on(changeset)
    end

    test "accepts valid SNMPv3 config with all fields" do
      changeset =
        Organization.changeset(%Organization{}, %{
          name: "Acme Corp",
          snmp_version: "3",
          snmpv3_username: "snmpuser",
          snmpv3_auth_protocol: "SHA-256",
          snmpv3_auth_password: "authpass123",
          snmpv3_priv_protocol: "AES",
          snmpv3_priv_password: "privpass123"
        })

      assert changeset.valid?
      assert get_field(changeset, :snmpv3_security_level) == "authPriv"
      assert get_field(changeset, :snmpv3_username) == "snmpuser"
    end
  end

  describe "changeset/2 - MikroTik validation" do
    test "requires mikrotik_username when mikrotik_enabled is true" do
      changeset =
        Organization.changeset(%Organization{}, %{
          name: "Acme Corp",
          mikrotik_enabled: true
        })

      refute changeset.valid?
      assert %{mikrotik_username: ["can't be blank"]} = errors_on(changeset)
    end

    test "does not require mikrotik_username when mikrotik_enabled is false" do
      changeset =
        Organization.changeset(%Organization{}, %{
          name: "Acme Corp",
          mikrotik_enabled: false
        })

      assert changeset.valid?
    end

    test "validates mikrotik_port range" do
      changeset_zero =
        Organization.changeset(%Organization{}, %{
          name: "Acme Corp",
          mikrotik_port: 0
        })

      refute changeset_zero.valid?
      assert %{mikrotik_port: _} = errors_on(changeset_zero)

      changeset_too_high =
        Organization.changeset(%Organization{}, %{
          name: "Acme Corp",
          mikrotik_port: 65_536
        })

      refute changeset_too_high.valid?
      assert %{mikrotik_port: _} = errors_on(changeset_too_high)

      changeset_valid =
        Organization.changeset(%Organization{}, %{
          name: "Acme Corp",
          mikrotik_port: 8729
        })

      assert changeset_valid.valid?
    end
  end
end
