// Cross-platform Node.js API server for Caddy management
// Uses Caddy Admin API and Technitium DNS API directly
// Run with: node caddy-api.js

const express = require('express');
const path = require('path');
const cors = require('cors');
const fs = require('fs');

const app = express();
const PORT = 3001;

// Middleware
app.use(cors());
app.use(express.json());
app.use(express.static('public'));

// Configuration
const CADDY_ADMIN_API = process.env.CADDY_ADMIN_API || 'http://localhost:2019';
const DNS_SERVER_API = process.env.DNS_SERVER_API || 'http://192.168.254.204:5380';
const DNS_API_TOKEN = process.env.TECHNITIUM_API_TOKEN || '';

// Helper function to make HTTP requests
async function makeRequest(url, options = {}) {
    const https = url.startsWith('https:') ? require('https') : require('http');
    const urlObj = new URL(url);

    return new Promise((resolve, reject) => {
        const reqOptions = {
            hostname: urlObj.hostname,
            port: urlObj.port,
            path: urlObj.pathname + urlObj.search,
            method: options.method || 'GET',
            headers: options.headers || {},
            ...options
        };

        const req = https.request(reqOptions, (res) => {
            let data = '';
            res.on('data', (chunk) => data += chunk);
            res.on('end', () => {
                try {
                    const parsed = JSON.parse(data);
                    resolve({ status: res.statusCode, data: parsed });
                } catch (e) {
                    resolve({ status: res.statusCode, data: data });
                }
            });
        });

        req.on('error', reject);

        if (options.body) {
            req.write(typeof options.body === 'string' ? options.body : JSON.stringify(options.body));
        }

        req.end();
    });
}

// Get current Caddy configuration
app.get('/api/v1/caddy/config', async (req, res) => {
    try {
        const response = await makeRequest(`${CADDY_ADMIN_API}/config/`);

        if (response.status === 200) {
            res.json({
                status: 'success',
                config: response.data
            });
        } else {
            res.status(response.status).json({
                status: 'error',
                message: 'Failed to get Caddy configuration',
                details: response.data
            });
        }
    } catch (error) {
        console.error('Error getting Caddy config:', error);
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});

// Get list of services (from apps.json + custom apps)
app.get('/api/v1/services', async (req, res) => {
    try {
        const servicesPath = path.join(__dirname, '../apps.json');

        if (fs.existsSync(servicesPath)) {
            const servicesData = fs.readFileSync(servicesPath, 'utf8');
            const services = JSON.parse(servicesData);
            res.json({ status: 'success', services });
        } else {
            res.json({ status: 'success', services: [] });
        }
    } catch (error) {
        console.error('Error reading services:', error);
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});

// Add DNS record via Technitium API
async function addDnsRecord(domain, ipAddress, ttl = 3600) {
    if (!DNS_API_TOKEN) {
        throw new Error('DNS API token not configured. Set TECHNITIUM_API_TOKEN environment variable.');
    }

    const url = `${DNS_SERVER_API}/api/zones/records/add?token=${DNS_API_TOKEN}&domain=${domain}&type=A&ipAddress=${ipAddress}&ttl=${ttl}`;

    console.log('Adding DNS record:', { domain, ipAddress, ttl });
    const response = await makeRequest(url);

    if (response.data.status === 'ok') {
        return { success: true, message: 'DNS record added successfully' };
    } else {
        throw new Error(`DNS API error: ${response.data.message || 'Unknown error'}`);
    }
}

// Delete DNS record via Technitium API
async function deleteDnsRecord(domain, ipAddress) {
    if (!DNS_API_TOKEN) {
        console.warn('DNS API token not configured. Skipping DNS deletion.');
        return { success: true, message: 'DNS deletion skipped (no token)' };
    }

    const url = `${DNS_SERVER_API}/api/zones/records/delete?token=${DNS_API_TOKEN}&domain=${domain}&type=A&ipAddress=${ipAddress}`;

    console.log('Deleting DNS record:', { domain, ipAddress });
    const response = await makeRequest(url);

    if (response.data.status === 'ok') {
        return { success: true, message: 'DNS record deleted successfully' };
    } else {
        throw new Error(`DNS API error: ${response.data.message || 'Unknown error'}`);
    }
}

// Add route to Caddy via Admin API
async function addCaddyRoute(domain, upstreamUrl, useTls = true) {
    // Build Caddy route configuration
    const routeConfig = {
        "@id": domain,
        "match": [{
            "host": [domain]
        }],
        "handle": [{
            "handler": "reverse_proxy",
            "upstreams": [{
                "dial": upstreamUrl.replace(/^https?:\/\//, '')
            }]
        }],
        "terminal": true
    };

    // If using internal TLS, we need to add TLS configuration
    if (useTls) {
        // Caddy handles TLS automatically for matched domains
        // Internal CA is configured in the global Caddyfile
    }

    console.log('Adding Caddy route:', JSON.stringify(routeConfig, null, 2));

    // Add the route to the HTTP server
    const response = await makeRequest(`${CADDY_ADMIN_API}/config/apps/http/servers/srv0/routes/0`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(routeConfig)
    });

    if (response.status === 200 || response.status === 201) {
        return { success: true, message: 'Caddy route added successfully' };
    } else {
        throw new Error(`Caddy API error: ${JSON.stringify(response.data)}`);
    }
}

// Deploy app endpoint - handles DNS and Caddy configuration via APIs
app.post('/api/v1/apps/deploy', async (req, res) => {
    try {
        const { appId, config } = req.body;
        const { subdomain, ip, createDns, port, sslType, dnsType } = config;

        console.log('Deploying app:', { appId, config });

        // Validate required fields
        if (!appId || !subdomain || !ip) {
            return res.status(400).json({
                success: false,
                message: 'Missing required fields: appId, subdomain, ip'
            });
        }

        // Build the full domain
        const domain = subdomain.includes('.') ? subdomain : `${subdomain}.sami`;
        const finalPort = port || '80';
        const upstreamUrl = `${ip}:${finalPort}`;

        // Step 1: Add DNS record if requested (private DNS)
        if (createDns && dnsType === 'private') {
            try {
                await addDnsRecord(domain, ip);
            } catch (dnsError) {
                console.error('DNS creation failed:', dnsError);
                return res.status(500).json({
                    success: false,
                    message: `DNS creation failed: ${dnsError.message}`,
                    step: 'dns'
                });
            }
        }

        // Step 2: Add route to Caddy via Admin API
        try {
            const useTls = sslType === 'internal';
            await addCaddyRoute(domain, upstreamUrl, useTls);
        } catch (caddyError) {
            console.error('Caddy route addition failed:', caddyError);

            // Rollback DNS if it was created
            if (createDns && dnsType === 'private') {
                try {
                    await deleteDnsRecord(domain, ip);
                } catch (rollbackError) {
                    console.error('DNS rollback failed:', rollbackError);
                }
            }

            return res.status(500).json({
                success: false,
                message: `Caddy configuration failed: ${caddyError.message}`,
                step: 'caddy'
            });
        }

        // Step 3: Return success response
        res.json({
            success: true,
            message: `App ${appId} deployed successfully`,
            url: `https://${domain}`,
            domain: domain,
            ip: ip,
            port: finalPort,
            containerId: null,
            dnsCreated: createDns && dnsType === 'private',
            caddyConfigured: true
        });

    } catch (error) {
        console.error('Deployment error:', error);
        res.status(500).json({
            success: false,
            message: error.message
        });
    }
});

// Delete app endpoint - removes DNS and Caddy configuration
app.post('/api/v1/apps/delete', async (req, res) => {
    try {
        const { domain, ip } = req.body;

        if (!domain) {
            return res.status(400).json({
                success: false,
                message: 'Domain is required'
            });
        }

        console.log('Deleting app:', { domain, ip });

        // Step 1: Remove from Caddy
        try {
            const response = await makeRequest(`${CADDY_ADMIN_API}/id/${domain}`, {
                method: 'DELETE'
            });

            if (response.status !== 200) {
                console.warn('Caddy route deletion warning:', response.data);
            }
        } catch (caddyError) {
            console.error('Caddy route deletion failed:', caddyError);
            // Continue anyway to try DNS deletion
        }

        // Step 2: Remove DNS record if IP provided
        if (ip) {
            try {
                await deleteDnsRecord(domain, ip);
            } catch (dnsError) {
                console.error('DNS deletion failed:', dnsError);
                return res.status(500).json({
                    success: false,
                    message: `DNS deletion failed: ${dnsError.message}`,
                    caddyDeleted: true,
                    dnsDeleted: false
                });
            }
        }

        res.json({
            success: true,
            message: 'App deleted successfully',
            domain: domain,
            caddyDeleted: true,
            dnsDeleted: !!ip
        });

    } catch (error) {
        console.error('Deletion error:', error);
        res.status(500).json({
            success: false,
            message: error.message
        });
    }
});

// Test endpoint
app.get('/api/v1/caddy/test', (req, res) => {
    res.json({
        status: 'success',
        message: 'Caddy API is running',
        timestamp: new Date().toISOString(),
        platform: process.platform,
        caddyAdminApi: CADDY_ADMIN_API,
        dnsServerApi: DNS_SERVER_API,
        dnsTokenConfigured: !!DNS_API_TOKEN
    });
});

// Health check
app.get('/health', (req, res) => {
    res.json({ status: 'healthy', timestamp: new Date().toISOString() });
});

// Start server
app.listen(PORT, () => {
    console.log(`\n====================================`);
    console.log(`Caddy API server running on http://localhost:${PORT}`);
    console.log(`====================================`);
    console.log(`Caddy Admin API: ${CADDY_ADMIN_API}`);
    console.log(`DNS Server API: ${DNS_SERVER_API}`);
    console.log(`DNS Token: ${DNS_API_TOKEN ? '✓ Configured' : '✗ Not configured'}`);
    console.log(`\nEndpoints:`);
    console.log(`  POST /api/apps/deploy - Deploy an app (DNS + Caddy)`);
    console.log(`  POST /api/apps/delete - Delete an app (DNS + Caddy)`);
    console.log(`  GET  /api/services - Get list of services`);
    console.log(`  GET  /api/caddy/config - Get current Caddy configuration`);
    console.log(`  GET  /api/caddy/test - Test API connectivity`);
    console.log(`  GET  /health - Health check`);
    console.log(`====================================\n`);
});

module.exports = app;
