import { TestBed } from '@angular/core/testing';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { TitleRegistryService } from './title-registry.service';

describe('TitleRegistryService', () => {
  let service: TitleRegistryService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [TitleRegistryService],
    });

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

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

  describe('register', () => {
    it('should register and immediately execute the updater', () => {
      // Arrange
      const updater = vi.fn();

      // Act
      service.register(updater);

      // Assert
      expect(updater).toHaveBeenCalledOnce();
    });

    it('should return a unique symbol for each registration', () => {
      // Arrange
      const updater1 = vi.fn();
      const updater2 = vi.fn();

      // Act
      const id1 = service.register(updater1);
      const id2 = service.register(updater2);

      // Assert
      expect(id1).not.toBe(id2);
      expect(typeof id1).toBe('symbol');
      expect(typeof id2).toBe('symbol');
    });

    it('should make the last registered updater the active one', () => {
      // Arrange
      const updater1 = vi.fn();
      const updater2 = vi.fn();
      const updater3 = vi.fn();

      const id1 = service.register(updater1);
      const id2 = service.register(updater2);
      const id3 = service.register(updater3);

      vi.clearAllMocks();

      // Act - Only the last registered should execute
      service.updateIfActive(id1, updater1);
      service.updateIfActive(id2, updater2);
      service.updateIfActive(id3, updater3);

      // Assert
      expect(updater1).not.toHaveBeenCalled();
      expect(updater2).not.toHaveBeenCalled();
      expect(updater3).toHaveBeenCalledOnce();
    });
  });

  describe('unregister', () => {
    it('should remove the registration', () => {
      // Arrange
      const updater = vi.fn();
      const id = service.register(updater);
      vi.clearAllMocks();

      // Act
      service.unregister(id);
      service.updateIfActive(id, updater);

      // Assert - Updater should not be called after unregistration
      expect(updater).not.toHaveBeenCalled();
    });

    it('should restore the previous registration when active one is unregistered', () => {
      // Arrange
      const updater1 = vi.fn();
      const updater2 = vi.fn();

      const id1 = service.register(updater1);
      const id2 = service.register(updater2);

      vi.clearAllMocks();

      // Act - Unregister the active one (id2)
      service.unregister(id2);

      // Assert - updater1 should have been called to restore title
      expect(updater1).toHaveBeenCalledOnce();
      expect(updater2).not.toHaveBeenCalled();
    });

    it('should make the previous registration active after unregistering current active', () => {
      // Arrange
      const updater1 = vi.fn();
      const updater2 = vi.fn();

      const id1 = service.register(updater1);
      const id2 = service.register(updater2);

      service.unregister(id2);
      vi.clearAllMocks();

      // Act - id1 should now be active
      service.updateIfActive(id1, updater1);
      service.updateIfActive(id2, updater2);

      // Assert
      expect(updater1).toHaveBeenCalledOnce();
      expect(updater2).not.toHaveBeenCalled();
    });

    it('should handle unregistering a non-active registration', () => {
      // Arrange
      const updater1 = vi.fn();
      const updater2 = vi.fn();
      const updater3 = vi.fn();

      const id1 = service.register(updater1);
      const id2 = service.register(updater2);
      const id3 = service.register(updater3);

      vi.clearAllMocks();

      // Act - Unregister the middle one (not active)
      service.unregister(id2);

      // Assert - id3 should still be active, updater2 should not be called
      service.updateIfActive(id3, updater3);
      expect(updater2).not.toHaveBeenCalled();
      expect(updater3).toHaveBeenCalledOnce();
    });

    it('should handle unregistering when no registrations remain', () => {
      // Arrange
      const updater = vi.fn();
      const id = service.register(updater);
      vi.clearAllMocks();

      // Act
      service.unregister(id);

      // Assert - No errors should occur
      service.updateIfActive(id, updater);
      expect(updater).not.toHaveBeenCalled();
    });
  });

  describe('updateIfActive', () => {
    it('should execute updater only if it is the active registration', () => {
      // Arrange
      const updater1 = vi.fn();
      const updater2 = vi.fn();

      const id1 = service.register(updater1);
      const id2 = service.register(updater2); // This becomes active

      vi.clearAllMocks();

      // Act
      service.updateIfActive(id1, updater1);
      service.updateIfActive(id2, updater2);

      // Assert
      expect(updater1).not.toHaveBeenCalled(); // Not active
      expect(updater2).toHaveBeenCalledOnce(); // Active
    });

    it('should not execute updater for unregistered id', () => {
      // Arrange
      const updater = vi.fn();
      const id = service.register(updater);

      service.unregister(id);
      vi.clearAllMocks();

      // Act
      service.updateIfActive(id, updater);

      // Assert
      expect(updater).not.toHaveBeenCalled();
    });
  });

  describe('nested component scenario', () => {
    it('should handle parent → child → back to parent flow', () => {
      // Arrange - Simulate parent component
      const parentUpdater = vi.fn(() => 'Parent Title');
      const parentId = service.register(parentUpdater);

      vi.clearAllMocks();

      // Act 1 - Child component registers
      const childUpdater = vi.fn(() => 'Child Title');
      const childId = service.register(childUpdater);

      // Assert 1 - Child is active
      expect(childUpdater).toHaveBeenCalledOnce();

      vi.clearAllMocks();

      // Act 2 - Child component is destroyed
      service.unregister(childId);

      // Assert 2 - Parent title is restored
      expect(parentUpdater).toHaveBeenCalledOnce();

      vi.clearAllMocks();

      // Act 3 - Parent updates should work
      service.updateIfActive(parentId, parentUpdater);

      // Assert 3 - Parent is active again
      expect(parentUpdater).toHaveBeenCalledOnce();
    });

    it('should handle three-level nesting (grandparent → parent → child)', () => {
      // Arrange
      const grandparentUpdater = vi.fn();
      const parentUpdater = vi.fn();
      const childUpdater = vi.fn();

      const grandparentId = service.register(grandparentUpdater);
      const parentId = service.register(parentUpdater);
      const childId = service.register(childUpdater);

      vi.clearAllMocks();

      // Act 1 - Verify child is active
      service.updateIfActive(childId, childUpdater);
      expect(childUpdater).toHaveBeenCalledOnce();

      // Act 2 - Remove child, parent should become active
      service.unregister(childId);
      expect(parentUpdater).toHaveBeenCalledOnce();

      vi.clearAllMocks();

      // Act 3 - Remove parent, grandparent should become active
      service.unregister(parentId);
      expect(grandparentUpdater).toHaveBeenCalledOnce();
    });
  });
});
