import { SessionService } from './session.service';
import mockfs from 'mock-fs';
import fs from 'node:fs';
import yaml from 'yaml';
import { LoginError } from '../cores/errors/login-error';

const mockPingServer = jest.fn(() => Promise.resolve({ data: { res: 'pong' } }));
const mockUserInfo = jest.fn(() => Promise.resolve({ data: { email: 'admin@example.com' } }));

jest.mock('../api/open-api', () => {
  return {
    __esModule: true,
    ...jest.requireActual('../api/open-api'),
    UserApi: jest.fn().mockImplementation(() => {
      return { getMyUserInfo: mockUserInfo };
    }),
    ServerInfoApi: jest.fn().mockImplementation(() => {
      return { pingServer: mockPingServer };
    }),
  };
});

describe('SessionService', () => {
  let sessionService: SessionService;
  beforeAll(() => {
    // Write a dummy output before mock-fs to prevent some annoying errors
    console.log();
  });

  beforeEach(() => {
    const configDir = '/config';
    sessionService = new SessionService(configDir);
  });

  it('should connect to immich', async () => {
    mockfs({
      '/config/auth.yml': 'apiKey: pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg\ninstanceUrl: https://test/api',
    });
    await sessionService.connect();
    expect(mockPingServer).toHaveBeenCalledTimes(1);
  });

  it('should error if no auth file exists', async () => {
    mockfs();
    await sessionService.connect().catch((error) => {
      expect(error.message).toEqual('No auth file exist. Please login first');
    });
  });

  it('should error if auth file is missing instance URl', async () => {
    mockfs({
      '/config/auth.yml': 'foo: pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg\napiKey: https://test/api',
    });
    await sessionService.connect().catch((error) => {
      expect(error).toBeInstanceOf(LoginError);
      expect(error.message).toEqual('Instance URL missing in auth config file /config/auth.yml');
    });
  });

  it('should error if auth file is missing api key', async () => {
    mockfs({
      '/config/auth.yml': 'instanceUrl: pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg\nbar: https://test/api',
    });
    await sessionService.connect().catch((error) => {
      expect(error).toBeInstanceOf(LoginError);
      expect(error.message).toEqual('API key missing in auth config file /config/auth.yml');
    });
  });

  it('should create auth file when logged in', async () => {
    mockfs();

    await sessionService.keyLogin('https://test/api', 'pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg');

    const data: string = await fs.promises.readFile('/config/auth.yml', 'utf8');
    const authConfig = yaml.parse(data);
    expect(authConfig.instanceUrl).toBe('https://test/api');
    expect(authConfig.apiKey).toBe('pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg');
  });

  it('should delete auth file when logging out', async () => {
    mockfs({
      '/config/auth.yml': 'apiKey: pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg\ninstanceUrl: https://test/api',
    });
    await sessionService.logout();

    await fs.promises.access('/auth.yml', fs.constants.F_OK).catch((error) => {
      expect(error.message).toContain('ENOENT');
    });
  });

  afterEach(() => {
    mockfs.restore();
  });
});
