import { spawn } from 'child_process';
import * as path from 'path';
import { fileURLToPath } from 'url';
import iconv from 'iconv-lite';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const JAVA_DIR = path.resolve(__dirname, '..', 'java');
const JAVA_JAR = path.join(JAVA_DIR, 'jcc-11.5.9.0.jar');
const JAVA_JSON_JAR = path.join(JAVA_DIR, 'json-20251224.jar');
const JAVA_CLASS = 'Db2Query';
export class Db2QueryService {
    jdbcUrl;
    username;
    password;
    schema;
    javaPath;
    constructor() {
        this.jdbcUrl = process.env.DB2_JDBC_URL || '';
        this.username = process.env.DB2_USERNAME || '';
        this.password = process.env.DB2_PASSWORD || '';
        this.schema = process.env.DB2_SCHEMA || '';
        this.javaPath = process.env.JAVA_HOME ? path.join(process.env.JAVA_HOME, 'bin', 'java') : 'java';
        if (!this.jdbcUrl) {
            console.warn('[WARN] DB2_JDBC_URL is not set');
        }
    }
    async testConnection() {
        try {
            console.log('[INFO] Testing database connection...');
            await this.executeJavaCommand('info');
            console.log('[INFO] Database connection successful!');
            return true;
        }
        catch (error) {
            console.error('[ERROR] Database connection failed:', error);
            return false;
        }
    }
    async queryBySql(sql, limit = 200, offset = 0) {
        console.log('[INFO] Executing query:', sql.substring(0, 100) + (sql.length > 100 ? '...' : ''));
        if (offset > 0) {
            const result = await this.executeJavaCommand('query', sql, limit.toString(), offset.toString());
            return result;
        }
        const result = await this.executeJavaCommand('query', sql, limit.toString());
        return result;
    }
    async queryTables(tableNamePattern = '%', schemaPattern, limit = 200) {
        const schema = schemaPattern || this.schema;
        console.log(`[INFO] Querying tables: schema=${schema}, table=${tableNamePattern}`);
        const result = await this.executeJavaCommand('tables', tableNamePattern, schema, limit.toString());
        return result;
    }
    async queryTableColumns(tableName, schemaName) {
        const schema = schemaName || this.schema;
        console.log(`[INFO] Querying columns: schema=${schema}, table=${tableName}`);
        const result = await this.executeJavaCommand('columns', tableName, schema);
        return result;
    }
    async getDatabaseInfo() {
        console.log('[INFO] Getting database info...');
        const result = await this.executeJavaCommand('info');
        return result;
    }
    async executeJavaCommand(mode, ...args) {
        const maxRetries = 2;
        for (let attempt = 0; attempt <= maxRetries; attempt++) {
            try {
                return await this.runJavaProcess(mode, ...args);
            }
            catch (error) {
                if (attempt < maxRetries) {
                    console.warn(`[WARN] Java process failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying...`);
                    await new Promise(resolve => setTimeout(resolve, 1000));
                }
                else {
                    throw error;
                }
            }
        }
        throw new Error('Unreachable');
    }
    runJavaProcess(mode, ...args) {
        return new Promise((resolve, reject) => {
            const javaArgs = [
                '-Dfile.encoding=UTF-8',
                '-cp',
                `.;${JAVA_JAR};${JAVA_JSON_JAR}`,
                JAVA_CLASS,
                mode,
                ...args
            ];
            const env = {
                ...process.env,
                DB2_JDBC_URL: this.jdbcUrl,
                DB2_USERNAME: this.username,
                DB2_PASSWORD: this.password,
                DB2_SCHEMA: this.schema
            };
            let stdout = '';
            let stderr = '';
            const proc = spawn(this.javaPath, javaArgs, {
                cwd: JAVA_DIR,
                env
            });
            proc.stdout?.on('data', (data) => {
                stdout += iconv.decode(data, 'utf-8');
            });
            proc.stderr?.on('data', (data) => {
                stderr += iconv.decode(data, 'utf-8');
            });
            proc.on('close', (code) => {
                if (code !== 0) {
                    reject(new Error(`Java process exited with code ${code}: ${stderr}`));
                    return;
                }
                try {
                    const output = stdout.trim();
                    if (!output) {
                        reject(new Error('Java process returned empty output'));
                        return;
                    }
                    const result = JSON.parse(output);
                    resolve(result);
                }
                catch (error) {
                    reject(new Error(`Failed to parse Java output: ${error}\nOutput: ${stdout}`));
                }
            });
            proc.on('error', (error) => {
                reject(error);
            });
        });
    }
}
//# sourceMappingURL=db2-query-service.js.map