// syncDatabase.js

import { initializeDatabase, syncModelsWithUpdates, syncModelsAlways, sequelize } from './sequelize.js';
import initializeTypes from './initializeTypes.js';
import initializeSettings from './initializeSettings.js';
import initializeUserRights from './initializeUserRights.js';
import initializeImageTypes from './initializeImageTypes.js';
import initializeFalukant from './initializeFalukant.js';
import setupAssociations from '../models/associations.js';
import models from '../models/index.js';
import { createTriggers } from '../models/trigger.js';
import initializeForum from './initializeForum.js';
import initializeChat from './initializeChat.js';
import initializeMatch3Data from './initializeMatch3.js';
import updateExistingMatch3Levels from './updateExistingMatch3Levels.js';
import initializeTaxi from './initializeTaxi.js';

// Normale Synchronisation (nur bei STAGE=dev Schema-Updates)
const syncDatabase = async () => {
    try {
        // Zeige den aktuellen Stage an
        const currentStage = process.env.STAGE || 'nicht gesetzt';
        console.log(`🚀 Starte Datenbank-Synchronisation (Stage: ${currentStage})`);
        
        if (currentStage !== 'dev') {
            console.log('⚠️  WARNUNG: Automatische Schema-Updates sind deaktiviert');
            console.log('💡 Setze STAGE=dev in der .env Datei für automatische Schema-Updates');
            console.log('🔒 Produktionsmodus: Nur normale Synchronisation ohne Schema-Änderungen');
        } else {
            console.log('✅ Entwicklungsmodus aktiv - Schema-Updates sind aktiviert');
        }

        console.log("Initializing database schemas...");
        await initializeDatabase();  

        // Vokabeltrainer: Tabellen sicherstellen (auch ohne manuell ausgeführte Migrations)
        // Hintergrund: In Produktion sind Schema-Updates deaktiviert, und Migrations werden nicht automatisch ausgeführt.
        // Damit API/Menu nicht mit "relation does not exist" (42P01) scheitert, legen wir die Tabellen idempotent an.
        console.log("Ensuring Vocab-Trainer tables exist...");
        try {
            await sequelize.query(`
                CREATE TABLE IF NOT EXISTS community.vocab_language (
                    id SERIAL PRIMARY KEY,
                    owner_user_id INTEGER NOT NULL,
                    name TEXT NOT NULL,
                    share_code TEXT NOT NULL,
                    created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),
                    CONSTRAINT vocab_language_owner_fk
                        FOREIGN KEY (owner_user_id)
                        REFERENCES community."user"(id)
                        ON DELETE CASCADE,
                    CONSTRAINT vocab_language_share_code_uniq UNIQUE (share_code)
                );

                CREATE TABLE IF NOT EXISTS community.vocab_language_subscription (
                    id SERIAL PRIMARY KEY,
                    user_id INTEGER NOT NULL,
                    language_id INTEGER NOT NULL,
                    created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),
                    CONSTRAINT vocab_language_subscription_user_fk
                        FOREIGN KEY (user_id)
                        REFERENCES community."user"(id)
                        ON DELETE CASCADE,
                    CONSTRAINT vocab_language_subscription_language_fk
                        FOREIGN KEY (language_id)
                        REFERENCES community.vocab_language(id)
                        ON DELETE CASCADE,
                    CONSTRAINT vocab_language_subscription_uniq UNIQUE (user_id, language_id)
                );

                CREATE INDEX IF NOT EXISTS vocab_language_owner_idx
                    ON community.vocab_language(owner_user_id);
                CREATE INDEX IF NOT EXISTS vocab_language_subscription_user_idx
                    ON community.vocab_language_subscription(user_id);
                CREATE INDEX IF NOT EXISTS vocab_language_subscription_language_idx
                    ON community.vocab_language_subscription(language_id);

                CREATE TABLE IF NOT EXISTS community.vocab_chapter (
                    id SERIAL PRIMARY KEY,
                    language_id INTEGER NOT NULL,
                    title TEXT NOT NULL,
                    created_by_user_id INTEGER NOT NULL,
                    created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),
                    CONSTRAINT vocab_chapter_language_fk
                        FOREIGN KEY (language_id)
                        REFERENCES community.vocab_language(id)
                        ON DELETE CASCADE,
                    CONSTRAINT vocab_chapter_creator_fk
                        FOREIGN KEY (created_by_user_id)
                        REFERENCES community."user"(id)
                        ON DELETE CASCADE
                );
                CREATE INDEX IF NOT EXISTS vocab_chapter_language_idx
                    ON community.vocab_chapter(language_id);

                CREATE TABLE IF NOT EXISTS community.vocab_lexeme (
                    id SERIAL PRIMARY KEY,
                    language_id INTEGER NOT NULL,
                    text TEXT NOT NULL,
                    normalized TEXT NOT NULL,
                    created_by_user_id INTEGER NOT NULL,
                    created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),
                    CONSTRAINT vocab_lexeme_language_fk
                        FOREIGN KEY (language_id)
                        REFERENCES community.vocab_language(id)
                        ON DELETE CASCADE,
                    CONSTRAINT vocab_lexeme_creator_fk
                        FOREIGN KEY (created_by_user_id)
                        REFERENCES community."user"(id)
                        ON DELETE CASCADE,
                    CONSTRAINT vocab_lexeme_unique_per_language UNIQUE (language_id, normalized)
                );
                CREATE INDEX IF NOT EXISTS vocab_lexeme_language_idx
                    ON community.vocab_lexeme(language_id);

                CREATE TABLE IF NOT EXISTS community.vocab_chapter_lexeme (
                    id SERIAL PRIMARY KEY,
                    chapter_id INTEGER NOT NULL,
                    learning_lexeme_id INTEGER NOT NULL,
                    reference_lexeme_id INTEGER NOT NULL,
                    created_by_user_id INTEGER NOT NULL,
                    created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),
                    CONSTRAINT vocab_chlex_chapter_fk
                        FOREIGN KEY (chapter_id)
                        REFERENCES community.vocab_chapter(id)
                        ON DELETE CASCADE,
                    CONSTRAINT vocab_chlex_learning_fk
                        FOREIGN KEY (learning_lexeme_id)
                        REFERENCES community.vocab_lexeme(id)
                        ON DELETE CASCADE,
                    CONSTRAINT vocab_chlex_reference_fk
                        FOREIGN KEY (reference_lexeme_id)
                        REFERENCES community.vocab_lexeme(id)
                        ON DELETE CASCADE,
                    CONSTRAINT vocab_chlex_creator_fk
                        FOREIGN KEY (created_by_user_id)
                        REFERENCES community."user"(id)
                        ON DELETE CASCADE,
                    CONSTRAINT vocab_chlex_unique UNIQUE (chapter_id, learning_lexeme_id, reference_lexeme_id)
                );
                CREATE INDEX IF NOT EXISTS vocab_chlex_chapter_idx
                    ON community.vocab_chapter_lexeme(chapter_id);
                CREATE INDEX IF NOT EXISTS vocab_chlex_learning_idx
                    ON community.vocab_chapter_lexeme(learning_lexeme_id);
                CREATE INDEX IF NOT EXISTS vocab_chlex_reference_idx
                    ON community.vocab_chapter_lexeme(reference_lexeme_id);
            `);
            console.log("✅ Vocab-Trainer Tabellen sind vorhanden.");
        } catch (e) {
            console.warn('⚠️  Konnte Vocab-Trainer Tabellen nicht sicherstellen:', e?.message || e);
        }

        // Vorab: Stelle kritische Spalten sicher, damit Index-Erstellung nicht fehlschlägt
        console.log("Pre-ensure Taxi columns (traffic_light) ...");
        try {
            await sequelize.query(`
                DO $$
                BEGIN
                    IF NOT EXISTS (
                        SELECT 1 FROM information_schema.columns
                        WHERE table_schema = 'taxi' AND table_name = 'taxi_map_tile' AND column_name = 'traffic_light'
                    ) THEN
                        ALTER TABLE taxi.taxi_map_tile
                        ADD COLUMN traffic_light BOOLEAN NOT NULL DEFAULT false;
                    END IF;
                END
                $$;
            `);
            console.log("✅ traffic_light-Spalte ist vorhanden");
        } catch (e) {
            console.warn('⚠️  Konnte traffic_light-Spalte nicht vorab sicherstellen:', e?.message || e);
        }

        // Cleanup: Entferne verwaiste Einträge vor Schema-Updates (nur wenn Schema-Updates aktiviert)
        if (currentStage === 'dev') {
            console.log("Cleaning up orphaned entries...");
            try {
                // Cleanup user_param_visibility
                const result1 = await sequelize.query(`
                    DELETE FROM community.user_param_visibility
                    WHERE param_id NOT IN (
                        SELECT id FROM community.user_param
                    );
                `);
                const deletedCount1 = result1[1] || 0;
                if (deletedCount1 > 0) {
                    console.log(`✅ ${deletedCount1} verwaiste user_param_visibility Einträge entfernt`);
                }

                // Cleanup stock mit ungültigen branch_id (0 oder nicht existierend)
                const result2 = await sequelize.query(`
                    DELETE FROM falukant_data.stock
                    WHERE branch_id = 0 OR branch_id NOT IN (
                        SELECT id FROM falukant_data.branch
                    );
                `);
                const deletedCount2 = result2[1] || 0;
                if (deletedCount2 > 0) {
                    console.log(`✅ ${deletedCount2} verwaiste stock Einträge entfernt`);
                }

                // Cleanup knowledge mit ungültigen character_id oder product_id
                const result3 = await sequelize.query(`
                    DELETE FROM falukant_data.knowledge
                    WHERE character_id NOT IN (
                        SELECT id FROM falukant_data.character
                    ) OR product_id NOT IN (
                        SELECT id FROM falukant_type.product
                    );
                `);
                const deletedCount3 = result3[1] || 0;
                if (deletedCount3 > 0) {
                    console.log(`✅ ${deletedCount3} verwaiste knowledge Einträge entfernt`);
                }

            // Cleanup notification mit ungültigen user_id
            const result4 = await sequelize.query(`
                DELETE FROM falukant_log.notification
                WHERE user_id NOT IN (
                    SELECT id FROM falukant_data.falukant_user
                );
            `);
            const deletedCount4 = result4[1] || 0;
            if (deletedCount4 > 0) {
                console.log(`✅ ${deletedCount4} verwaiste notification Einträge entfernt`);
            }

            // Cleanup promotional_gift mit ungültigen sender_character_id oder recipient_character_id
            const result5 = await sequelize.query(`
                DELETE FROM falukant_log.promotional_gift
                WHERE sender_character_id NOT IN (
                    SELECT id FROM falukant_data.character
                ) OR recipient_character_id NOT IN (
                    SELECT id FROM falukant_data.character
                );
            `);
            const deletedCount5 = result5[1] || 0;
            if (deletedCount5 > 0) {
                console.log(`✅ ${deletedCount5} verwaiste promotional_gift Einträge entfernt`);
            }

            // Cleanup user_house mit ungültigen house_type_id oder user_id
            const result6 = await sequelize.query(`
                DELETE FROM falukant_data.user_house
                WHERE house_type_id NOT IN (
                    SELECT id FROM falukant_type.house
                ) OR user_id NOT IN (
                    SELECT id FROM falukant_data.falukant_user
                );
            `);
            const deletedCount6 = result6[1] || 0;
            if (deletedCount6 > 0) {
                console.log(`✅ ${deletedCount6} verwaiste user_house Einträge entfernt`);
            }

            // Cleanup child_relation mit ungültigen father_character_id, mother_character_id oder child_character_id
            const result7 = await sequelize.query(`
                DELETE FROM falukant_data.child_relation
                WHERE father_character_id NOT IN (
                    SELECT id FROM falukant_data.character
                ) OR mother_character_id NOT IN (
                    SELECT id FROM falukant_data.character
                ) OR child_character_id NOT IN (
                    SELECT id FROM falukant_data.character
                );
            `);
            const deletedCount7 = result7[1] || 0;
            if (deletedCount7 > 0) {
                console.log(`✅ ${deletedCount7} verwaiste child_relation Einträge entfernt`);
            }

            if (deletedCount1 === 0 && deletedCount2 === 0 && deletedCount3 === 0 && deletedCount4 === 0 && deletedCount5 === 0 && deletedCount6 === 0 && deletedCount7 === 0) {
                console.log("✅ Keine verwaisten Einträge gefunden");
            }
            } catch (e) {
                console.warn('⚠️  Konnte verwaiste Einträge nicht bereinigen:', e?.message || e);
            }
        }

        console.log("Setting up associations...");
        setupAssociations();

        console.log("Synchronizing models...");
        await syncModelsWithUpdates(models);

        console.log("Initializing settings...");
        await initializeSettings();

        console.log("Initializing types...");
        await initializeTypes();   

        console.log("Initializing user rights...");
        await initializeUserRights();

        console.log("Initializing image types...");
        await initializeImageTypes();

        console.log("Initializing forums...");
        await initializeForum();     

        console.log("Initializing Falukant...");
        await initializeFalukant();
        
        console.log("Creating triggers...");
        await createTriggers();  

        console.log("Initializing chat...");
        await initializeChat();

        // Match3-Initialisierung NACH der Model-Synchronisation
        console.log("Initializing Match3...");
        await initializeMatch3Data();
        
        // Match3-Levels aktualisieren NACH der Initialisierung
        console.log("Updating existing Match3 levels...");
        await updateExistingMatch3Levels();

        console.log("Initializing Taxi...");
        await initializeTaxi();

        console.log('Database synchronization complete.');
    } catch (error) {
        console.error('Unable to synchronize the database:', error);
    }
};

// Deployment-Synchronisation (immer Schema-Updates)
const syncDatabaseForDeployment = async () => {
    try {
        // WICHTIG: Bei Caching-Problemen das Script neu starten
        // Node.js cached ES-Module, daher müssen Models neu geladen werden
        console.log('📦 Lade Models neu (Node.js Module-Cache wird verwendet)...');
        
        // Zeige den aktuellen Stage an
        const currentStage = process.env.STAGE || 'nicht gesetzt';
        console.log(`🚀 Starte Datenbank-Synchronisation für Deployment (Stage: ${currentStage})`);
        console.log('✅ Deployment-Modus: Schema-Updates sind immer aktiviert');

        console.log("Initializing database schemas...");
        await initializeDatabase();

        // Vorab: Stelle kritische Spalten sicher, damit Index-Erstellung nicht fehlschlägt
        console.log("Pre-ensure Taxi columns (traffic_light) ...");
        try {
            await sequelize.query(`
                DO $$
                BEGIN
                    IF NOT EXISTS (
                        SELECT 1 FROM information_schema.columns
                        WHERE table_schema = 'taxi' AND table_name = 'taxi_map_tile' AND column_name = 'traffic_light'
                    ) THEN
                        ALTER TABLE taxi.taxi_map_tile
                        ADD COLUMN traffic_light BOOLEAN NOT NULL DEFAULT false;
                    END IF;
                END
                $$;
            `);
            console.log("✅ traffic_light-Spalte ist vorhanden");
        } catch (e) {
            console.warn('⚠️  Konnte traffic_light-Spalte nicht vorab sicherstellen:', e?.message || e);
        }

        // Migration: Transport product_id und size nullable machen
        console.log("Making transport product_id and size nullable...");
        try {
            await sequelize.query(`
                DO $$
                BEGIN
                    -- Prüfe ob product_id NOT NULL Constraint existiert
                    IF EXISTS (
                        SELECT 1 FROM information_schema.columns
                        WHERE table_schema = 'falukant_data' 
                        AND table_name = 'transport' 
                        AND column_name = 'product_id'
                        AND is_nullable = 'NO'
                    ) THEN
                        ALTER TABLE falukant_data.transport 
                        ALTER COLUMN product_id DROP NOT NULL;
                        RAISE NOTICE 'product_id NOT NULL Constraint entfernt';
                    END IF;
                    
                    -- Prüfe ob size NOT NULL Constraint existiert
                    IF EXISTS (
                        SELECT 1 FROM information_schema.columns
                        WHERE table_schema = 'falukant_data' 
                        AND table_name = 'transport' 
                        AND column_name = 'size'
                        AND is_nullable = 'NO'
                    ) THEN
                        ALTER TABLE falukant_data.transport 
                        ALTER COLUMN size DROP NOT NULL;
                        RAISE NOTICE 'size NOT NULL Constraint entfernt';
                    END IF;
                END
                $$;
            `);
            console.log("✅ Transport product_id und size sind jetzt nullable");
        } catch (e) {
            console.warn('⚠️  Konnte Transport-Spalten nicht nullable machen:', e?.message || e);
        }

        // Cleanup: Entferne verwaiste Einträge vor Schema-Updates
        console.log("Cleaning up orphaned entries...");
        try {
            // Cleanup user_param_visibility
            const result1 = await sequelize.query(`
                DELETE FROM community.user_param_visibility
                WHERE param_id NOT IN (
                    SELECT id FROM community.user_param
                );
            `);
            const deletedCount1 = result1[1] || 0;
            if (deletedCount1 > 0) {
                console.log(`✅ ${deletedCount1} verwaiste user_param_visibility Einträge entfernt`);
            }

            // Cleanup stock mit ungültigen branch_id (0 oder nicht existierend)
            const result2 = await sequelize.query(`
                DELETE FROM falukant_data.stock
                WHERE branch_id = 0 OR branch_id NOT IN (
                    SELECT id FROM falukant_data.branch
                );
            `);
            const deletedCount2 = result2[1] || 0;
            if (deletedCount2 > 0) {
                console.log(`✅ ${deletedCount2} verwaiste stock Einträge entfernt`);
            }

            // Cleanup knowledge mit ungültigen character_id oder product_id
            const result3 = await sequelize.query(`
                DELETE FROM falukant_data.knowledge
                WHERE character_id NOT IN (
                    SELECT id FROM falukant_data.character
                ) OR product_id NOT IN (
                    SELECT id FROM falukant_type.product
                );
            `);
            const deletedCount3 = result3[1] || 0;
            if (deletedCount3 > 0) {
                console.log(`✅ ${deletedCount3} verwaiste knowledge Einträge entfernt`);
            }

            // Cleanup notification mit ungültigen user_id
            const result4 = await sequelize.query(`
                DELETE FROM falukant_log.notification
                WHERE user_id NOT IN (
                    SELECT id FROM falukant_data.falukant_user
                );
            `);
            const deletedCount4 = result4[1] || 0;
            if (deletedCount4 > 0) {
                console.log(`✅ ${deletedCount4} verwaiste notification Einträge entfernt`);
            }

            // Cleanup promotional_gift mit ungültigen sender_character_id oder recipient_character_id
            const result5 = await sequelize.query(`
                DELETE FROM falukant_log.promotional_gift
                WHERE sender_character_id NOT IN (
                    SELECT id FROM falukant_data.character
                ) OR recipient_character_id NOT IN (
                    SELECT id FROM falukant_data.character
                );
            `);
            const deletedCount5 = result5[1] || 0;
            if (deletedCount5 > 0) {
                console.log(`✅ ${deletedCount5} verwaiste promotional_gift Einträge entfernt`);
            }

            // Cleanup user_house mit ungültigen house_type_id oder user_id
            const result6 = await sequelize.query(`
                DELETE FROM falukant_data.user_house
                WHERE house_type_id NOT IN (
                    SELECT id FROM falukant_type.house
                ) OR user_id NOT IN (
                    SELECT id FROM falukant_data.falukant_user
                );
            `);
            const deletedCount6 = result6[1] || 0;
            if (deletedCount6 > 0) {
                console.log(`✅ ${deletedCount6} verwaiste user_house Einträge entfernt`);
            }

            // Cleanup child_relation mit ungültigen father_character_id, mother_character_id oder child_character_id
            const result7 = await sequelize.query(`
                DELETE FROM falukant_data.child_relation
                WHERE father_character_id NOT IN (
                    SELECT id FROM falukant_data.character
                ) OR mother_character_id NOT IN (
                    SELECT id FROM falukant_data.character
                ) OR child_character_id NOT IN (
                    SELECT id FROM falukant_data.character
                );
            `);
            const deletedCount7 = result7[1] || 0;
            if (deletedCount7 > 0) {
                console.log(`✅ ${deletedCount7} verwaiste child_relation Einträge entfernt`);
            }

            // Cleanup political_office mit ungültigen character_id, office_type_id oder region_id
            const result8 = await sequelize.query(`
                DELETE FROM falukant_data.political_office
                WHERE character_id NOT IN (
                    SELECT id FROM falukant_data.character
                ) OR office_type_id NOT IN (
                    SELECT id FROM falukant_type.political_office_type
                ) OR region_id NOT IN (
                    SELECT id FROM falukant_data.region
                );
            `);
            const deletedCount8 = result8[1] || 0;
            if (deletedCount8 > 0) {
                console.log(`✅ ${deletedCount8} verwaiste political_office Einträge entfernt`);
            }

            // Cleanup vehicle.condition: Legacy-Nulls + Range clamp (UI zeigt sonst "Unbekannt")
            const result9 = await sequelize.query(`
                UPDATE falukant_data.vehicle
                SET condition = 100
                WHERE condition IS NULL;
            `);
            const updatedNullConditions = result9[1] || 0;
            if (updatedNullConditions > 0) {
                console.log(`✅ ${updatedNullConditions} vehicle.condition NULL → 100 gesetzt`);
            }
            const result10 = await sequelize.query(`
                UPDATE falukant_data.vehicle
                SET condition = GREATEST(0, LEAST(100, condition))
                WHERE condition < 0 OR condition > 100;
            `);
            const clampedConditions = result10[1] || 0;
            if (clampedConditions > 0) {
                console.log(`✅ ${clampedConditions} vehicle.condition Werte auf 0..100 geklemmt`);
            }

            if (deletedCount1 === 0 && deletedCount2 === 0 && deletedCount3 === 0 && deletedCount4 === 0 && deletedCount5 === 0 && deletedCount6 === 0 && deletedCount7 === 0 && deletedCount8 === 0 && updatedNullConditions === 0 && clampedConditions === 0) {
                console.log("✅ Keine verwaisten Einträge gefunden");
            }
        } catch (e) {
            console.warn('⚠️  Konnte verwaiste Einträge nicht bereinigen:', e?.message || e);
        }

        console.log("Setting up associations...");
        setupAssociations();

        console.log("Synchronizing models with schema updates...");
        await syncModelsAlways(models);

        console.log("Initializing settings...");
        await initializeSettings();

        console.log("Initializing types...");
        await initializeTypes();   

        console.log("Initializing user rights...");
        await initializeUserRights();

        console.log("Initializing image types...");
        await initializeImageTypes();

        console.log("Initializing forums...");
        await initializeForum();     

        console.log("Initializing Falukant...");
        await initializeFalukant();
        
        console.log("Creating triggers...");
        await createTriggers();  

        console.log("Initializing chat...");
        await initializeChat();

        // Match3-Initialisierung NACH der Model-Synchronisation UND nach der Erstellung aller Tabellen
        console.log("Initializing Match3...");
        await initializeMatch3Data();
        
        // Match3-Levels aktualisieren NACH der Initialisierung
        console.log("Updating existing Match3 levels...");
        await updateExistingMatch3Levels();

        console.log("Initializing Taxi...");
        await initializeTaxi();

        console.log('Database synchronization for deployment complete.');
    } catch (error) {
        console.error('Unable to synchronize the database for deployment:', error);
        throw error; // Fehler weiterwerfen
    }
};

export { syncDatabase, syncDatabaseForDeployment };
