import { Injector, runInInjectionContext } from '@angular/core';
import { environment } from '../../../../../environments/environment';
import type { ServerEndpoint } from '../../domain/models/server-directory.model';
import * as serverDirectoryStorageKeys from '../../infrastructure/constants/server-directory.infrastructure.constants';
import { ServerEndpointStorageService } from '../../infrastructure/services/server-endpoint-storage.service';
import { ServerEndpointStateService } from './server-endpoint-state.service';

function createLocalStorageMock(): Storage {
  const store = new Map<string, string>();

  return {
    get length(): number {
      return store.size;
    },
    clear(): void {
      store.clear();
    },
    getItem(key: string): string | null {
      return store.get(key) ?? null;
    },
    key(index: number): string | null {
      return [...store.keys()][index] ?? null;
    },
    removeItem(key: string): void {
      store.delete(key);
    },
    setItem(key: string, value: string): void {
      store.set(key, value);
    }
  };
}

Object.defineProperty(globalThis, 'localStorage', {
  value: createLocalStorageMock(),
  configurable: true
});

function getConfiguredDefaultServer(key: string): { key?: string; name?: string; url?: string } {
  const defaultServer = environment.defaultServers.find((server) => server.key === key);

  if (!defaultServer) {
    throw new Error(`Missing configured default server for key: ${key}`);
  }

  return defaultServer;
}

function seedStoredEndpoints(endpoints: ServerEndpoint[]): void {
  localStorage.setItem(serverDirectoryStorageKeys.SERVER_ENDPOINTS_STORAGE_KEY, JSON.stringify(endpoints));
}

function createService(): ServerEndpointStateService {
  const injector = Injector.create({
    providers: [
      {
        provide: ServerEndpointStorageService,
        useClass: ServerEndpointStorageService,
        deps: []
      }
    ]
  });

  return runInInjectionContext(injector, () => new ServerEndpointStateService());
}

function getRequiredDefaultEndpoint(service: ServerEndpointStateService, defaultKey: string | undefined): ServerEndpoint {
  const endpoint = service.servers().find((candidate) => candidate.defaultKey === defaultKey);

  if (!endpoint) {
    throw new Error(`Expected default endpoint for key: ${defaultKey ?? 'unknown'}`);
  }

  return endpoint;
}

describe('ServerEndpointStateService', () => {
  beforeEach(() => {
    localStorage.clear();
  });

  it('reactivates configured default endpoints unless the user disabled them', () => {
    const defaultServer = getConfiguredDefaultServer('toju-primary');

    seedStoredEndpoints([
      {
        id: 'default-server',
        name: 'Stored Default',
        url: defaultServer.url ?? '',
        isActive: false,
        isDefault: true,
        defaultKey: defaultServer.key,
        status: 'unknown'
      }
    ]);

    const service = createService();
    const endpoint = service.servers().find((candidate) => candidate.defaultKey === defaultServer.key);

    expect(endpoint?.isActive).toBe(true);
  });

  it('keeps a configured default endpoint inactive after the user turned it off', () => {
    const defaultServer = getConfiguredDefaultServer('toju-primary');

    seedStoredEndpoints([
      {
        id: 'default-server',
        name: 'Stored Default',
        url: defaultServer.url ?? '',
        isActive: true,
        isDefault: true,
        defaultKey: defaultServer.key,
        status: 'unknown'
      }
    ]);

    localStorage.setItem(
      serverDirectoryStorageKeys.DISABLED_DEFAULT_SERVER_KEYS_STORAGE_KEY,
      JSON.stringify([defaultServer.key])
    );

    const service = createService();
    const endpoint = service.servers().find((candidate) => candidate.defaultKey === defaultServer.key);

    expect(endpoint?.isActive).toBe(false);
  });

  it('keeps configured default endpoints active even when stored as incompatible unless the user disabled them', () => {
    const defaultServer = getConfiguredDefaultServer('toju-primary');

    seedStoredEndpoints([
      {
        id: 'default-server',
        name: 'Stored Default',
        url: defaultServer.url ?? '',
        isActive: false,
        isDefault: true,
        defaultKey: defaultServer.key,
        status: 'incompatible'
      }
    ]);

    const service = createService();
    const endpoint = service.servers().find((candidate) => candidate.defaultKey === defaultServer.key);

    expect(endpoint?.isActive).toBe(true);
    expect(endpoint?.status).toBe('incompatible');
  });

  it('does not deactivate configured default endpoints when compatibility checks fail', () => {
    const defaultServer = getConfiguredDefaultServer('toju-primary');
    const service = createService();
    const endpoint = getRequiredDefaultEndpoint(service, defaultServer.key);

    service.updateServerStatus(endpoint.id, 'incompatible');

    expect(service.servers().find((candidate) => candidate.id === endpoint.id)?.isActive).toBe(true);
  });

  it('resolves legacy https source URLs to the local http default endpoint on the same host', () => {
    const defaultServer = getConfiguredDefaultServer('default');
    const service = createService();
    const legacyHttpsUrl = defaultServer.url?.replace(/^http:\/\//, 'https://') ?? '';

    expect(service.findServerByUrl(legacyHttpsUrl)?.url).toBe(defaultServer.url);
  });

  it('persists turning a configured default endpoint off and back on', () => {
    const defaultServer = getConfiguredDefaultServer('toju-primary');
    const service = createService();
    const endpoint = getRequiredDefaultEndpoint(service, defaultServer.key);

    service.deactivateServer(endpoint.id);

    expect(service.servers().find((candidate) => candidate.id === endpoint.id)?.isActive).toBe(false);
    expect(JSON.parse(
      localStorage.getItem(serverDirectoryStorageKeys.DISABLED_DEFAULT_SERVER_KEYS_STORAGE_KEY) ?? '[]'
    )).toContain(defaultServer.key);

    service.setActiveServer(endpoint.id);

    expect(service.servers().find((candidate) => candidate.id === endpoint.id)?.isActive).toBe(true);
    expect(localStorage.getItem(serverDirectoryStorageKeys.DISABLED_DEFAULT_SERVER_KEYS_STORAGE_KEY)).toBeNull();
  });
});
