import { HttpClient } from '@angular/common/http';
import { TestBed } from '@angular/core/testing';
import { environment } from 'environments/environment';
import { of, throwError } from 'rxjs';
import { vi } from 'vitest';

import { OAuthSessionService } from './oauth-session.service';

// The expected absolute URL is derived from environment so the test stays in
// sync with configuration changes and never locks in the relative-URL bug.
const EXPECTED_SESSION_URL = `${environment.api.rest}/o-auth/session`;

describe('OAuthSessionService', () => {
  let service: OAuthSessionService;
  let httpPostSpy: ReturnType<typeof vi.fn>;

  beforeEach(() => {
    httpPostSpy = vi.fn();

    TestBed.configureTestingModule({
      providers: [
        OAuthSessionService,
        {
          provide: HttpClient,
          useValue: { post: httpPostSpy },
        },
      ],
    });

    service = TestBed.inject(OAuthSessionService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  describe('establishSession', () => {
    it('should POST to the absolute backend URL (environment.api.rest/o-auth/session) with the access token body and withCredentials: true', () => {
      httpPostSpy.mockReturnValue(of(null));

      service.establishSession('test-access-token').subscribe();

      expect(httpPostSpy).toHaveBeenCalledWith(
        EXPECTED_SESSION_URL,
        { access_token: 'test-access-token' },
        { withCredentials: true },
      );
    });

    it('URL must be absolute (not relative) — starts with http', () => {
      httpPostSpy.mockReturnValue(of(null));

      service.establishSession('test-access-token').subscribe();

      const calledUrl: string = httpPostSpy.mock.calls[0][0];
      expect(calledUrl.startsWith('http')).toBe(true);
    });

    it('should complete successfully on 204 (null body)', () =>
      new Promise<void>((resolve, reject) => {
        httpPostSpy.mockReturnValue(of(null));

        service.establishSession('some-token').subscribe({
          next: () => resolve(),
          error: (err: unknown) => reject(err),
        });
      }));

    it('should propagate errors from the HTTP call', () =>
      new Promise<void>((resolve) => {
        const mockError = new Error('Network error');
        httpPostSpy.mockReturnValue(throwError(() => mockError));

        service.establishSession('bad-token').subscribe({
          next: () => resolve(), // won't be called
          error: (err: unknown) => {
            expect(err).toBe(mockError);
            resolve();
          },
        });
      }));
  });
});
