import { firstValueFrom, of } from 'rxjs';
import {
  describe,
  expect,
  it
} from 'vitest';

import { UsersActions } from '../../../../store/users/users.actions';
import {
  buildLoginReturnQueryParams,
  resolveSafeReturnUrl,
  waitForAuthenticationOutcome
} from './auth-navigation.rules';

describe('resolveSafeReturnUrl', () => {
  it('returns the requested in-app path unchanged', () => {
    expect(resolveSafeReturnUrl('/servers')).toBe('/servers');
    expect(resolveSafeReturnUrl('/room/abc')).toBe('/room/abc');
  });

  it('unwraps nested login returnUrl chains to the original destination', () => {
    const nested = '/login?returnUrl=%2Flogin%3FreturnUrl%3D%252Fservers';

    expect(resolveSafeReturnUrl(nested)).toBe('/servers');
    expect(resolveSafeReturnUrl(`/login?returnUrl=${encodeURIComponent(nested)}`)).toBe('/servers');
  });

  it('falls back to dashboard for auth-only return targets', () => {
    expect(resolveSafeReturnUrl('/login')).toBe('/dashboard');
    expect(resolveSafeReturnUrl('/register')).toBe('/dashboard');
    expect(resolveSafeReturnUrl(null)).toBe('/dashboard');
  });

  it('rejects open redirects and protocol-relative paths', () => {
    expect(resolveSafeReturnUrl('//evil.example/phish')).toBe('/dashboard');
    expect(resolveSafeReturnUrl('https://evil.example/phish')).toBe('/dashboard');
  });
});

describe('buildLoginReturnQueryParams', () => {
  it('preserves a safe destination when redirecting from protected routes', () => {
    expect(buildLoginReturnQueryParams('/servers')).toEqual({ returnUrl: '/servers' });
  });

  it('does not nest login returnUrl values', () => {
    expect(buildLoginReturnQueryParams('/login?returnUrl=%2Fservers')).toEqual({ returnUrl: '/servers' });
    expect(buildLoginReturnQueryParams('/login?returnUrl=%2Flogin%3FreturnUrl%3D%252Fservers')).toEqual({
      returnUrl: '/servers'
    });
  });

  it('omits returnUrl when there is no meaningful destination', () => {
    expect(buildLoginReturnQueryParams('/login')).toEqual({});
    expect(buildLoginReturnQueryParams('/register')).toEqual({});
  });
});

describe('waitForAuthenticationOutcome', () => {
  it('resolves when authentication storage preparation succeeds', async () => {
    const user = {
      id: 'user-1',
      oderId: 'user-1',
      username: 'alice',
      displayName: 'Alice',
      status: 'online' as const,
      role: 'member' as const,
      joinedAt: 1
    };
    const outcome = await firstValueFrom(waitForAuthenticationOutcome(of(
      UsersActions.setCurrentUser({ user })
    )));

    expect(outcome).toEqual({ kind: 'success', user });
  });

  it('resolves with a failure when authentication storage preparation fails', async () => {
    const outcome = await firstValueFrom(waitForAuthenticationOutcome(of(
      UsersActions.loadCurrentUserFailure({ error: 'Failed to prepare local user state.' })
    )));

    expect(outcome).toEqual({
      kind: 'failure',
      error: 'Failed to prepare local user state.'
    });
  });
});
