import { Neo4jAdapter } from '../../adapters/neo4j';
import { MetadataRegistry } from '../../core/MetadataRegistry';
import { Entity, Property, Id, Labels, ManyToOne, ManyToMany } from '../../core/decorators';
import { TestRegistryProvider } from '../../core/TestRegistryProvider';

describe('Neo4jAdapter - Dynamic Imports', () => {
  let adapter: Neo4jAdapter;
  let registry: MetadataRegistry;

  beforeEach(() => {
    // Use test registry provider
    const testProvider = new TestRegistryProvider();
    registry = testProvider.getRegistry();
    adapter = new Neo4jAdapter(registry, 'bolt://localhost:7687');
  });

  // Test entities with dynamic imports (like our real entities)
  @Entity()
  @Labels(['User'])
  class TestUser {
    @Id()
    id!: string;

    @Property()
    name!: string;

    @Property()
    email!: string;
  }

  @Entity()
  @Labels(['Space'])
  class TestSpace {
    @Id()
    id!: string;

    @Property()
    name!: string;

    @Property()
    spaceType!: string;

    // Dynamic import relationship (this is what's failing)
    @ManyToOne({ target: () => TestUser, inverse: 'ownedSpaces' })
    owner?: TestUser;

    // Dynamic import many-to-many relationship
    @ManyToMany({ target: () => TestUser, inverse: 'participatingSpaces' })
    participants!: TestUser[];
  }

  describe('Dynamic Import Relationships', () => {
    it('should handle ManyToOne relationships with function targets', async () => {
      // Register entities
      registry.registerEntity(TestUser);
      registry.registerEntity(TestSpace);

      // Create a user
      const user = new TestUser();
      user.id = 'user-1';
      user.name = 'Test User';
      user.email = 'test@example.com';

      // Create a space with the user as owner
      const space = new TestSpace();
      space.id = 'space-1';
      space.name = 'Test Space';
      space.spaceType = 'chat';
      space.owner = user;

      // This should not throw
      await expect(adapter.save(space)).resolves.not.toThrow();
    });

    it('should handle ManyToMany relationships with function targets', async () => {
      // Register entities
      registry.registerEntity(TestUser);
      registry.registerEntity(TestSpace);

      // Create a user
      const user = new TestUser();
      user.id = 'user-1';
      user.name = 'Test User';
      user.email = 'test@example.com';

      // Create a space with the user as participant
      const space = new TestSpace();
      space.id = 'space-1';
      space.name = 'Test Space';
      space.spaceType = 'chat';
      space.participants = [user];

      // This should not throw
      await expect(adapter.save(space)).resolves.not.toThrow();
    });

    it('should handle both ManyToOne and ManyToMany relationships together', async () => {
      // Register entities
      registry.registerEntity(TestUser);
      registry.registerEntity(TestSpace);

      // Create users
      const owner = new TestUser();
      owner.id = 'owner-1';
      owner.name = 'Owner';
      owner.email = 'owner@example.com';

      const participant = new TestUser();
      participant.id = 'participant-1';
      participant.name = 'Participant';
      participant.email = 'participant@example.com';

      // Create a space with both owner and participants
      const space = new TestSpace();
      space.id = 'space-1';
      space.name = 'Test Space';
      space.spaceType = 'chat';
      space.owner = owner;
      space.participants = [owner, participant];

      // This should not throw
      await expect(adapter.save(space)).resolves.not.toThrow();
    });

    it('should get entity labels correctly for dynamic import targets', () => {
      // Register entities
      registry.registerEntity(TestUser);
      registry.registerEntity(TestSpace);

      // Test that we can get labels for the target types
      const userLabels = (adapter as any).getEntityLabels(TestUser);
      const spaceLabels = (adapter as any).getEntityLabels(TestSpace);

      expect(userLabels).toEqual(['User']);
      expect(spaceLabels).toEqual(['Space']);
    });
  });

  describe('Promise Resolution', () => {
    it('should handle Promise-based targets (simulating dynamic imports)', async () => {
      // Register entities
      registry.registerEntity(TestUser);
      registry.registerEntity(TestSpace);

      // Create a Promise-based target (this simulates what happens with dynamic imports)
      const promiseTarget = Promise.resolve(TestUser);

      // Test that getEntityLabels can handle Promise targets
      const labels = await (adapter as any).getEntityLabels(promiseTarget);
      expect(labels).toEqual(['User']);
    });

    it('should handle mixed Promise and non-Promise targets', async () => {
      // Register entities
      registry.registerEntity(TestUser);
      registry.registerEntity(TestSpace);

      // Test regular class
      const regularLabels = (adapter as any).getEntityLabels(TestUser);
      expect(regularLabels).toEqual(['User']);

      // Test Promise-wrapped class
      const promiseTarget = Promise.resolve(TestUser);
      const promiseLabels = await (adapter as any).getEntityLabels(promiseTarget);
      expect(promiseLabels).toEqual(['User']);
    });
  });
});