import { describe, it, expect } from 'vitest';
import { importDBMLToDiagram } from '../dbml-import';
import { generateDBMLFromDiagram } from '../../dbml-export/dbml-export';
import { applyDBMLChanges } from '../../apply-dbml/apply-dbml';
import { DatabaseType } from '@/lib/domain/database-type';
import type { Diagram } from '@/lib/domain/diagram';

describe('DBML Schema Handling - Fantasy Realm Database', () => {
    describe('MySQL - No Schema Support', () => {
        it('should not add public schema for MySQL databases', async () => {
            // Fantasy realm DBML with tables that would typically get 'public' schema
            const dbmlContent = `
                Table "wizards" {
                    "id" bigint [pk]
                    "name" varchar(100)
                    "magic_level" int
                    "Yes" varchar(10) // Reserved DBML keyword
                    "No" varchar(10)  // Reserved DBML keyword
                }

                Table "dragons" {
                    "id" bigint [pk]
                    "name" varchar(100)
                    "treasure_count" int
                    "is_friendly" boolean
                }

                Table "spells" {
                    "id" bigint [pk]
                    "spell_name" varchar(200)
                    "wizard_id" bigint
                    "power_level" int
                }

                Ref: "spells"."wizard_id" > "wizards"."id"
            `;

            const diagram = await importDBMLToDiagram(dbmlContent, {
                databaseType: DatabaseType.MYSQL,
            });

            // Verify no 'public' schema was added
            expect(diagram.tables).toBeDefined();
            diagram.tables?.forEach((table) => {
                expect(table.schema).toBe('');
                console.log(
                    `✓ Table "${table.name}" has no schema (MySQL behavior)`
                );
            });

            // Check specific tables
            const wizardsTable = diagram.tables?.find(
                (t) => t.name === 'wizards'
            );
            expect(wizardsTable).toBeDefined();
            expect(wizardsTable?.schema).toBe('');

            // Check that reserved keywords are preserved as field names
            const yesField = wizardsTable?.fields.find((f) => f.name === 'Yes');
            const noField = wizardsTable?.fields.find((f) => f.name === 'No');
            expect(yesField).toBeDefined();
            expect(noField).toBeDefined();
        });

        it('should preserve IDs when re-importing DBML (no false changes)', async () => {
            // Create initial diagram
            const initialDBML = `
                Table "kingdoms" {
                    "id" bigint [pk]
                    "name" varchar(100)
                    "ruler" varchar(100)
                    "Yes" varchar(10) // Acceptance status
                    "No" varchar(10)  // Rejection status
                }

                Table "knights" {
                    "id" bigint [pk]
                    "name" varchar(100)
                    "kingdom_id" bigint
                    "honor_points" int
                }

                Ref: "knights"."kingdom_id" > "kingdoms"."id"
            `;

            // Import initial DBML
            const sourceDiagram = await importDBMLToDiagram(initialDBML, {
                databaseType: DatabaseType.MYSQL,
            });

            // Export to DBML
            const exported = generateDBMLFromDiagram(sourceDiagram);

            // Re-import the exported DBML (simulating edit mode)
            const reimportedDiagram = await importDBMLToDiagram(
                exported.inlineDbml,
                {
                    databaseType: DatabaseType.MYSQL,
                }
            );

            // Apply DBML changes (should preserve IDs)
            const targetDiagram: Diagram = {
                ...sourceDiagram,
                tables: reimportedDiagram.tables,
                relationships: reimportedDiagram.relationships,
                customTypes: reimportedDiagram.customTypes,
            };

            const resultDiagram = applyDBMLChanges({
                sourceDiagram,
                targetDiagram,
            });

            // Verify IDs are preserved
            expect(resultDiagram.tables?.length).toBe(
                sourceDiagram.tables?.length
            );

            sourceDiagram.tables?.forEach((sourceTable, idx) => {
                const resultTable = resultDiagram.tables?.[idx];
                expect(resultTable?.id).toBe(sourceTable.id);
                expect(resultTable?.name).toBe(sourceTable.name);

                // Check field IDs are preserved
                sourceTable.fields.forEach((sourceField, fieldIdx) => {
                    const resultField = resultTable?.fields[fieldIdx];
                    expect(resultField?.id).toBe(sourceField.id);
                    expect(resultField?.name).toBe(sourceField.name);
                });
            });

            console.log('✓ All IDs preserved after DBML round-trip');
        });
    });

    describe('PostgreSQL - Schema Support', () => {
        it('should handle schemas correctly for PostgreSQL', async () => {
            // Fantasy realm with multiple schemas
            const dbmlContent = `
                Table "public"."heroes" {
                    "id" bigint [pk]
                    "name" varchar(100)
                    "class" varchar(50)
                }

                Table "private"."secret_quests" {
                    "id" bigint [pk]
                    "quest_name" varchar(200)
                    "hero_id" bigint
                }

                Table "artifacts" {
                    "id" bigint [pk]
                    "name" varchar(100)
                    "power" int
                }

                Ref: "private"."secret_quests"."hero_id" > "public"."heroes"."id"
            `;

            const diagram = await importDBMLToDiagram(dbmlContent, {
                databaseType: DatabaseType.POSTGRESQL,
            });

            // Check schemas are preserved correctly
            const heroesTable = diagram.tables?.find(
                (t) => t.name === 'heroes'
            );
            expect(heroesTable?.schema).toBe(''); // 'public' should be converted to empty

            const secretQuestsTable = diagram.tables?.find(
                (t) => t.name === 'secret_quests'
            );
            expect(secretQuestsTable?.schema).toBe('private'); // Other schemas preserved

            const artifactsTable = diagram.tables?.find(
                (t) => t.name === 'artifacts'
            );
            expect(artifactsTable?.schema).toBe(''); // No schema = empty string
        });

        it('should rename reserved keywords for PostgreSQL', async () => {
            const dbmlContent = `
                Table "magic_items" {
                    "id" bigint [pk]
                    "name" varchar(100)
                    "Order" int // SQL keyword
                    "Yes" varchar(10) // DBML keyword
                    "No" varchar(10)  // DBML keyword
                }
            `;

            const diagram = await importDBMLToDiagram(dbmlContent, {
                databaseType: DatabaseType.POSTGRESQL,
            });

            const exported = generateDBMLFromDiagram(diagram);

            // For PostgreSQL, keywords should be renamed in export
            expect(exported.standardDbml).toContain('Order_field');
            expect(exported.standardDbml).toContain('Yes_field');
            expect(exported.standardDbml).toContain('No_field');
        });
    });

    describe('Public Schema Handling - The Core Fix', () => {
        it('should strip public schema for MySQL to prevent ID mismatch', async () => {
            // This test verifies the core fix - that 'public' schema is converted to empty string
            const dbmlWithPublicSchema = `
                Table "public"."enchanted_items" {
                    "id" bigint [pk]
                    "item_name" varchar(100)
                    "power" int
                }

                Table "public"."spell_books" {
                    "id" bigint [pk]
                    "title" varchar(200)
                    "author" varchar(100)
                }
            `;

            const mysqlDiagram = await importDBMLToDiagram(
                dbmlWithPublicSchema,
                {
                    databaseType: DatabaseType.MYSQL,
                }
            );

            // For MySQL, 'public' schema should be stripped
            mysqlDiagram.tables?.forEach((table) => {
                expect(table.schema).toBe('');
                console.log(
                    `✓ MySQL: Table "${table.name}" has no schema (public was stripped)`
                );
            });

            // Now test with PostgreSQL - public should also be stripped (it's the default)
            const pgDiagram = await importDBMLToDiagram(dbmlWithPublicSchema, {
                databaseType: DatabaseType.POSTGRESQL,
            });

            pgDiagram.tables?.forEach((table) => {
                expect(table.schema).toBe('');
                console.log(
                    `✓ PostgreSQL: Table "${table.name}" has no schema (public is default)`
                );
            });
        });

        it('should preserve non-public schemas', async () => {
            const dbmlWithCustomSchema = `
                Table "fantasy"."magic_users" {
                    "id" bigint [pk]
                    "name" varchar(100)
                    "class" varchar(50)
                }

                Table "adventure"."quests" {
                    "id" bigint [pk]
                    "title" varchar(200)
                    "reward" int
                }
            `;

            const diagram = await importDBMLToDiagram(dbmlWithCustomSchema, {
                databaseType: DatabaseType.POSTGRESQL,
            });

            // Non-public schemas should be preserved
            const magicTable = diagram.tables?.find(
                (t) => t.name === 'magic_users'
            );
            const questTable = diagram.tables?.find((t) => t.name === 'quests');

            expect(magicTable?.schema).toBe('fantasy');
            expect(questTable?.schema).toBe('adventure');
            console.log('✓ Custom schemas preserved correctly');
        });
    });

    describe('Edge Cases - The Dungeon of Bugs', () => {
        it('should handle tables with names that need quoting', async () => {
            const dbmlContent = `
                Table "dragons_lair" {
                    "id" bigint [pk]
                    "treasure_amount" decimal
                }

                Table "wizard_tower" {
                    "id" bigint [pk]
                    "floor_count" int
                }

                Table "quest_log" {
                    "id" bigint [pk]
                    "quest_name" varchar(200)
                }
            `;

            const diagram = await importDBMLToDiagram(dbmlContent, {
                databaseType: DatabaseType.MYSQL,
            });

            // Tables should be imported correctly
            expect(diagram.tables?.length).toBe(3);
            expect(
                diagram.tables?.find((t) => t.name === 'dragons_lair')
            ).toBeDefined();
            expect(
                diagram.tables?.find((t) => t.name === 'wizard_tower')
            ).toBeDefined();
            expect(
                diagram.tables?.find((t) => t.name === 'quest_log')
            ).toBeDefined();
        });

        it('should handle the Work_Order_Page_Debug case with Yes/No fields', async () => {
            // This is the exact case that was causing the original bug
            const dbmlContent = `
                Table "Work_Order_Page_Debug" {
                    "ID" bigint [pk, not null]
                    "Work_Order_For" varchar(255)
                    "Quan_to_Make" int
                    "Text_Gen" text
                    "Gen_Info" text
                    "Yes" varchar(255)
                    "No" varchar(255)
                }
            `;

            const diagram = await importDBMLToDiagram(dbmlContent, {
                databaseType: DatabaseType.MYSQL,
            });

            const table = diagram.tables?.find(
                (t) => t.name === 'Work_Order_Page_Debug'
            );
            expect(table).toBeDefined();

            // Check Yes and No fields are preserved
            const yesField = table?.fields.find((f) => f.name === 'Yes');
            const noField = table?.fields.find((f) => f.name === 'No');

            expect(yesField).toBeDefined();
            expect(noField).toBeDefined();
            expect(yesField?.name).toBe('Yes');
            expect(noField?.name).toBe('No');

            // Export and verify it doesn't cause errors
            const exported = generateDBMLFromDiagram(diagram);
            expect(exported.standardDbml).toContain('"Yes"');
            expect(exported.standardDbml).toContain('"No"');

            // Re-import should work without errors
            const reimported = await importDBMLToDiagram(exported.inlineDbml, {
                databaseType: DatabaseType.MYSQL,
            });

            expect(reimported.tables?.length).toBe(1);
        });
    });

    describe('Round-trip Testing - The Eternal Cycle', () => {
        it('should maintain data integrity through multiple import/export cycles', async () => {
            const originalDBML = `
                Table "guild_members" {
                    "id" bigint [pk]
                    "name" varchar(100)
                    "level" int
                    "Yes" varchar(10) // Active status
                    "No" varchar(10)  // Inactive status
                    "Order" int       // SQL keyword - rank order
                }

                Table "guild_quests" {
                    "id" bigint [pk]
                    "quest_name" varchar(200)
                    "assigned_to" bigint
                    "difficulty" int
                }

                Ref: "guild_quests"."assigned_to" > "guild_members"."id"
            `;

            let currentDiagram = await importDBMLToDiagram(originalDBML, {
                databaseType: DatabaseType.MYSQL,
            });

            // Store original IDs
            const originalTableIds = currentDiagram.tables?.map((t) => ({
                name: t.name,
                id: t.id,
            }));

            // Perform 3 round-trips
            for (let cycle = 1; cycle <= 3; cycle++) {
                console.log(`🔄 Round-trip cycle ${cycle}`);

                // Export
                const exported = generateDBMLFromDiagram(currentDiagram);

                // Re-import
                const reimported = await importDBMLToDiagram(
                    exported.inlineDbml,
                    {
                        databaseType: DatabaseType.MYSQL,
                    }
                );

                // Apply changes
                const targetDiagram: Diagram = {
                    ...currentDiagram,
                    tables: reimported.tables,
                    relationships: reimported.relationships,
                    customTypes: reimported.customTypes,
                };

                currentDiagram = applyDBMLChanges({
                    sourceDiagram: currentDiagram,
                    targetDiagram,
                });

                // Verify IDs are still the same as original
                originalTableIds?.forEach((original) => {
                    const currentTable = currentDiagram.tables?.find(
                        (t) => t.name === original.name
                    );
                    expect(currentTable?.id).toBe(original.id);
                });
            }

            console.log('✓ Data integrity maintained through 3 cycles');
        });
    });
});
