import "reflect-metadata";
import { Neo4jAdapter } from "../../adapters/neo4j";
import { MetadataRegistry } from "../../core/MetadataRegistry";
import { SchemaBuilder } from "../../core/SchemaBuilder";
import { Entity, Property, Id, Labels } from "../../core/decorators";

// Simple test entity to isolate the property extraction bug
@Entity()
@Labels(['TestEntity'])
class SimpleTestEntity {
  @Id()
  id!: string;

  @Property({ required: true })
  name!: string;

  @Property()
  description?: string;

  @Property()
  count?: number;
}

describe('Neo4jAdapter Property Extraction Bug', () => {
  let adapter: Neo4jAdapter;
  let registry: MetadataRegistry;

  beforeAll(async () => {
    registry = new MetadataRegistry();
    const builder = new SchemaBuilder(registry);
    builder.registerEntities([SimpleTestEntity]);
    
    adapter = new Neo4jAdapter(registry, "bolt://127.0.0.1:7687", { 
      username: "neo4j", 
      password: "password" 
    });

    // Skip if Neo4j not available
    try {
      await adapter.runNativeQuery("RETURN 1");
    } catch (error) {
      console.log("⚠️  Skipping Neo4j tests - database not available");
      return;
    }
  });

  afterAll(async () => {
    try {
      // Clean up any test data
      await adapter.runNativeQuery("MATCH (n:TestEntity) DETACH DELETE n");
      if (typeof (adapter as any).close === 'function') {
        await (adapter as any).close();
      }
    } catch (error) {
      // Ignore cleanup errors
    }
  });

  beforeEach(async () => {
    // Skip if Neo4j not available
    try {
      await adapter.runNativeQuery("RETURN 1");
    } catch (error) {
      return;
    }
  });

  it('should extract properties correctly from entity', async () => {
    const entity = new SimpleTestEntity();
    entity.id = 'test-123';
    entity.name = 'Test Entity';
    entity.description = 'A test entity for property extraction';
    entity.count = 42;

    // Debug: Check what the schema reflector sees
    const entityType = entity.constructor;
    const schema = (adapter as any).reflector.getEntitySchema(entityType);
    console.log('🔍 Entity schema:', JSON.stringify(schema, null, 2));

    // Access the private method using type assertion
    const properties = (adapter as any).entityToProperties(entity);
    console.log('🔍 Extracted properties:', properties);

    expect(properties).toEqual({
      id: 'test-123',
      name: 'Test Entity',
      description: 'A test entity for property extraction',
      count: 42
    });
  });

  it('should handle entities with only id property', async () => {
    const entity = new SimpleTestEntity();
    entity.id = 'test-456';
    // Only set id, no other properties

    const properties = (adapter as any).entityToProperties(entity);

    // Should still return the id
    expect(properties).toEqual({
      id: 'test-456'
    });
  });

  it('should generate valid Cypher when entity has multiple properties', async () => {
    const entity = new SimpleTestEntity();
    entity.id = 'test-multi-' + Date.now();
    entity.name = 'Multi Property Test';
    entity.description = 'Testing multiple properties';
    entity.count = 99;

    // This should NOT throw a syntax error
    await expect(adapter.save(entity)).resolves.not.toThrow();

    // Verify it was saved
    const result = await adapter.query(SimpleTestEntity, { id: entity.id });
    expect(result).toBeTruthy();
    expect(result).toMatchObject({
      id: entity.id,
      name: 'Multi Property Test',
      description: 'Testing multiple properties',
      count: 99
    });

    // Clean up
    await adapter.delete(SimpleTestEntity, entity.id);
  });

  it('should generate valid Cypher when entity has only id (the bug case)', async () => {
    const entity = new SimpleTestEntity();
    entity.id = 'test-id-only-' + Date.now();
    // Intentionally leave other properties undefined to trigger the bug

    // This should NOT throw a syntax error - this is the bug we're fixing!
    await expect(adapter.save(entity)).resolves.not.toThrow();

    // Verify it was saved (even with minimal data)
    const result = await adapter.query(SimpleTestEntity, { id: entity.id });
    expect(result).toBeTruthy();
    expect(result?.id).toBe(entity.id);

    // Clean up
    await adapter.delete(SimpleTestEntity, entity.id);
  });

  it('should handle entities with some undefined properties', async () => {
    const entity = new SimpleTestEntity();
    entity.id = 'test-partial-' + Date.now();
    entity.name = 'Partial Entity';
    // description and count are undefined

    await expect(adapter.save(entity)).resolves.not.toThrow();

    const result = await adapter.query(SimpleTestEntity, { id: entity.id });
    expect(result).toBeTruthy();
    expect(result).toMatchObject({
      id: entity.id,
      name: 'Partial Entity'
    });
    // undefined properties shouldn't be in the result
    expect(result?.description).toBeUndefined();
    expect(result?.count).toBeUndefined();

    // Clean up
    await adapter.delete(SimpleTestEntity, entity.id);
  });
});