import { Sequelize, DataTypes } from 'sequelize';
import dotenv from 'dotenv';

dotenv.config();

// Validiere Umgebungsvariablen
const dbName = process.env.DB_NAME;
const dbUser = process.env.DB_USER;
const dbPass = process.env.DB_PASS || ''; // Fallback auf leeren String
const dbHost = process.env.DB_HOST;

console.log('Database configuration:');
console.log('DB_NAME:', dbName);
console.log('DB_USER:', dbUser);
console.log('DB_PASS:', dbPass ? '[SET]' : '[NOT SET]');
console.log('DB_HOST:', dbHost);

if (!dbName || !dbUser || !dbHost) {
    throw new Error('Missing required database environment variables: DB_NAME, DB_USER, or DB_HOST');
}

const sequelize = new Sequelize(dbName, dbUser, dbPass, {
    host: dbHost,
    dialect: 'postgres',
    define: {
        timestamps: false,
        underscored: true  // WICHTIG: Alle Datenbankfelder im snake_case Format
    },
});

const createSchemas = async () => {
    await sequelize.query('CREATE SCHEMA IF NOT EXISTS community');
    await sequelize.query('CREATE SCHEMA IF NOT EXISTS logs');
    await sequelize.query('CREATE SCHEMA IF NOT EXISTS type');
    await sequelize.query('CREATE SCHEMA IF NOT EXISTS service');
    await sequelize.query('CREATE SCHEMA IF NOT EXISTS forum');
    await sequelize.query('CREATE SCHEMA IF NOT EXISTS falukant_data');
    await sequelize.query('CREATE SCHEMA IF NOT EXISTS falukant_type');
    await sequelize.query('CREATE SCHEMA IF NOT EXISTS falukant_predefine');
    await sequelize.query('CREATE SCHEMA IF NOT EXISTS falukant_log');
    await sequelize.query('CREATE SCHEMA IF NOT EXISTS chat');
    await sequelize.query('CREATE SCHEMA IF NOT EXISTS match3');
    await sequelize.query('CREATE SCHEMA IF NOT EXISTS taxi');
};

const initializeDatabase = async () => {
    await createSchemas();
    const { default: models } = await import('../models/index.js');
    await syncModels(models);
};

const syncModels = async (models) => {
    for (const model of Object.values(models)) { 
        // Verwende force: false und alter: false, um Constraints nicht neu zu erstellen
        // Nur beim ersten Mal oder bei expliziten Schema-Änderungen sollte alter: true verwendet werden
        await model.sync({ alter: false, force: false });
    }
};

// Intelligente Schema-Synchronisation - prüft ob Updates nötig sind
const syncModelsWithUpdates = async (models) => {
    // Prüfe ob wir im Entwicklungsmodus sind
    if (process.env.STAGE !== 'dev') {
        console.log('🚫 Nicht im Entwicklungsmodus - überspringe Schema-Updates');
        console.log('📊 Aktueller Stage:', process.env.STAGE || 'nicht gesetzt');
        console.log('💡 Setze STAGE=dev in der .env Datei für automatische Schema-Updates');
        console.log('🔒 Sicherheitsmodus: Schema-Updates sind deaktiviert');
        
        // Normale Synchronisation ohne Updates
        for (const model of Object.values(models)) { 
            await model.sync({ alter: false, force: false });
        }
        return;
    }

    // Zusätzliche Sicherheitsabfrage für Produktionsumgebungen
    if (process.env.NODE_ENV === 'production' && process.env.STAGE !== 'dev') {
        console.log('🚨 PRODUKTIONSWARNUNG: Schema-Updates sind in Produktionsumgebungen deaktiviert!');
        console.log('🔒 Verwende nur normale Synchronisation ohne Schema-Änderungen');
        
        for (const model of Object.values(models)) { 
            await model.sync({ alter: false, force: false });
        }
        return;
    }

    console.log('🔍 Entwicklungsmodus aktiv - prüfe ob Schema-Updates nötig sind...');
    
    try {
        // Prüfe ob neue Felder existieren müssen
        const needsUpdate = await checkSchemaUpdates(models);
        
        if (needsUpdate) {
            console.log('🔄 Schema-Updates nötig - verwende alter: true');
            for (const model of Object.values(models)) { 
                await model.sync({ alter: true, force: false });
            }
            console.log('✅ Schema-Updates abgeschlossen');
        } else {
            console.log('✅ Keine Schema-Updates nötig - verwende alter: false');
            for (const model of Object.values(models)) { 
                await model.sync({ alter: false, force: false });
            }
        }
    } catch (error) {
        console.error('❌ Fehler bei Schema-Synchronisation:', error);
        // Fallback: Normale Synchronisation ohne Updates
        console.log('🔄 Fallback: Normale Synchronisation ohne Updates');
        for (const model of Object.values(models)) { 
            await model.sync({ alter: false, force: false });
        }
    }
};

// Prüft ob Schema-Updates nötig sind
const checkSchemaUpdates = async (models) => {
    try {
        console.log('🔍 Prüfe alle Schemas auf Updates...');
        
        // Alle verfügbaren Schemas
        const schemas = [
            'community', 'logs', 'type', 'service', 'forum', 
            'falukant_data', 'falukant_type', 'falukant_predefine', 'falukant_log', 
            'chat', 'match3'
        ];
        
        let needsUpdate = false;
        
        // Prüfe jedes Schema
        for (const schema of schemas) {
            const schemaNeedsUpdate = await checkSchemaForUpdates(schema, models);
            if (schemaNeedsUpdate) {
                needsUpdate = true;
            }
        }
        
        return needsUpdate;
    } catch (error) {
        console.error('❌ Fehler bei Schema-Prüfung:', error);
        return false; // Im Zweifelsfall: Keine Updates
    }
};

// Prüft ein spezifisches Schema auf Updates
const checkSchemaForUpdates = async (schemaName, models) => {
    try {
        console.log(`🔍 Prüfe Schema: ${schemaName}`);
        
        // Hole alle Tabellen in diesem Schema
        const tables = await sequelize.query(`
            SELECT table_name 
            FROM information_schema.tables 
            WHERE table_schema = $1
            ORDER BY table_name
        `, { 
            bind: [schemaName],
            type: sequelize.QueryTypes.SELECT 
        });
        
        if (tables.length === 0) {
            console.log(`  📊 Schema ${schemaName}: Keine Tabellen gefunden`);
            return false;
        }
        
        console.log(`  📊 Schema ${schemaName}: ${tables.length} Tabellen gefunden`);
        
        // Prüfe jede Tabelle auf Updates
        for (const table of tables) {
            const tableName = table.table_name;
            const tableNeedsUpdate = await checkTableForUpdates(schemaName, tableName, models);
            if (tableNeedsUpdate) {
                console.log(`  🔄 Tabelle ${tableName} braucht Updates`);
                return true;
            }
        }
        
        // Prüfe auf fehlende Tabellen (neue Models)
        const missingTables = await checkForMissingTables(schemaName, models);
        if (missingTables.length > 0) {
            console.log(`  🔄 Neue Tabellen gefunden: ${missingTables.join(', ')}`);
            return true;
        }
        
        console.log(`  ✅ Schema ${schemaName}: Keine Updates nötig`);
        return false;
        
    } catch (error) {
        console.error(`❌ Fehler beim Prüfen von Schema ${schemaName}:`, error);
        return false;
    }
};

// Prüft auf fehlende Tabellen (neue Models)
const checkForMissingTables = async (schemaName, models) => {
    try {
        const missingTables = [];
        
        // Hole alle erwarteten Tabellen aus den Models
        for (const [modelName, model] of Object.entries(models)) {
            if (model._schema === schemaName) {
                const tableExists = await sequelize.query(`
                    SELECT EXISTS (
                        SELECT FROM information_schema.tables 
                        WHERE table_schema = $1 
                        AND table_name = $2
                    );
                `, { 
                    bind: [schemaName, model.tableName],
                    type: sequelize.QueryTypes.SELECT 
                });
                
                if (!tableExists[0]?.exists) {
                    missingTables.push(model.tableName);
                }
            }
        }
        
        return missingTables;
    } catch (error) {
        console.error(`❌ Fehler beim Prüfen fehlender Tabellen:`, error);
        return [];
    }
};

// Prüft eine spezifische Tabelle auf Updates
const checkTableForUpdates = async (schemaName, tableName, models) => {
    try {
        // Finde das entsprechende Model
        const model = findModelForTable(schemaName, tableName, models);
        if (!model) {
            console.log(`    ⚠️  Kein Model für Tabelle ${schemaName}.${tableName} gefunden`);
            return false;
        }
        
        // Hole aktuelle Spalten der Tabelle
        const currentColumns = await sequelize.query(`
            SELECT column_name, data_type, is_nullable, column_default
            FROM information_schema.columns 
            WHERE table_schema = $1 
            AND table_name = $2
            ORDER BY ordinal_position
        `, { 
            bind: [schemaName, tableName],
            type: sequelize.QueryTypes.SELECT 
        });
        
        // Hole erwartete Spalten aus dem Model
        const expectedColumns = Object.keys(model.rawAttributes);
        
        // Vergleiche aktuelle und erwartete Spalten
        const missingColumns = expectedColumns.filter(expectedCol => {
            return !currentColumns.some(currentCol => 
                currentCol.column_name === expectedCol
            );
        });
        
        if (missingColumns.length > 0) {
            console.log(`    📊 Fehlende Spalten in ${tableName}: ${missingColumns.join(', ')}`);
            return true;
        }
        
        // Prüfe auf geänderte Spalten-Typen oder Constraints
        for (const expectedCol of expectedColumns) {
            const currentCol = currentColumns.find(col => col.column_name === expectedCol);
            if (currentCol) {
                const needsUpdate = await checkColumnForUpdates(
                    schemaName, tableName, expectedCol, currentCol, model.rawAttributes[expectedCol]
                );
                if (needsUpdate) {
                    return true;
                }
            }
        }
        
        return false;
        
    } catch (error) {
        console.error(`❌ Fehler beim Prüfen von Tabelle ${schemaName}.${tableName}:`, error);
        return false;
    }
};

// Prüft eine spezifische Spalte auf Updates
const checkColumnForUpdates = async (schemaName, tableName, columnName, currentColumn, expectedAttribute) => {
    try {
        // Prüfe Datentyp-Änderungen
        if (currentColumn.data_type !== getExpectedDataType(expectedAttribute)) {
            console.log(`    🔄 Spalte ${columnName}: Datentyp geändert (${currentColumn.data_type} → ${getExpectedDataType(expectedAttribute)})`);
            return true;
        }
        
        // Prüfe NULL/NOT NULL Änderungen
        const currentNullable = currentColumn.is_nullable === 'YES';
        const expectedNullable = expectedAttribute.allowNull !== false;
        if (currentNullable !== expectedNullable) {
            console.log(`    🔄 Spalte ${columnName}: NULL-Constraint geändert (${currentNullable} → ${expectedNullable})`);
            return true;
        }
        
        // Prüfe Standardwert-Änderungen
        if (expectedAttribute.defaultValue !== undefined && 
            currentColumn.column_default !== getExpectedDefaultValue(expectedAttribute.defaultValue)) {
            console.log(`    🔄 Spalte ${columnName}: Standardwert geändert`);
            return true;
        }
        
        return false;
        
    } catch (error) {
        console.error(`❌ Fehler beim Prüfen von Spalte ${columnName}:`, error);
        return false;
    }
};

// Hilfsfunktion: Findet Model für eine Tabelle
const findModelForTable = (schemaName, tableName, models) => {
    // Suche nach dem Model basierend auf Schema und Tabellenname
    for (const [modelName, model] of Object.entries(models)) {
        if (model.tableName === tableName && 
            model._schema === schemaName) {
            return model;
        }
    }
    return null;
};

// Hilfsfunktion: Konvertiert Sequelize-Datentyp zu PostgreSQL-Datentyp
const getExpectedDataType = (attribute) => {
    if (!attribute || !attribute.type) return 'text';
    const type = attribute.type;

    // Direktklassentypen in Sequelize sind Funktionen/Klassen; "instanceof" funktioniert bei Wrappern wie DataTypes.INTEGER() nicht immer.
    // Deshalb vergleichen wir über den .key wenn verfügbar.
    const key = type.key || type.constructor?.key;
    switch (key) {
        case 'INTEGER': return 'integer';
        case 'STRING': return 'character varying';
        case 'TEXT': return 'text';
        case 'BOOLEAN': return 'boolean';
        case 'DATE': return 'timestamp without time zone';
        case 'JSON': return 'json';
        case 'DECIMAL': return 'numeric';
        default:
            return 'text';
    }
};

// Hilfsfunktion: Konvertiert Sequelize-Default zu PostgreSQL-Default
const getExpectedDefaultValue = (defaultValue) => {
    if (defaultValue === null) return null;
    if (typeof defaultValue === 'string') return `'${defaultValue}'`;
    if (typeof defaultValue === 'number') return defaultValue.toString();
    if (typeof defaultValue === 'boolean') return defaultValue.toString();
    if (defaultValue === sequelize.literal('CURRENT_TIMESTAMP')) return 'CURRENT_TIMESTAMP';
    
    // Fallback
    return defaultValue?.toString() || null;
};

// Separate Funktion für Schema-Updates (nur bei Bedarf aufrufen)
const updateSchema = async (models) => {
    console.log('🔄 Aktualisiere Datenbankschema...');
    for (const model of Object.values(models)) { 
        await model.sync({ alter: true, force: false });
    }
    console.log('✅ Datenbankschema aktualisiert');
};

async function updateFalukantUserMoney(falukantUserId, moneyChange, activity, changedBy = null) {
    try {
        const result = await sequelize.query(
            `SELECT falukant_data.update_money(
                :falukantUserId, 
                :moneyChange, 
                :activity, 
                :changedBy
            )`,
            {
                replacements: {
                    falukantUserId,
                    moneyChange,
                    activity,
                    changedBy,
                },
                type: sequelize.QueryTypes.SELECT,
            }
        );
        return { 
            success: true, 
            message: 'Money updated successfully', 
            result 
        };
    } catch (error) {
        console.error('Error updating money:', error);
        return { 
            success: false, 
            message: error.message 
        };
    }
}

// Immer Schema-Updates (für Deployment)
const syncModelsAlways = async (models) => {
    console.log('🔍 Deployment-Modus: Führe immer Schema-Updates durch...');
    
    try {
        for (const model of Object.values(models)) { 
            await model.sync({ alter: true, force: false });
        }
        console.log('✅ Schema-Updates für alle Models abgeschlossen');
    } catch (error) {
        console.error('❌ Fehler bei Schema-Updates:', error);
        throw error;
    }
};

export { sequelize, initializeDatabase, syncModels, syncModelsWithUpdates, syncModelsAlways, updateSchema, updateFalukantUserMoney };
