"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDatabase = createDatabase;
exports.runMigrations = runMigrations;
exports.createDefaultAdmin = createDefaultAdmin;
exports.createGmailTeam = createGmailTeam;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const bcryptjs_1 = __importDefault(require("bcryptjs"));
const pg_1 = require("pg");
const sequelize_1 = require("sequelize");
const umzug_1 = require("umzug");
const uuid_1 = require("uuid");
const db_config_1 = require("../config/db-config");
const dbConfig = (0, db_config_1.getDbConfig)();
const dbName = (0, db_config_1.getDbName)();
/**
 * Create a pg Client configuration, using connectionString if DATABASE_URL is available
 * This properly handles URL-encoded passwords and special characters
 */
function createClientConfig(database) {
    if (process.env.DATABASE_URL) {
        // Use connectionString directly - pg library handles URL encoding properly
        try {
            const url = new URL(process.env.DATABASE_URL);
            if (database) {
                url.pathname = `/${database}`;
            }
            return { connectionString: url.toString() };
        }
        catch (error) {
            // If URL parsing fails (e.g., unencoded special chars in password),
            // try to use the connection string directly with pg library
            // pg's parser might be more forgiving
            if (database) {
                // Try to modify the database name in the connection string
                const urlStr = process.env.DATABASE_URL;
                const dbMatch = urlStr.match(/\/([^/?]+)(\?|$)/);
                if (dbMatch) {
                    const modifiedUrl = urlStr.replace(/\/([^/?]+)(\?|$)/, `/${database}$2`);
                    return { connectionString: modifiedUrl };
                }
            }
            // If all else fails, use connection string as-is and let pg handle it
            return { connectionString: process.env.DATABASE_URL };
        }
    }
    // Fall back to individual config parameters
    return {
        host: dbConfig.host,
        port: dbConfig.port,
        user: dbConfig.user,
        password: dbConfig.password,
        database: database || dbName,
    };
}
async function createDatabase() {
    // Try connecting to different default databases to create the target database
    const defaultDatabases = ['postgres', 'template1'];
    let client = null;
    let connected = false;
    console.log(`Attempting to connect to PostgreSQL at ${dbConfig.host}:${dbConfig.port}...`);
    for (const defaultDb of defaultDatabases) {
        try {
            client = new pg_1.Client(createClientConfig(defaultDb));
            await client.connect();
            console.log(`Connected to PostgreSQL as ${dbConfig.user} (via ${defaultDb})`);
            connected = true;
            break;
        }
        catch (error) {
            if (client) {
                try {
                    await client.end();
                }
                catch (e) {
                    // Ignore cleanup errors
                }
            }
            // Try next database
            continue;
        }
    }
    if (!connected) {
        console.error('Error: Could not connect to PostgreSQL. Make sure PostgreSQL is running.');
        throw new Error('Could not connect to PostgreSQL');
    }
    if (!client) {
        throw new Error('Client is null despite connected being true');
    }
    try {
        // Check if database exists
        const result = await client.query(`SELECT 1 FROM pg_database WHERE datname = $1`, [dbName]);
        if (result.rows.length > 0) {
            console.log(`Database "${dbName}" already exists.`);
            await client.end();
            return;
        }
        // Create the database
        // Note: Parameterized queries cannot be used for database names in CREATE DATABASE
        // We sanitize by using double quotes, assuming dbName comes from trusted env or config
        await client.query(`CREATE DATABASE "${dbName}"`);
        console.log(`Database "${dbName}" created successfully.`);
        await client.end();
    }
    catch (error) {
        if (error.code === '42P04') {
            // Database already exists (race condition)
            console.log(`Database "${dbName}" already exists.`);
        }
        else {
            console.error('Error creating database:', error.message);
            if (client) {
                await client.end();
            }
            throw error;
        }
    }
}
async function runMigrations() {
    console.log('Running database migrations...');
    const currentDir = __dirname;
    // Helper to find file recursively up or in common locations
    const findFile = (searchPaths) => {
        for (const p of searchPaths) {
            if (fs_1.default.existsSync(p))
                return p;
        }
        return null;
    };
    // Possible locations for migrations
    const migrationsPath = findFile([
        path_1.default.resolve(currentDir, '../../migrations'), // dist/src/../../migrations -> dist/migrations ?? No.
        // If we are in dist/src, ../.. is dist. ../../../ is root.
        // If migrations are in root/migrations.
        path_1.default.resolve(currentDir, '../../../migrations'), // from dist/src
        path_1.default.resolve(currentDir, '../../migrations'), // from src/scripts
        path_1.default.resolve(process.cwd(), 'node_modules/@device-farm/hub/migrations'),
        path_1.default.resolve(process.cwd(), 'migrations'), // Fallback to cwd
    ]);
    if (!migrationsPath) {
        throw new Error('Could not find migrations directory');
    }
    console.log(`Using migrations from: ${migrationsPath}`);
    // Initialize Sequelize
    const sequelize = new sequelize_1.Sequelize(dbName, dbConfig.user, dbConfig.password, {
        host: dbConfig.host,
        port: dbConfig.port,
        dialect: 'postgres',
        logging: false, // console.log to see SQL
    });
    try {
        const umzug = new umzug_1.Umzug({
            migrations: {
                glob: ['*.js', { cwd: migrationsPath }],
                resolve: ({ name, path: migrationPath, context }) => {
                    // Use native require to load external migration files (bypassing webpack)
                    // eslint-disable-next-line no-eval
                    const requireFunc = eval('require');
                    if (!fs_1.default.existsSync(migrationPath)) {
                        throw new Error(`Migration file not found: ${migrationPath}`);
                    }
                    // eslint-disable-next-line @typescript-eslint/no-var-requires
                    const migration = requireFunc(migrationPath);
                    return {
                        name,
                        up: async () => await migration.up(context.queryInterface, context.Sequelize),
                        down: async () => await migration.down(context.queryInterface, context.Sequelize),
                    };
                },
            },
            context: { queryInterface: sequelize.getQueryInterface(), Sequelize: sequelize_1.Sequelize },
            storage: new umzug_1.SequelizeStorage({ sequelize }),
            logger: console,
        });
        await umzug.up();
        console.log('Migrations completed successfully.');
    }
    catch (error) {
        console.error('Migrations failed:', error);
        throw error;
    }
    finally {
        await sequelize.close();
    }
}
// Generate access key (similar to generateAccessKey function)
function generateAccessKey(prefix) {
    const KEY_POOL = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ1234567890';
    const accessKeyPrefix = prefix.substring(0, 12) + '_';
    const randomKey = Array.from({ length: 20 - accessKeyPrefix.length }, () => KEY_POOL[Math.floor(Math.random() * KEY_POOL.length)]).join('');
    return `${accessKeyPrefix}${randomKey}`;
}
async function createDefaultAdmin() {
    console.log('Checking/Creating default admin user...');
    const client = new pg_1.Client(createClientConfig());
    try {
        await client.connect();
        // Get SUPER_ADMIN role
        const roleResult = await client.query(`SELECT id FROM roles WHERE name = 'SUPER_ADMIN' LIMIT 1`);
        if (roleResult.rows.length === 0) {
            throw new Error('SUPER_ADMIN role not found. Please run migrations first.');
        }
        const roleId = roleResult.rows[0].id;
        // Create default organization if it doesn't exist
        let orgId;
        const orgResult = await client.query(`SELECT id FROM organizations WHERE name = 'Default Organization' LIMIT 1`);
        if (orgResult.rows.length === 0) {
            orgId = (0, uuid_1.v4)();
            await client.query(`INSERT INTO organizations (id, name, created_at, updated_at) 
         VALUES ($1, $2, NOW(), NOW())`, [orgId, 'Default Organization']);
            console.log(`✓ Created default organization: ${orgId}`);
        }
        else {
            orgId = orgResult.rows[0].id;
        }
        // Check if admin user already exists
        const existingUser = await client.query(`SELECT id, email, access_key, token FROM users WHERE email = $1`, ['admin@device-farm.com']);
        if (existingUser.rows.length > 0) {
            const user = existingUser.rows[0];
            console.log(`✓ Admin user already exists (Access Key: ${user.access_key})`);
            await client.end();
            return;
        }
        // Create admin user
        const userId = (0, uuid_1.v4)();
        const password = process.env.SUPER_ADMIN_PASSWORD || 'Admin@123';
        const hashedPassword = await bcryptjs_1.default.hash(password, 10);
        const accessKey = generateAccessKey('admin');
        const token = (0, uuid_1.v4)();
        await client.query(`INSERT INTO users (
        id, email, password, firstname, lastname, 
        role_id, organization_id, access_key, token, 
        is_active, source, created_at, updated_at
      ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, NOW(), NOW())`, [
            userId,
            'admin@device-farm.com',
            hashedPassword,
            'Admin',
            'User',
            roleId,
            orgId,
            accessKey,
            token,
            true,
            'local',
        ]);
        console.log(`\n✅ Default Admin user created successfully!`);
        console.log(`  Email: admin@device-farm.com`);
        console.log(`  Access Key: ${accessKey}`);
        await client.end();
    }
    catch (error) {
        console.error('Error creating admin user:', error.message);
        if (client) {
            try {
                await client.end();
            }
            catch (e) {
                // ignore
            }
        }
        throw error;
    }
}
async function createGmailTeam() {
    console.log('Checking/Creating Gmail team...');
    const client = new pg_1.Client(createClientConfig());
    try {
        await client.connect();
        // Get default organization
        const orgResult = await client.query(`SELECT id FROM organizations WHERE name = 'Default Organization' LIMIT 1`);
        if (orgResult.rows.length === 0) {
            throw new Error('Default Organization not found. Please run createDefaultAdmin first.');
        }
        const orgId = orgResult.rows[0].id;
        // Check if Gmail team already exists
        const existingTeam = await client.query(`SELECT id FROM teams WHERE name = $1 LIMIT 1`, [
            'Gmail',
        ]);
        if (existingTeam.rows.length > 0) {
            console.log(`✓ Gmail team already exists (ID: ${existingTeam.rows[0].id})`);
            await client.end();
            return;
        }
        // Create Gmail team
        const teamId = (0, uuid_1.v4)();
        await client.query(`INSERT INTO teams (id, organization_id, name, description, created_at, updated_at) 
       VALUES ($1, $2, $3, $4, NOW(), NOW())`, [teamId, orgId, 'Gmail', 'Automatically created team for Google OAuth users']);
        console.log(`✅ Gmail team created successfully! (ID: ${teamId})`);
        await client.end();
    }
    catch (error) {
        console.error('Error creating Gmail team:', error.message);
        if (client) {
            try {
                await client.end();
            }
            catch (e) {
                // ignore
            }
        }
        throw error;
    }
}
