#!/usr/bin/env node

const fs = require('fs');
const Papa = require('papaparse');
const path = require('path');

// Input and output file paths
const INPUT_FILE = '/mnt/storagessd1tb/changemaker.lite.dev/changemaker.lite/map/data/City_of_Edmonton_-_Neighbourhoods_20250807.csv';
const OUTPUT_FILE = path.join(path.dirname(INPUT_FILE), 'edmonton_nocodb_optimized.csv');

// Function to convert WKT MultiPolygon to GeoJSON with optimized precision
function wktToGeoJSON(wkt, precision = 7) {
    if (!wkt || typeof wkt !== 'string') return null;
    
    let coords = wkt.replace('MULTIPOLYGON ', '').trim();
    coords = coords.slice(1, -1);
    
    const polygons = [];
    let currentPolygon = [];
    let currentRing = [];
    let depth = 0;
    let currentCoord = '';
    
    for (let i = 0; i < coords.length; i++) {
        const char = coords[i];
        
        if (char === '(') {
            depth++;
            if (depth === 2) currentRing = [];
        } else if (char === ')') {
            if (depth === 2) {
                if (currentCoord.trim()) {
                    let [lon, lat] = currentCoord.trim().split(' ').map(Number);
                    // Reduce precision (7 decimals = ~1cm accuracy)
                    const multiplier = Math.pow(10, precision);
                    lon = Math.round(lon * multiplier) / multiplier;
                    lat = Math.round(lat * multiplier) / multiplier;
                    currentRing.push([lon, lat]);
                    currentCoord = '';
                }
                currentPolygon.push(currentRing);
            } else if (depth === 1) {
                polygons.push(currentPolygon);
                currentPolygon = [];
            }
            depth--;
        } else if (char === ',') {
            if (depth === 2 && currentCoord.trim()) {
                let [lon, lat] = currentCoord.trim().split(' ').map(Number);
                const multiplier = Math.pow(10, precision);
                lon = Math.round(lon * multiplier) / multiplier;
                lat = Math.round(lat * multiplier) / multiplier;
                currentRing.push([lon, lat]);
                currentCoord = '';
            }
        } else if (char !== ' ' || currentCoord.trim()) {
            currentCoord += char;
        }
    }
    
    return polygons.length === 1 
        ? { type: "Polygon", coordinates: polygons[0] }
        : { type: "MultiPolygon", coordinates: polygons };
}

// Smart polygon simplification - only remove redundant points
function smartSimplify(coordinates, maxPoints = 500) {
    return coordinates.map(ring => {
        if (ring.length <= maxPoints) return ring;
        
        // Calculate step to evenly sample points
        const step = ring.length / maxPoints;
        const simplified = [];
        
        for (let i = 0; i < maxPoints - 1; i++) {
            const index = Math.floor(i * step);
            simplified.push(ring[index]);
        }
        
        // Always include the last point to close the polygon
        simplified.push(ring[ring.length - 1]);
        
        return simplified;
    });
}

// Function to calculate bounds from GeoJSON
function calculateBounds(geoJSON) {
    if (!geoJSON) return null;
    
    let minLat = Infinity, maxLat = -Infinity;
    let minLon = Infinity, maxLon = -Infinity;
    
    function processCoords(coords) {
        if (Array.isArray(coords[0]) && Array.isArray(coords[0][0])) {
            coords.forEach(processCoords);
        } else if (Array.isArray(coords[0]) && typeof coords[0][0] === 'number') {
            coords.forEach(([lon, lat]) => {
                minLat = Math.min(minLat, lat);
                maxLat = Math.max(maxLat, lat);
                minLon = Math.min(minLon, lon);
                maxLon = Math.max(maxLon, lon);
            });
        }
    }
    
    processCoords(geoJSON.coordinates);
    return `[[${minLat.toFixed(7)},${minLon.toFixed(7)}],[${maxLat.toFixed(7)},${maxLon.toFixed(7)}]]`;
}

// Compact JSON stringify (no extra whitespace)
function compactStringify(obj) {
    return JSON.stringify(obj, null, 0);
}

// Main conversion function
function convertCSV() {
    console.log('📁 Reading file:', INPUT_FILE);
    
    // Read the CSV file
    const csvData = fs.readFileSync(INPUT_FILE, 'utf8');
    
    console.log('📊 Parsing CSV...');
    
    // Parse the CSV
    const parsed = Papa.parse(csvData, {
        header: true,
        dynamicTyping: true,
        skipEmptyLines: true
    });
    
    console.log(`✅ Found ${parsed.data.length} neighbourhoods to convert`);
    console.log('🎯 Optimizing precision for NocoDB (100k char limit)...\n');
    
    // Get current timestamp
    const now = new Date();
    const timestamp = now.toISOString().replace('T', ' ').replace('Z', '+00:00').slice(0, -7) + '+00:00';
    
    const stats = {
        totalRows: 0,
        optimizedRows: 0,
        simplifiedRows: 0,
        precision7: 0,
        precision6: 0,
        precision5: 0
    };
    
    // Convert each row
    const convertedData = parsed.data.map((row, index) => {
        if ((index + 1) % 50 === 0) {
            console.log(`  Converting... ${index + 1}/${parsed.data.length}`);
        }
        
        stats.totalRows++;
        
        // Start with 7 decimal precision (~1cm accuracy)
        let geoJSON = wktToGeoJSON(row['Geometry Multipolygon'], 7);
        let geoJSONString = geoJSON ? compactStringify(geoJSON) : '';
        
        // If still too large, try 6 decimals (~10cm accuracy)
        if (geoJSONString.length > 99000) {
            stats.precision7++;
            geoJSON = wktToGeoJSON(row['Geometry Multipolygon'], 6);
            geoJSONString = compactStringify(geoJSON);
            
            // If still too large, try 5 decimals (~1m accuracy)
            if (geoJSONString.length > 99000) {
                stats.precision6++;
                geoJSON = wktToGeoJSON(row['Geometry Multipolygon'], 5);
                geoJSONString = compactStringify(geoJSON);
                
                // If STILL too large, simplify points
                if (geoJSONString.length > 99000) {
                    stats.precision5++;
                    const originalPoints = JSON.stringify(geoJSON).length;
                    
                    if (geoJSON.type === 'Polygon') {
                        geoJSON.coordinates = smartSimplify(geoJSON.coordinates);
                    } else if (geoJSON.type === 'MultiPolygon') {
                        geoJSON.coordinates = geoJSON.coordinates.map(polygon => 
                            smartSimplify(polygon)
                        );
                    }
                    
                    geoJSONString = compactStringify(geoJSON);
                    stats.simplifiedRows++;
                    
                    console.log(`  ⚠️  ${row['Neighbourhood Name']}: Simplified from ${originalPoints} to ${geoJSONString.length} chars`);
                }
            }
        } else {
            stats.optimizedRows++;
        }
        
        // Create enhanced description with ward information
        let description = '';
        if (row['Civic Ward']) {
            description += `Ward: ${row['Civic Ward']}\n\n`;
        }
        if (row['Description']) {
            description += row['Description'];
        }
        
        return {
            'ID': index + 1,
            'CreatedAt': timestamp,
            'UpdatedAt': timestamp,
            'Name': row['Neighbourhood Name'] || '',
            'Description': description.substring(0, 5000),
            'Color': '#3388ff',
            'Opacity': 0.7,
            'Category': 'Neighborhood',
            'PublicVisibility': true,
            'OfficialCut': true,
            'GeoJSONData': geoJSONString,
            'Bounds': calculateBounds(geoJSON),
            'CreatedBy': 'City of Edmonton',
            'CreatedAtDate': row['Effective Start Date'] || null,
            'UpdatedAtDate': timestamp
        };
    });
    
    console.log('\n📝 Converting to CSV format...');
    
    // Convert back to CSV
    const outputCSV = Papa.unparse(convertedData);
    
    // Write the file
    fs.writeFileSync(OUTPUT_FILE, outputCSV);
    
    const fileSizeMB = (Buffer.byteLength(outputCSV) / 1024 / 1024).toFixed(2);
    
    console.log('\n✨ Conversion complete!');
    console.log('══════════════════════════════════════════════════════════');
    console.log(`📊 Total neighbourhoods: ${stats.totalRows}`);
    console.log(`✅ Optimized with 7 decimals (1cm): ${stats.optimizedRows}`);
    console.log(`📉 Reduced to 6 decimals (10cm): ${stats.precision7}`);
    console.log(`📉 Reduced to 5 decimals (1m): ${stats.precision6}`);
    console.log(`⚠️  Point reduction needed: ${stats.simplifiedRows}`);
    console.log(`💾 File size: ${fileSizeMB} MB`);
    console.log(`📁 Output saved to: ${OUTPUT_FILE}`);
    console.log('══════════════════════════════════════════════════════════');
    console.log('\n✅ All geometries are under 100,000 characters!');
    console.log('   You can import this directly to NocoDB without changing column types.');
}

// Run the conversion
convertCSV();
