defmodule Towerops.Accounts.ScopeTest do
  use ExUnit.Case, async: true

  alias Towerops.Accounts.Scope
  alias Towerops.Accounts.User
  alias Towerops.Organizations.Organization

  defp build_user(attrs \\ %{}) do
    defaults = %{
      id: Ecto.UUID.generate(),
      is_superuser: false,
      timezone: "America/Chicago"
    }

    struct!(User, Map.merge(defaults, attrs))
  end

  defp build_organization(attrs \\ %{}) do
    defaults = %{
      id: Ecto.UUID.generate(),
      name: "Test Org",
      slug: "test-org"
    }

    struct!(Organization, Map.merge(defaults, attrs))
  end

  describe "for_user/1" do
    test "creates scope from user" do
      user = build_user()
      scope = Scope.for_user(user)

      assert %Scope{} = scope
      assert scope.user == user
      assert scope.superuser == nil
      assert scope.impersonating? == false
    end

    test "uses user timezone" do
      user = build_user(%{timezone: "America/Chicago"})
      scope = Scope.for_user(user)

      assert scope.timezone == "America/Chicago"
    end

    test "defaults to UTC when user has no timezone" do
      user = build_user(%{timezone: nil})
      scope = Scope.for_user(user)

      assert scope.timezone == "UTC"
    end

    test "uses user time_format" do
      user = build_user(%{time_format: "12h"})
      scope = Scope.for_user(user)

      assert scope.time_format == "12h"
    end

    test "defaults to 24h when user has no time_format" do
      user = build_user(%{time_format: nil})
      scope = Scope.for_user(user)

      assert scope.time_format == "24h"
    end

    test "returns nil for nil user" do
      assert Scope.for_user(nil) == nil
    end
  end

  describe "for_impersonation/2" do
    test "creates impersonation scope" do
      superuser = build_user(%{is_superuser: true})
      target = build_user(%{timezone: "Europe/London"})

      scope = Scope.for_impersonation(superuser, target)

      assert %Scope{} = scope
      assert scope.user == target
      assert scope.superuser == superuser
      assert scope.timezone == "Europe/London"
    end

    test "sets impersonating? flag" do
      superuser = build_user(%{is_superuser: true})
      target = build_user()

      scope = Scope.for_impersonation(superuser, target)

      assert scope.impersonating? == true
    end

    test "uses target user time_format" do
      superuser = build_user(%{is_superuser: true, time_format: "24h"})
      target = build_user(%{time_format: "12h"})

      scope = Scope.for_impersonation(superuser, target)

      assert scope.time_format == "12h"
    end
  end

  describe "put_organization/2" do
    test "adds organization to scope" do
      user = build_user()
      scope = Scope.for_user(user)
      org = build_organization()

      updated_scope = Scope.put_organization(scope, org)

      assert updated_scope.organization == org
    end

    test "clears organization when nil" do
      user = build_user()
      org = build_organization()

      scope =
        user
        |> Scope.for_user()
        |> Scope.put_organization(org)

      assert scope.organization == org

      cleared = Scope.put_organization(scope, nil)
      assert cleared.organization == nil
    end

    test "returns nil when scope is nil" do
      org = build_organization()
      assert Scope.put_organization(nil, org) == nil
    end
  end

  describe "superuser?/1" do
    test "returns true for superuser" do
      user = build_user(%{is_superuser: true})
      scope = Scope.for_user(user)

      assert Scope.superuser?(scope) == true
    end

    test "returns true for impersonating superuser" do
      superuser = build_user(%{is_superuser: true})
      target = build_user(%{is_superuser: false})

      scope = Scope.for_impersonation(superuser, target)

      assert Scope.superuser?(scope) == true
    end

    test "returns false for regular user" do
      user = build_user(%{is_superuser: false})
      scope = Scope.for_user(user)

      refute Scope.superuser?(scope)
    end

    test "returns false for nil" do
      assert Scope.superuser?(nil) == false
    end
  end
end
