/**
 * Database Setup and Configuration Helper
 * Handles database installation detection and configuration
 */
import { exec } from 'child_process';
import * as fs from 'fs/promises';
import * as path from 'path';
import { promisify } from 'util';
import { AppError, ensureDir, ErrorCode, getEnv, safeParse, safeReadFile, writeJSON, } from '../../utils/common.js';
import { waitForServiceReady } from '../../utils/condition-waiter.js';
const execAsync = promisify(exec);
export class DatabaseSetup {
    /**
     * Check if Neo4j is installed and running
     */
    static async checkNeo4jAvailability() {
        const result = {
            installed: false,
            running: false,
            version: undefined,
            dockerAvailable: false,
        };
        // Check if Neo4j is installed via command line
        try {
            const { stdout } = await execAsync('neo4j version 2>/dev/null');
            result.installed = true;
            result.version = stdout.trim();
        }
        catch {
            // Neo4j CLI not found
        }
        // Check if Docker is available
        try {
            await execAsync('docker --version 2>/dev/null');
            result.dockerAvailable = true;
            // Check if Neo4j container is running
            const { stdout } = await execAsync('docker ps --format "table {{.Names}}" 2>/dev/null');
            if (stdout.includes('neo4j')) {
                result.installed = true;
                result.running = true;
            }
        }
        catch {
            // Docker not available
        }
        // Check if Neo4j server is responding
        try {
            const neo4j = await import('neo4j-driver');
            const driver = neo4j.default.driver('bolt://localhost:7687', neo4j.default.auth.basic('neo4j', 'neo4j'));
            await driver.verifyConnectivity();
            await driver.close();
            result.running = true;
        }
        catch {
            // Server not responding
        }
        return result;
    }
    /**
     * Get database credentials from environment or config
     */
    static async getCredentials(options) {
        const credentials = {
            neo4j: {
                uri: getEnv('NEO4J_URI', 'bolt://localhost:7687') || 'bolt://localhost:7687',
                user: getEnv('NEO4J_USER', 'neo4j') || 'neo4j',
                password: getEnv('NEO4J_PASSWORD', '') || '',
                database: getEnv('NEO4J_DATABASE', 'scrivener') || 'scrivener',
            },
        };
        // Check for .env file in project
        const envPath = path.join(options.projectPath, '.env');
        try {
            const envContent = await safeReadFile(envPath);
            const envVars = this.parseEnvFile(envContent);
            if (envVars.NEO4J_URI)
                credentials.neo4j.uri = envVars.NEO4J_URI;
            if (envVars.NEO4J_USER)
                credentials.neo4j.user = envVars.NEO4J_USER;
            if (envVars.NEO4J_PASSWORD)
                credentials.neo4j.password = envVars.NEO4J_PASSWORD;
            if (envVars.NEO4J_DATABASE)
                credentials.neo4j.database = envVars.NEO4J_DATABASE;
        }
        catch {
            // .env file not found or not readable
        }
        // Check for user config file
        const configPath = path.join(options.projectPath, '.scrivener-databases', 'credentials.json');
        try {
            const configContent = await safeReadFile(configPath);
            const config = safeParse(configContent, {});
            if (config.neo4j) {
                Object.assign(credentials.neo4j, config.neo4j);
            }
        }
        catch {
            // Config file not found
        }
        return credentials;
    }
    /**
     * Setup Neo4j using Docker if not installed
     */
    static async setupNeo4jWithDocker() {
        try {
            console.error('Setting up Neo4j with Docker...');
            // Pull Neo4j image
            await execAsync('docker pull neo4j:latest');
            // Run Neo4j container
            const runCommand = `
				docker run -d \
				--name scrivener-neo4j \
				-p 7474:7474 -p 7687:7687 \
				-e NEO4J_AUTH=neo4j/scrivener-mcp \
				-e NEO4J_PLUGINS='["apoc"]' \
				-v scrivener-neo4j-data:/data \
				neo4j:latest
			`
                .replace(/\s+/g, ' ')
                .trim();
            await execAsync(runCommand);
            // Wait for Neo4j to start
            await this.waitForNeo4j();
            console.error('Neo4j container started successfully');
            return true;
        }
        catch (error) {
            console.error('Failed to setup Neo4j with Docker:', error);
            return false;
        }
    }
    /**
     * Wait for Neo4j to become available
     */
    static async waitForNeo4j(maxAttempts = 30) {
        const neo4j = await import('neo4j-driver');
        // Use condition-based waiting instead of loop with fixed delays
        await waitForServiceReady('localhost', 7687, maxAttempts * 1000);
        // Final connectivity verification
        try {
            const driver = neo4j.default.driver('bolt://localhost:7687', neo4j.default.auth.basic('neo4j', 'scrivener-mcp'));
            await driver.verifyConnectivity();
            await driver.close();
        }
        catch (error) {
            throw new AppError('Neo4j service is running but authentication failed', ErrorCode.INITIALIZATION_ERROR, { originalError: error.message });
        }
    }
    /**
     * Generate setup instructions for manual installation
     */
    static getSetupInstructions() {
        return `
Database Setup Instructions
===========================

SQLite:
-------
SQLite is automatically handled by the application.
No manual installation required.

Neo4j (Optional but Recommended):
----------------------------------
Neo4j provides advanced graph analytics for your writing project.

Option 1: Docker (Recommended)
-------------------------------
1. Install Docker: https://docs.docker.com/get-docker/
2. Run: docker run -d --name scrivener-neo4j \\
        -p 7474:7474 -p 7687:7687 \\
        -e NEO4J_AUTH=neo4j/your-password \\
        neo4j:latest

Option 2: Native Installation
-----------------------------
1. Download Neo4j: https://neo4j.com/download/
2. Install and start Neo4j
3. Set initial password when prompted
4. Default connection: bolt://localhost:7687

Option 3: Neo4j AuraDB (Cloud)
------------------------------
1. Sign up at: https://neo4j.com/cloud/aura-free/
2. Create a free instance
3. Note your connection URI and credentials

Configuration:
-------------
Create a .env file in your project root:
NEO4J_URI=bolt://localhost:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=your-password
NEO4J_DATABASE=scrivener

Or set environment variables before running the application.

The application will work with SQLite only if Neo4j is not available,
but you'll miss out on advanced features like:
- Character relationship mapping
- Story structure visualization
- Plot complexity analysis
- Narrative flow optimization
`.trim();
    }
    /**
     * Parse .env file content
     */
    static parseEnvFile(content) {
        const vars = {};
        const lines = content.split('\n');
        for (const line of lines) {
            const trimmed = line.trim();
            if (!trimmed || trimmed.startsWith('#'))
                continue;
            const [key, ...valueParts] = trimmed.split('=');
            if (key && valueParts.length > 0) {
                let value = valueParts.join('=').trim();
                // Remove quotes if present
                if ((value.startsWith('"') && value.endsWith('"')) ||
                    (value.startsWith("'") && value.endsWith("'"))) {
                    value = value.slice(1, -1);
                }
                vars[key.trim()] = value;
            }
        }
        return vars;
    }
    /**
     * Save credentials securely
     */
    static async saveCredentials(projectPath, credentials) {
        const configPath = path.join(projectPath, '.scrivener-databases', 'credentials.json');
        const configDir = path.dirname(configPath);
        await ensureDir(configDir);
        // Load existing config
        let existing = {};
        try {
            const content = await safeReadFile(configPath);
            existing = safeParse(content, {});
        }
        catch {
            // File doesn't exist yet
        }
        // Merge credentials
        const updated = {
            ...existing,
            ...credentials,
            updatedAt: new Date().toISOString(),
        };
        // Save with restricted permissions
        await writeJSON(configPath, updated);
        // Set restricted permissions after writing
        await fs.chmod(configPath, 0o600); // Read/write for owner only
    }
}
//# sourceMappingURL=database-setup.js.map