import fs from 'fs';
import https from 'https';
import { exec } from 'child_process';
import chokidar from 'chokidar';
import path from 'path';
import util from 'util';
import dotenv from 'dotenv';

// Load environment variables
dotenv.config();

if (!process.env.ENVIRONMENT) {
    console.error("❌ ENVIRONMENT variable is missing.");
    process.exit(1);
}

// Determine environment and configuration file
const nodeEnv = process.env.ENVIRONMENT || 'dev';
const configFile = `config_${nodeEnv}.json`;

// Load configuration file
let config;
try {
    config = JSON.parse(fs.readFileSync(path.resolve('../', configFile), 'utf8'));
} catch (error) {
    console.error(`❌ Failed to read configuration file '${configFile}': ${error.message}`);
    process.exit(1);
}

// Validate required configurations
const requiredFields = [
    'OPENAPI_URL',
    'SCHEMA_PATH',
    'OUTPUT_DIRECTORY',
    'SSL_KEY_PATH',
    'SSL_CERT_PATH',
];
for (const field of requiredFields) {
    if (!config[field]) {
        console.error(`❌ Missing required configuration: ${field}`);
        process.exit(1);
    }
}

// Resolve paths from the config
const OPENAPI_URL = config.OPENAPI_URL;
const SCHEMA_PATH = path.resolve(config.SCHEMA_PATH);
const OUTPUT_DIRECTORY = path.resolve(config.OUTPUT_DIRECTORY);
const SSL_KEY_PATH = path.resolve(config.SSL_KEY_PATH);
const SSL_CERT_PATH = path.resolve(config.SSL_CERT_PATH);

// Log configuration
console.log(`[INFO] Environment: ${nodeEnv}`);
console.log(`[INFO] Using SCHEMA_PATH: ${SCHEMA_PATH}`);
console.log(`[INFO] Using OUTPUT_DIRECTORY: ${OUTPUT_DIRECTORY}`);

// Verify SSL files
if (!fs.existsSync(SSL_KEY_PATH) || !fs.existsSync(SSL_CERT_PATH)) {
    console.error(`❌ SSL files not found:
    Key Path: ${SSL_KEY_PATH} (exists: ${fs.existsSync(SSL_KEY_PATH)})
    Cert Path: ${SSL_CERT_PATH} (exists: ${fs.existsSync(SSL_CERT_PATH)})`);
    process.exit(1);
}

const execPromisified = util.promisify(exec);

let isGenerating = false;
const debounceDelay = 500; // Debounce interval in milliseconds

// Debounce function to control rapid re-triggering
function debounce(func, delay) {
    let timer;
    return (...args) => {
        clearTimeout(timer);
        timer = setTimeout(() => {
            func.apply(this, args);
        }, delay);
    };
}

// Main function to fetch OpenAPI schema and generate services
async function fetchAndGenerate() {
    if (isGenerating) {
        console.log("⚠️ Generation process is already running.");
        return;
    }
    isGenerating = true;

    try {
        // Fetch OpenAPI schema over HTTPS
        console.log("🚀 Fetching OpenAPI schema...");
        const options = {
            rejectUnauthorized: false,
            key: fs.readFileSync(SSL_KEY_PATH),
            cert: fs.readFileSync(SSL_CERT_PATH),
        };

        const res = await new Promise((resolve, reject) => {
            https.get(OPENAPI_URL, options, resolve).on('error', reject);
        });

        let data = '';
        res.on('data', (chunk) => {
            data += chunk;
        });

        res.on('end', async () => {
            try {
                // Save schema file
                fs.writeFileSync(SCHEMA_PATH, data, 'utf8');
                console.log(`✅ OpenAPI schema saved to ${SCHEMA_PATH}`);

                console.log("🧼 Cleaning output directory...");
                await fs.promises.rm(OUTPUT_DIRECTORY, { recursive: true, force: true });

                console.log(`✅ Output directory cleaned: ${OUTPUT_DIRECTORY}`);
                if (!fs.existsSync(OUTPUT_DIRECTORY)) {
                    console.log(`✅ Confirmed removal of ${OUTPUT_DIRECTORY}`);
                } else {
                    console.error(`❌ Failed to remove output directory: ${OUTPUT_DIRECTORY}`);
                }

                // Generate services
                const command = `npx openapi -i ${SCHEMA_PATH} -o ${OUTPUT_DIRECTORY}`;
                console.log(`🔧 Executing command: ${command}`);

                const { stdout, stderr } = await execPromisified(command);
                if (stderr) {
                    console.error(`⚠️ stderr while generating services: ${stderr}`);
                } else {
                    console.log(`✅ Service generation completed successfully:\n${stdout}`);
                }

                // Copy the generated OpenAPI models to ../logistics/openapi
                const targetDirectory = path.resolve('../logistics/openapi'); // Adjust as per logistics directory
                console.log(`🔄 Copying generated OpenAPI models to ${targetDirectory}...`);

                await fs.promises.rm(targetDirectory, { recursive: true, force: true }); // Clean target directory
                await fs.promises.mkdir(targetDirectory, { recursive: true }); // Ensure the directory exists

                // Copy files from OUTPUT_DIRECTORY to the target directory recursively
                const copyRecursive = async (src, dest) => {
                    const entries = await fs.promises.readdir(src, { withFileTypes: true });
                    for (const entry of entries) {
                        const srcPath = path.join(src, entry.name);
                        const destPath = path.join(dest, entry.name);

                        if (entry.isDirectory()) {
                            await fs.promises.mkdir(destPath, { recursive: true });
                            await copyRecursive(srcPath, destPath);
                        } else {
                            await fs.promises.copyFile(srcPath, destPath);
                        }
                    }
                };
                await copyRecursive(OUTPUT_DIRECTORY, targetDirectory);

                console.log(`✅ OpenAPI models copied successfully to ${targetDirectory}`);
            } catch (error) {
                console.error(`❌ Error during schema processing or generation: ${error.message}`);
            }
            isGenerating = false;
        });
    } catch (error) {
        console.error(`❌ Failed to fetch OpenAPI schema: ${error.message}`);
        isGenerating = false;
    }
}

// Backend directory based on the environment
const backendDirectory = (() => {
    switch (nodeEnv) {
        case 'prod':
            return path.resolve('/home/jungfrau/heidi-v2/backend/app'); // Production path
        case 'test':
            return path.resolve('/home/jungfrau/heidi-v2/backend/app'); // Test path
        case 'dev':
        default:
            return path.resolve('/Users/gotthardg/PycharmProjects/heidi-v2/backend/app'); // Development path
    }
})();

if (!fs.existsSync(backendDirectory)) {
    console.error(`❌ Backend directory does not exist: ${backendDirectory}`);
    process.exit(1);
}
console.log(`👀 Watching for changes in ${backendDirectory}`);

// Watcher for change detection
const watcher = chokidar.watch(backendDirectory, {
    persistent: true,
    ignored: [SCHEMA_PATH, OUTPUT_DIRECTORY],
});

watcher
    .on('add', debounce(fetchAndGenerate, debounceDelay))
    .on('change', debounce(fetchAndGenerate, debounceDelay))
    .on('unlink', debounce(fetchAndGenerate, debounceDelay));

console.log(`👀 Watching for changes in ${backendDirectory}`);