/**
 * guards.spec.js
 *
 * Cobre as funções puras extraídas de guards.js. O beforeEach inteiro
 * (applyGuards) não é unit-testado aqui — exige mock de pinia + supabase +
 * vue-router. Cobertura completa de navegação fica para testes E2E (T#10).
 *
 * Mocks: supabase e stores são mockados via vi.mock para permitir que o
 * módulo guards.js carregue sem explodir — mas os testes focam nas puras.
 */
import { describe, it, expect, vi, beforeEach } from 'vitest';

// Mocks antes do import do SUT
vi.mock('@/lib/supabase/client', () => ({
    supabase: {
        auth: {
            getUser: vi.fn().mockResolvedValue({ data: { user: null }, error: null }),
            onAuthStateChange: vi.fn().mockReturnValue({ data: { subscription: { unsubscribe: vi.fn() } } })
        },
        from: vi.fn().mockReturnThis(),
        select: vi.fn().mockReturnThis(),
        eq: vi.fn().mockReturnThis(),
        single: vi.fn().mockResolvedValue({ data: null, error: null }),
        maybeSingle: vi.fn().mockResolvedValue({ data: null, error: null })
    }
}));

vi.mock('@/stores/tenantStore', () => ({ useTenantStore: vi.fn() }));
vi.mock('@/stores/entitlementsStore', () => ({ useEntitlementsStore: vi.fn() }));
vi.mock('@/stores/tenantFeaturesStore', () => ({ useTenantFeaturesStore: vi.fn() }));
vi.mock('@/stores/menuStore', () => ({ useMenuStore: vi.fn() }));
vi.mock('@/navigation', () => ({ getMenuByRole: vi.fn().mockReturnValue([]) }));
vi.mock('@/utils/upgradeContext', () => ({ buildUpgradeUrl: vi.fn() }));
vi.mock('@/support/supportLogger', () => ({
    logGuard: vi.fn(),
    logError: vi.fn(),
    logPerf: vi.fn(() => () => {})
}));
vi.mock('@/composables/useAjuda', () => ({ resetAjuda: vi.fn() }));
vi.mock('@/app/session', () => ({
    sessionUser: { value: null },
    sessionReady: { value: true },
    sessionRefreshing: { value: false },
    initSession: vi.fn().mockResolvedValue(undefined)
}));
vi.mock('@/router/accessRedirects', () => ({
    denyByRole: vi.fn(({ to }) => ({ path: '/pages/access' })),
    denyByPlan: vi.fn()
}));

const { isUuid, roleToPath, shouldLoadEntitlements, matchesRoles } = await import('../guards.js');

describe('isUuid — validação UUID v1-5 case insensitive', () => {
    it('aceita UUIDs v4 canônicos', () => {
        expect(isUuid('550e8400-e29b-41d4-a716-446655440000')).toBe(true);
        expect(isUuid('f47ac10b-58cc-4372-a567-0e02b2c3d479')).toBe(true);
    });

    it('aceita uppercase', () => {
        expect(isUuid('550E8400-E29B-41D4-A716-446655440000')).toBe(true);
    });

    it('rejeita strings vazias/null', () => {
        expect(isUuid('')).toBe(false);
        expect(isUuid(null)).toBe(false);
        expect(isUuid(undefined)).toBe(false);
    });

    it('rejeita formatos truncados/sem hífen', () => {
        expect(isUuid('550e8400e29b41d4a716446655440000')).toBe(false); // sem hífen
        expect(isUuid('550e8400-e29b-41d4-a716')).toBe(false);          // truncado
        expect(isUuid('not-a-uuid')).toBe(false);
    });

    it('rejeita version 6+ e variant inválido', () => {
        // variant precisa começar com 8, 9, a ou b
        expect(isUuid('550e8400-e29b-41d4-c716-446655440000')).toBe(false);
        // version precisa ser 1-5
        expect(isUuid('550e8400-e29b-61d4-a716-446655440000')).toBe(false);
    });
});

describe('roleToPath — mapa de role → home path', () => {
    it('mapeia clinic_admin/tenant_admin/admin → /admin', () => {
        expect(roleToPath('clinic_admin')).toBe('/admin');
        expect(roleToPath('tenant_admin')).toBe('/admin');
        expect(roleToPath('admin')).toBe('/admin');
    });

    it('mapeia therapist → /therapist', () => {
        expect(roleToPath('therapist')).toBe('/therapist');
    });

    it('mapeia supervisor → /supervisor', () => {
        expect(roleToPath('supervisor')).toBe('/supervisor');
    });

    it('mapeia patient/portal_user → /portal', () => {
        expect(roleToPath('patient')).toBe('/portal');
        expect(roleToPath('portal_user')).toBe('/portal');
    });

    it('mapeia saas_admin → /saas', () => {
        expect(roleToPath('saas_admin')).toBe('/saas');
    });

    it('fallback para /', () => {
        expect(roleToPath('unknown')).toBe('/');
        expect(roleToPath('')).toBe('/');
        expect(roleToPath(null)).toBe('/');
    });
});

describe('shouldLoadEntitlements — heurística de carga de entitlements', () => {
    it('retorna false sem tenantId', () => {
        expect(shouldLoadEntitlements({ loaded: true, activeTenantId: 't1' }, null)).toBe(false);
        expect(shouldLoadEntitlements({ loaded: true, activeTenantId: 't1' }, undefined)).toBe(false);
    });

    it('retorna true se nunca carregou', () => {
        expect(shouldLoadEntitlements({ loaded: false }, 't1')).toBe(true);
        expect(shouldLoadEntitlements({}, 't1')).toBe(true); // loaded undefined
    });

    it('retorna true se tenant mudou', () => {
        expect(shouldLoadEntitlements({ loaded: true, activeTenantId: 't1' }, 't2')).toBe(true);
        expect(shouldLoadEntitlements({ loaded: true, tenantId: 't1' }, 't2')).toBe(true);
    });

    it('retorna false se já carregado pro mesmo tenant', () => {
        expect(shouldLoadEntitlements({ loaded: true, activeTenantId: 't1' }, 't1')).toBe(false);
        expect(shouldLoadEntitlements({ loaded: true, tenantId: 't1' }, 't1')).toBe(false);
    });

    it('loaded=true sem tenantId anterior ainda exige carga se target existe', () => {
        // edge case: loaded=true mas sem activeTenantId (cenário inconsistente) →
        // heurística conservadora retorna false (não recarrega)
        expect(shouldLoadEntitlements({ loaded: true, activeTenantId: null }, 't1')).toBe(false);
    });
});

describe('matchesRoles — RBAC com normalização de aliases', () => {
    it('retorna true se lista vazia/null (sem restrição)', () => {
        expect(matchesRoles(null, 'therapist')).toBe(true);
        expect(matchesRoles([], 'therapist')).toBe(true);
        expect(matchesRoles(undefined, 'any')).toBe(true);
    });

    it('bate role canônico', () => {
        expect(matchesRoles(['therapist'], 'therapist')).toBe(true);
        expect(matchesRoles(['clinic_admin', 'therapist'], 'therapist')).toBe(true);
    });

    it('bate via normalização — tenant_admin contexto therapist tenant_admin equiv clinic_admin default', () => {
        // Sem kind, tenant_admin vira clinic_admin
        expect(matchesRoles(['clinic_admin'], 'tenant_admin')).toBe(true);
        expect(matchesRoles(['admin'], 'tenant_admin')).toBe(true);
    });

    it('rejeita role não listado', () => {
        expect(matchesRoles(['therapist'], 'clinic_admin')).toBe(false);
        expect(matchesRoles(['saas_admin'], 'therapist')).toBe(false);
    });

    it('ignora não-arrays', () => {
        expect(matchesRoles('therapist', 'therapist')).toBe(true);
        expect(matchesRoles({}, 'therapist')).toBe(true);
    });
});
