use Mojo::Base -strict;

use Test::More;
use Test::Mojo;
use utils::RBAC;
use utils::JWT;

# 1. Test Role Identification Functions
my $admin_user     = { id => 1, email => 'admin@anchor.com', role => 'admin' };
my $supporter_user = { id => 2, email => 'support@anchor.com', role => 'supporter' };
my $client_user    = { id => 3, email => 'client@anchor.com', role => 'client' };
my $default_user   = { id => 4, email => 'legacy@anchor.com' }; # no role field

ok(utils::RBAC::is_admin($admin_user), 'Admin user identified as admin');
ok(!utils::RBAC::is_admin($supporter_user), 'Supporter is not admin');
ok(!utils::RBAC::is_admin($client_user), 'Client is not admin');

ok(utils::RBAC::is_supporter($supporter_user), 'Supporter identified as supporter');
ok(!utils::RBAC::is_supporter($admin_user), 'Admin is not supporter');
ok(!utils::RBAC::is_supporter($client_user), 'Client is not supporter');

ok(utils::RBAC::is_staff($admin_user), 'Admin is staff');
ok(utils::RBAC::is_staff($supporter_user), 'Supporter is staff');
ok(!utils::RBAC::is_staff($client_user), 'Client is not staff');

ok(utils::RBAC::is_client($client_user), 'Client identified as client');
ok(utils::RBAC::is_client($default_user), 'User without explicit role defaults to client');
ok(!utils::RBAC::is_client($admin_user), 'Admin is not client');
ok(!utils::RBAC::is_client($supporter_user), 'Supporter is not client');

# 2. Test Admin Superuser Permissions (Admin always has all permissions)
is(utils::RBAC::has_permission(undef, $admin_user, 'clients.view'), 1, 'Admin has clients.view');
is(utils::RBAC::has_permission(undef, $admin_user, 'clients.delete'), 1, 'Admin has clients.delete');
is(utils::RBAC::has_permission(undef, $admin_user, 'tickets.reply'), 1, 'Admin has tickets.reply');
is(utils::RBAC::has_permission(undef, $admin_user, 'staff.manage'), 1, 'Admin has staff.manage');
is(utils::RBAC::has_permission(undef, $admin_user, 'any.custom.permission'), 1, 'Admin has arbitrary custom permission');

# 3. Test Client Immutable Permissions (Defined by backend policy)
is(utils::RBAC::has_permission(undef, $client_user, 'tickets.create_own'), 1, 'Client has tickets.create_own');
is(utils::RBAC::has_permission(undef, $client_user, 'tickets.view_own'), 1, 'Client has tickets.view_own');
is(utils::RBAC::has_permission(undef, $client_user, 'profile.view_own'), 1, 'Client has profile.view_own');
is(utils::RBAC::has_permission(undef, $client_user, 'departments.view'), 1, 'Client has departments.view');

# Client MUST NOT have admin/staff permissions
is(utils::RBAC::has_permission(undef, $client_user, 'clients.view'), 0, 'Client denied clients.view');
is(utils::RBAC::has_permission(undef, $client_user, 'clients.create'), 0, 'Client denied clients.create');
is(utils::RBAC::has_permission(undef, $client_user, 'tickets.view_all'), 0, 'Client denied tickets.view_all');
is(utils::RBAC::has_permission(undef, $client_user, 'tickets.reply'), 0, 'Client denied tickets.reply (staff reply)');
is(utils::RBAC::has_permission(undef, $client_user, 'staff.manage'), 0, 'Client denied staff.manage');

# 4. Test Mock / In-Memory DB Supporter Permissions
{
  # Mock DB handle that returns assigned permissions
  package MockDB;
  sub new { bless {}, shift }
  sub query {
    my ($self, $sql, $user_id, $slug) = @_;
    my $granted = ($user_id == 2 && $slug eq 'clients.view') ? 1 : 0;
    return MockRes->new($granted);
  }

  package MockRes;
  sub new { my ($class, $has_row) = @_; bless { has_row => $has_row }, $class }
  sub can { my ($self, $method) = @_; return $method eq 'hash' ? 1 : 0 }
  sub hash { my ($self) = @_; return $self->{has_row} ? { id => 1 } : undef }
}

my $mock_db = MockDB->new;
is(utils::RBAC::has_permission($mock_db, $supporter_user, 'clients.view'), 1, 'Supporter with granted clients.view is allowed');
is(utils::RBAC::has_permission($mock_db, $supporter_user, 'clients.delete'), 0, 'Supporter without clients.delete is denied');
is(utils::RBAC::has_permission($mock_db, $supporter_user, 'staff.manage'), 0, 'Supporter without staff.manage is denied');

# 5. Mojolicious Application RBAC Route Protection Tests
my $t = Test::Mojo->new('AnchorBackend');
my $config = $t->app->config;

# Helper to generate test JWT token with specific role
sub make_test_token {
  my ($user_id, $role) = @_;
  return utils::JWT::generate($config, {
    user_id    => $user_id,
    email      => "user$user_id\@example.com",
    first_name => 'Test',
    last_name  => 'User',
    role       => $role,
  });
}

my $client_token = make_test_token(3, 'client');
my $admin_token  = make_test_token(1, 'admin');

# Unauthenticated access to admin routes -> 401
$t->post_ok('/api/admin/permissions' => json => {})
  ->status_is(401)
  ->json_is('/status' => 'error');

$t->post_ok('/api/admin/staff/list' => json => {})
  ->status_is(401)
  ->json_is('/status' => 'error');

$t->post_ok('/api/admin/clients/list' => json => {})
  ->status_is(401)
  ->json_is('/status' => 'error');

# Client access to admin routes -> 403 Forbidden
$t->post_ok('/api/admin/permissions' => { Authorization => "Bearer $client_token" } => json => {})
  ->status_is(403)
  ->json_is('/status' => 'error');

$t->post_ok('/api/admin/staff/list' => { Authorization => "Bearer $client_token" } => json => {})
  ->status_is(403)
  ->json_is('/status' => 'error');

$t->post_ok('/api/admin/staff/create' => { Authorization => "Bearer $client_token" } => json => { email => 'hack@anchor.com' })
  ->status_is(403)
  ->json_is('/status' => 'error');

$t->post_ok('/api/admin/clients/list' => { Authorization => "Bearer $client_token" } => json => {})
  ->status_is(403)
  ->json_is('/status' => 'error');

# Admin access to create staff with missing fields -> 400 Bad Request
$t->post_ok('/api/admin/staff/create' => { Authorization => "Bearer $admin_token" } => json => {})
  ->status_is(400)
  ->json_is('/status' => 'error');

done_testing();
