<?php
header('Content-Type: application/json');
require_once 'config.php';

function decodeGeometry($rawGeometry) {
    if (is_array($rawGeometry)) {
        return $rawGeometry;
    }
    $decoded = json_decode($rawGeometry, true);
    return is_array($decoded) ? $decoded : null;
}

function geometryRings($geometry) {
    if (!is_array($geometry) || !isset($geometry['type']) || !isset($geometry['coordinates'])) {
        return [];
    }

    if ($geometry['type'] === 'Polygon') {
        return [$geometry['coordinates']];
    }

    if ($geometry['type'] === 'MultiPolygon') {
        return $geometry['coordinates'];
    }

    return [];
}

function projectLonLatToMeters($lng, $lat, $referenceLat) {
    $earthRadius = 6371000.0;
    $x = deg2rad((float)$lng) * $earthRadius * cos(deg2rad((float)$referenceLat));
    $y = deg2rad((float)$lat) * $earthRadius;
    return [$x, $y];
}

function ringAreaSquareMeters($ring) {
    if (!is_array($ring) || count($ring) < 4) {
        return 0.0;
    }

    $sumLat = 0.0;
    $count = 0;
    foreach ($ring as $point) {
        if (is_array($point) && count($point) >= 2) {
            $sumLat += (float)$point[1];
            $count++;
        }
    }

    if ($count === 0) {
        return 0.0;
    }

    $referenceLat = $sumLat / $count;
    $projected = [];
    foreach ($ring as $point) {
        if (is_array($point) && count($point) >= 2) {
            $projected[] = projectLonLatToMeters((float)$point[0], (float)$point[1], $referenceLat);
        }
    }

    if (count($projected) < 4) {
        return 0.0;
    }

    $area = 0.0;
    $maxIndex = count($projected) - 1;
    for ($i = 0; $i < $maxIndex; $i++) {
        $area += ($projected[$i][0] * $projected[$i + 1][1]) - ($projected[$i + 1][0] * $projected[$i][1]);
    }

    return abs($area / 2.0);
}

function geometryAreaKm2($geometry) {
    $polygons = geometryRings($geometry);
    if (empty($polygons)) {
        return 0.0;
    }

    $totalSquareMeters = 0.0;

    foreach ($polygons as $polygonRings) {
        if (!is_array($polygonRings) || empty($polygonRings)) {
            continue;
        }

        $outerArea = ringAreaSquareMeters($polygonRings[0]);
        $holeArea = 0.0;

        for ($i = 1; $i < count($polygonRings); $i++) {
            $holeArea += ringAreaSquareMeters($polygonRings[$i]);
        }

        $totalSquareMeters += max($outerArea - $holeArea, 0.0);
    }

    return $totalSquareMeters / 1000000.0;
}

function pointInRing($lng, $lat, $ring) {
    if (!is_array($ring) || count($ring) < 4) {
        return false;
    }

    $inside = false;
    $lastIndex = count($ring) - 1;

    for ($i = 0, $j = $lastIndex; $i <= $lastIndex; $j = $i++) {
        $xi = (float)$ring[$i][0];
        $yi = (float)$ring[$i][1];
        $xj = (float)$ring[$j][0];
        $yj = (float)$ring[$j][1];

        $intersect = (($yi > $lat) !== ($yj > $lat)) &&
            ($lng < ($xj - $xi) * ($lat - $yi) / (($yj - $yi) ?: 1e-12) + $xi);

        if ($intersect) {
            $inside = !$inside;
        }
    }

    return $inside;
}

function pointInGeometry($lng, $lat, $geometry) {
    $polygons = geometryRings($geometry);
    if (empty($polygons)) {
        return false;
    }

    foreach ($polygons as $polygonRings) {
        if (!is_array($polygonRings) || empty($polygonRings)) {
            continue;
        }

        if (!pointInRing($lng, $lat, $polygonRings[0])) {
            continue;
        }

        $insideHole = false;
        for ($i = 1; $i < count($polygonRings); $i++) {
            if (pointInRing($lng, $lat, $polygonRings[$i])) {
                $insideHole = true;
                break;
            }
        }

        if (!$insideHole) {
            return true;
        }
    }

    return false;
}

function geometryCentroid($geometry) {
    $polygons = geometryRings($geometry);
    $sumLng = 0.0;
    $sumLat = 0.0;
    $count = 0;

    foreach ($polygons as $polygonRings) {
        if (!is_array($polygonRings) || empty($polygonRings) || !is_array($polygonRings[0])) {
            continue;
        }

        foreach ($polygonRings[0] as $point) {
            if (is_array($point) && count($point) >= 2) {
                $sumLng += (float)$point[0];
                $sumLat += (float)$point[1];
                $count++;
            }
        }
    }

    if ($count === 0) {
        return ['lng' => 109.33, 'lat' => -0.02];
    }

    return [
        'lng' => $sumLng / $count,
        'lat' => $sumLat / $count
    ];
}

function haversineMeters($lat1, $lng1, $lat2, $lng2) {
    $earthRadius = 6371000.0;
    $dLat = deg2rad((float)$lat2 - (float)$lat1);
    $dLng = deg2rad((float)$lng2 - (float)$lng1);

    $a = sin($dLat / 2) * sin($dLat / 2)
        + cos(deg2rad((float)$lat1)) * cos(deg2rad((float)$lat2))
        * sin($dLng / 2) * sin($dLng / 2);

    $c = 2 * atan2(sqrt($a), sqrt(max(1 - $a, 0.0)));
    return $earthRadius * $c;
}

function roadCentroidLngLat($coordinates) {
    if (!is_array($coordinates) || empty($coordinates)) {
        return null;
    }

    $sumLat = 0.0;
    $sumLng = 0.0;
    $count = 0;

    foreach ($coordinates as $pair) {
        if (is_array($pair) && count($pair) >= 2 && is_numeric($pair[0]) && is_numeric($pair[1])) {
            // Road coordinates are stored as [lat, lng]
            $sumLat += (float)$pair[0];
            $sumLng += (float)$pair[1];
            $count++;
        }
    }

    if ($count === 0) {
        return null;
    }

    return [
        'lng' => $sumLng / $count,
        'lat' => $sumLat / $count
    ];
}

function deterministicRoadDensity($populationDensity, $areaId) {
    $seed = ($areaId * 1103515245 + 12345) & 0x7fffffff;
    $variation = 0.8 + (($seed % 1000) / 1000.0) * 0.6;
    $base = 0.9 + (log(max($populationDensity, 1.0) + 1.0) / log(10)) * 1.4;

    return round($base * $variation, 6);
}

function deterministicDamagedDensity($roadDensity, $areaId) {
    $seed = ($areaId * 214013 + 2531011) & 0x7fffffff;
    $ratio = 0.14 + (($seed % 1000) / 1000.0) * 0.18;
    return round($roadDensity * $ratio, 6);
}

try {
    $db = getDB();

    // Get layer type parameter
    $layer_type = isset($_GET['layer_type']) ? $_GET['layer_type'] : 'population';
    $admin_level = isset($_GET['admin_level']) ? strtolower($_GET['admin_level']) : 'kecamatan';

    // Validate layer type
    $valid_layers = ['population', 'road_density', 'damaged_roads_density', 'city_boundary'];
    if (!in_array($layer_type, $valid_layers)) {
        throw new Exception('Invalid layer type. Must be: ' . implode(', ', $valid_layers));
    }

    // Special handling for city boundary
    if ($layer_type === 'city_boundary') {
        $stmt = $db->prepare('SELECT id, name, geometry FROM pontianak_areas WHERE area_type = ?');
        $stmt->execute(['city']);
        $cities = $stmt->fetchAll(PDO::FETCH_ASSOC);

        $data_items = [];
        foreach ($cities as $city) {
            $data_items[] = [
                'area_id' => $city['id'],
                'area_name' => $city['name'],
                'value' => 0,  // No numeric value for boundary
                'geometry' => json_decode($city['geometry'], true)
            ];
        }

        http_response_code(200);
        echo json_encode([
            'success' => true,
            'layer_type' => 'city_boundary',
            'data' => $data_items,
            'min_value' => 0.0,
            'max_value' => 1.0,
            'admin_level' => 'city',
            'value_source' => 'boundary'
        ]);
        exit;
    }

    $valid_admin_levels = ['kecamatan', 'kelurahan'];
    if (!in_array($admin_level, $valid_admin_levels)) {
        throw new Exception('Invalid admin_level. Must be: ' . implode(', ', $valid_admin_levels));
    }

    // Get Pontianak areas by selected admin level
    $stmt = $db->prepare('SELECT id, name, geometry, population FROM pontianak_areas WHERE area_type = ? ORDER BY name');
    $stmt->execute([$admin_level]);
    $areas = $stmt->fetchAll(PDO::FETCH_ASSOC);

    if (empty($areas)) {
        // Fallback to kecamatan when kelurahan data does not exist.
        if ($admin_level === 'kelurahan') {
            $stmt->execute(['kecamatan']);
            $areas = $stmt->fetchAll(PDO::FETCH_ASSOC);
            $admin_level = 'kecamatan';
        }
    }

    if (empty($areas)) {
        throw new Exception('No Pontianak areas found for selected admin level. Run seed_pontianak_data.php?force=true first.');
    }

    $areaMetrics = [];
    foreach ($areas as $area) {
        $geometry = decodeGeometry($area['geometry']);
        if ($geometry === null) {
            continue;
        }

        $areaKm2 = geometryAreaKm2($geometry);
        if ($areaKm2 <= 0) {
            $areaKm2 = 0.01;
        }

        $population = (float)$area['population'];
        $populationDensity = $population / $areaKm2;

        $areaMetrics[(int)$area['id']] = [
            'id' => (int)$area['id'],
            'name' => $area['name'],
            'geometry' => $geometry,
            'area_km2' => $areaKm2,
            'centroid' => geometryCentroid($geometry),
            'population' => $population,
            'population_density' => $populationDensity
        ];
    }

    if (empty($areaMetrics)) {
        throw new Exception('Area geometry data is invalid for selected admin level.');
    }

    $areaRoadCounts = [];
    $areaDamagedRoadCounts = [];
    foreach ($areaMetrics as $metric) {
        $areaRoadCounts[$metric['id']] = 0;
        $areaDamagedRoadCounts[$metric['id']] = 0;
    }

    $roadsExist = false;
    $valueSource = 'computed';

    if ($layer_type === 'road_density' || $layer_type === 'damaged_roads_density') {
        $roadStmt = $db->query('SELECT id, coordinates, condition_status FROM roads');
        $roads = $roadStmt->fetchAll(PDO::FETCH_ASSOC);

        foreach ($roads as $road) {
            $coordinates = json_decode($road['coordinates'], true);
            $centroid = roadCentroidLngLat($coordinates);
            if ($centroid === null) {
                continue;
            }

            $roadsExist = true;

            $assignedAreaId = null;
            foreach ($areaMetrics as $areaMetric) {
                if (pointInGeometry($centroid['lng'], $centroid['lat'], $areaMetric['geometry'])) {
                    $assignedAreaId = $areaMetric['id'];
                    break;
                }
            }

            if ($assignedAreaId === null) {
                $nearestDistance = null;
                foreach ($areaMetrics as $areaMetric) {
                    $distance = haversineMeters(
                        $centroid['lat'],
                        $centroid['lng'],
                        $areaMetric['centroid']['lat'],
                        $areaMetric['centroid']['lng']
                    );

                    if ($nearestDistance === null || $distance < $nearestDistance) {
                        $nearestDistance = $distance;
                        $assignedAreaId = $areaMetric['id'];
                    }
                }
            }

            if ($assignedAreaId !== null) {
                $areaRoadCounts[$assignedAreaId]++;
                if (in_array($road['condition_status'], ['minor_damage', 'major_damage'])) {
                    $areaDamagedRoadCounts[$assignedAreaId]++;
                }
            }
        }

        if (!$roadsExist) {
            $valueSource = 'dummy';
        }
    }

    $data_items = [];
    $values = [];

    foreach ($areaMetrics as $areaMetric) {
        $area_id = $areaMetric['id'];
        $geometry = $areaMetric['geometry'];
        $value = 0;

        // Calculate value based on layer type
        if ($layer_type === 'population') {
            $value = $areaMetric['population_density'];

        } else if ($layer_type === 'road_density') {
            if ($roadsExist) {
                $value = $areaRoadCounts[$area_id] / $areaMetric['area_km2'];
            } else {
                $value = deterministicRoadDensity($areaMetric['population_density'], $area_id);
            }

        } else if ($layer_type === 'damaged_roads_density') {
            if ($roadsExist) {
                $value = $areaDamagedRoadCounts[$area_id] / $areaMetric['area_km2'];
            } else {
                $fallbackRoadDensity = deterministicRoadDensity($areaMetric['population_density'], $area_id);
                $value = deterministicDamagedDensity($fallbackRoadDensity, $area_id);
            }
        }

        $value = (float)$value;
        $values[] = $value;

        $data_items[] = [
            'area_id' => $area_id,
            'area_name' => $areaMetric['name'],
            'value' => $value,
            'geometry' => $geometry,
            'area_km2' => round($areaMetric['area_km2'], 6),
            'population' => $areaMetric['population'],
            'population_density' => round($areaMetric['population_density'], 6),
            'value_source' => $valueSource
        ];
    }

    // Calculate min and max values
    $min_value = count($values) > 0 ? min($values) : 0;
    $max_value = count($values) > 0 ? max($values) : 0;

    // If all values are same, set reasonable range
    if ($min_value === $max_value && $min_value > 0) {
        $min_value = $min_value * 0.8;
        $max_value = $max_value * 1.2;
    } else if ($min_value === $max_value) {
        $min_value = 0;
        $max_value = 1;
    }

    // Return success response
    http_response_code(200);
    echo json_encode([
        'success' => true,
        'layer_type' => $layer_type,
        'data' => $data_items,
        'min_value' => floatval($min_value),
        'max_value' => floatval($max_value),
        'admin_level' => $admin_level,
        'value_source' => $valueSource
    ]);

} catch (PDOException $e) {
    http_response_code(500);
    echo json_encode([
        'success' => false,
        'error' => 'Database error: ' . $e->getMessage()
    ]);
} catch (Exception $e) {
    http_response_code(400);
    echo json_encode([
        'success' => false,
        'error' => $e->getMessage()
    ]);
}
?>
