import {
  describe,
  it,
  expect,
  vi,
  beforeEach
} from 'vitest';
import { Injector, runInInjectionContext } from '@angular/core';
import { Router } from '@angular/router';
import { Store } from '@ngrx/store';

import { CreateServerDialogComponent } from './create-server-dialog.component';
import { RoomsActions } from '../../../../store/rooms/rooms.actions';
import { ServerDirectoryFacade } from '../../application/facades/server-directory.facade';
import { ViewportService } from '../../../../core/platform';

function installLocalStorageMock(): void {
  const store = new Map<string, string>();

  vi.stubGlobal('localStorage', {
    getItem: (key: string) => store.get(key) ?? null,
    setItem: (key: string, value: string) => store.set(key, String(value)),
    removeItem: (key: string) => store.delete(key),
    clear: () => store.clear(),
    key: (index: number) => Array.from(store.keys())[index] ?? null,
    get length() {
      return store.size;
    }
  });
}

function createHarness() {
  const dispatch = vi.fn();
  const store = { dispatch } as unknown as Store;
  const router = { navigate: vi.fn() } as unknown as Router;
  const serverDirectory = {
    activeServers: () => [{ id: 'ep-1', name: 'Local', url: 'https://local.test' }]
  } as unknown as ServerDirectoryFacade;
  const viewport = { isMobile: () => false } as unknown as ViewportService;
  const injector = Injector.create({
    providers: [
      CreateServerDialogComponent,
      { provide: Store, useValue: store },
      { provide: Router, useValue: router },
      { provide: ServerDirectoryFacade, useValue: serverDirectory },
      { provide: ViewportService, useValue: viewport }
    ]
  });
  const component = runInInjectionContext(injector, () => injector.get(CreateServerDialogComponent));

  return { component, dispatch, router };
}

describe('CreateServerDialogComponent', () => {
  beforeEach(() => {
    installLocalStorageMock();
    localStorage.setItem('metoyou_currentUserId', 'user-1');
  });

  it('defaults the signal endpoint to the first active endpoint', () => {
    const { component } = createHarness();

    expect(component.sourceId()).toBe('ep-1');
    expect(component.canCreate).toBe(false);
  });

  it('dispatches createRoom and emits created with the form values', () => {
    const { component, dispatch } = createHarness();
    const created = vi.fn();

    component.created.subscribe(created);

    component.name.set('My Server');
    component.description.set('A place');
    component.topic.set('gaming');
    component.isPrivate.set(true);
    component.password.set('secret');

    component.create();

    const action = dispatch.mock.calls.find(([entry]) => entry.type === RoomsActions.createRoom.type)?.[0];

    expect(action).toMatchObject({
      name: 'My Server',
      description: 'A place',
      topic: 'gaming',
      isPrivate: true,
      password: 'secret',
      sourceId: 'ep-1'
    });

    expect(created).toHaveBeenCalledTimes(1);
  });

  it('does not dispatch or emit created when the name is blank', () => {
    const { component, dispatch } = createHarness();
    const created = vi.fn();

    component.created.subscribe(created);

    component.create();

    expect(dispatch.mock.calls.some(([entry]) => entry.type === RoomsActions.createRoom.type)).toBe(false);
    expect(created).not.toHaveBeenCalled();
  });

  it('applies a category preset to the topic and toggles it off', () => {
    const { component } = createHarness();
    const gaming = component.categories[0];

    component.selectCategory(gaming);
    expect(component.topic()).toBe(gaming.topic);
    expect(component.selectedCategoryId()).toBe(gaming.id);

    component.selectCategory(gaming);
    expect(component.topic()).toBe('');
    expect(component.selectedCategoryId()).toBeNull();
  });

  it('emits cancelled when cancelled', () => {
    const { component } = createHarness();
    const cancelled = vi.fn();

    component.cancelled.subscribe(cancelled);

    component.cancel();

    expect(cancelled).toHaveBeenCalledTimes(1);
  });

  it('redirects to login and does not dispatch when not logged in', () => {
    const { component, dispatch, router } = createHarness();

    localStorage.removeItem('metoyou_currentUserId');
    component.name.set('My Server');

    component.create();

    expect(router.navigate).toHaveBeenCalledWith(['/login']);
    expect(dispatch.mock.calls.some(([entry]) => entry.type === RoomsActions.createRoom.type)).toBe(false);
  });
});
