defmodule MvWeb.AuthorizationTest do
  @moduledoc """
  Tests for UI-level authorization helpers.
  """
  use ExUnit.Case, async: true

  alias MvWeb.Authorization
  alias Mv.Membership.Member
  alias Mv.Accounts.User

  describe "can?/3 with resource atom" do
    test "returns true when user has permission for resource+action" do
      admin = %{
        id: "admin-123",
        role: %{permission_set_name: "admin"}
      }

      assert Authorization.can?(admin, :create, Mv.Membership.Member) == true
      assert Authorization.can?(admin, :read, Mv.Membership.Member) == true
      assert Authorization.can?(admin, :update, Mv.Membership.Member) == true
      assert Authorization.can?(admin, :destroy, Mv.Membership.Member) == true
    end

    test "returns false when user lacks permission" do
      read_only_user = %{
        id: "read-only-123",
        role: %{permission_set_name: "read_only"}
      }

      assert Authorization.can?(read_only_user, :create, Mv.Membership.Member) == false
      assert Authorization.can?(read_only_user, :read, Mv.Membership.Member) == true
      assert Authorization.can?(read_only_user, :update, Mv.Membership.Member) == false
      assert Authorization.can?(read_only_user, :destroy, Mv.Membership.Member) == false
    end

    test "returns false for nil user" do
      assert Authorization.can?(nil, :create, Mv.Membership.Member) == false
      assert Authorization.can?(nil, :read, Mv.Membership.Member) == false
    end

    test "admin can manage roles" do
      admin = %{
        id: "admin-123",
        role: %{permission_set_name: "admin"}
      }

      assert Authorization.can?(admin, :create, Mv.Authorization.Role) == true
      assert Authorization.can?(admin, :read, Mv.Authorization.Role) == true
      assert Authorization.can?(admin, :update, Mv.Authorization.Role) == true
      assert Authorization.can?(admin, :destroy, Mv.Authorization.Role) == true
    end

    test "non-admin can read roles but cannot create/update/destroy" do
      normal_user = %{
        id: "normal-123",
        role: %{permission_set_name: "normal_user"}
      }

      assert Authorization.can?(normal_user, :read, Mv.Authorization.Role) == true
      assert Authorization.can?(normal_user, :create, Mv.Authorization.Role) == false
      assert Authorization.can?(normal_user, :update, Mv.Authorization.Role) == false
      assert Authorization.can?(normal_user, :destroy, Mv.Authorization.Role) == false
    end
  end

  describe "can?/3 with record struct - scope :all" do
    test "admin can update any member" do
      admin = %{
        id: "admin-123",
        role: %{permission_set_name: "admin"}
      }

      member1 = %Member{id: "member-1", user: %User{id: "other-user"}}
      member2 = %Member{id: "member-2", user: %User{id: "another-user"}}

      assert Authorization.can?(admin, :update, member1) == true
      assert Authorization.can?(admin, :update, member2) == true
    end

    test "normal_user can update any member" do
      normal_user = %{
        id: "normal-123",
        role: %{permission_set_name: "normal_user"}
      }

      member = %Member{id: "member-1", user: %User{id: "other-user"}}

      assert Authorization.can?(normal_user, :update, member) == true
    end
  end

  describe "can?/3 with record struct - scope :own" do
    test "user can update own User record" do
      user = %{
        id: "user-123",
        role: %{permission_set_name: "own_data"}
      }

      own_user_record = %User{id: "user-123"}
      other_user_record = %User{id: "other-user"}

      assert Authorization.can?(user, :update, own_user_record) == true
      assert Authorization.can?(user, :update, other_user_record) == false
    end
  end

  describe "can?/3 with record struct - scope :linked" do
    test "user can update linked member" do
      user = %{
        id: "user-123",
        role: %{permission_set_name: "own_data"}
      }

      # Member has_one :user (inverse relationship)
      linked_member = %Member{id: "member-1", user: %User{id: "user-123"}}
      unlinked_member = %Member{id: "member-2", user: nil}
      unlinked_member_other = %Member{id: "member-3", user: %User{id: "other-user"}}

      assert Authorization.can?(user, :update, linked_member) == true
      assert Authorization.can?(user, :update, unlinked_member) == false
      assert Authorization.can?(user, :update, unlinked_member_other) == false
    end

    test "user can update CustomFieldValue of linked member" do
      user = %{
        id: "user-123",
        role: %{permission_set_name: "own_data"}
      }

      linked_cfv = %Mv.Membership.CustomFieldValue{
        id: "cfv-1",
        member: %Member{id: "member-1", user: %User{id: "user-123"}}
      }

      unlinked_cfv = %Mv.Membership.CustomFieldValue{
        id: "cfv-2",
        member: %Member{id: "member-2", user: nil}
      }

      unlinked_cfv_other = %Mv.Membership.CustomFieldValue{
        id: "cfv-3",
        member: %Member{id: "member-3", user: %User{id: "other-user"}}
      }

      assert Authorization.can?(user, :update, linked_cfv) == true
      assert Authorization.can?(user, :update, unlinked_cfv) == false
      assert Authorization.can?(user, :update, unlinked_cfv_other) == false
    end
  end

  describe "can_access_page?/2" do
    test "admin can access all pages via wildcard" do
      admin = %{
        id: "admin-123",
        role: %{permission_set_name: "admin"}
      }

      assert Authorization.can_access_page?(admin, "/admin/roles") == true
      assert Authorization.can_access_page?(admin, "/members") == true
      assert Authorization.can_access_page?(admin, "/any/page") == true
    end

    test "read_only user can access allowed pages" do
      read_only_user = %{
        id: "read-only-123",
        role: %{permission_set_name: "read_only"}
      }

      assert Authorization.can_access_page?(read_only_user, "/") == true
      assert Authorization.can_access_page?(read_only_user, "/members") == true
      assert Authorization.can_access_page?(read_only_user, "/members/123") == true
      assert Authorization.can_access_page?(read_only_user, "/admin/roles") == false
    end

    test "matches dynamic routes correctly" do
      read_only_user = %{
        id: "read-only-123",
        role: %{permission_set_name: "read_only"}
      }

      assert Authorization.can_access_page?(read_only_user, "/members/123") == true
      assert Authorization.can_access_page?(read_only_user, "/members/abc") == true
      assert Authorization.can_access_page?(read_only_user, "/members/123/edit") == false
    end

    test "read_only can access own profile /users/:id only" do
      read_only_user = %{
        id: "read-only-123",
        role: %{permission_set_name: "read_only"}
      }

      assert Authorization.can_access_page?(read_only_user, "/users/read-only-123") == true
      assert Authorization.can_access_page?(read_only_user, "/users/read-only-123/edit") == true
      assert Authorization.can_access_page?(read_only_user, "/users/other-id") == false
      assert Authorization.can_access_page?(read_only_user, "/users/other-id/edit") == false
    end

    test "normal_user can access own profile /users/:id only" do
      normal_user = %{
        id: "normal-456",
        role: %{permission_set_name: "normal_user"}
      }

      assert Authorization.can_access_page?(normal_user, "/users/normal-456") == true
      assert Authorization.can_access_page?(normal_user, "/users/normal-456/edit") == true
      assert Authorization.can_access_page?(normal_user, "/users/other-id") == false
    end

    test "reserved segment 'new' is not matched by :id" do
      read_only_user = %{
        id: "read-only-123",
        role: %{permission_set_name: "read_only"}
      }

      assert Authorization.can_access_page?(read_only_user, "/members/new") == false
      assert Authorization.can_access_page?(read_only_user, "/groups/new") == false
    end

    test "returns false for nil user" do
      assert Authorization.can_access_page?(nil, "/members") == false
      assert Authorization.can_access_page?(nil, "/admin/roles") == false
    end
  end

  describe "error handling" do
    test "user without role returns false" do
      user_without_role = %{id: "user-123", role: nil}

      assert Authorization.can?(user_without_role, :create, Mv.Membership.Member) == false
      assert Authorization.can_access_page?(user_without_role, "/members") == false
    end

    test "user with invalid permission_set_name returns false" do
      user_with_invalid_permission = %{
        id: "user-123",
        role: %{permission_set_name: "invalid_set"}
      }

      assert Authorization.can?(user_with_invalid_permission, :create, Mv.Membership.Member) ==
               false

      assert Authorization.can_access_page?(user_with_invalid_permission, "/members") == false
    end

    test "handles missing fields gracefully" do
      user_missing_role = %{id: "user-123"}

      assert Authorization.can?(user_missing_role, :create, Mv.Membership.Member) == false
      assert Authorization.can_access_page?(user_missing_role, "/members") == false
    end
  end
end
