<?php

// --- Error Reporting (Recommended for Development) ---
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// In production, turn display_errors off and configure log_errors.
// ini_set('display_errors', 0);
// ini_set('log_errors', 1);
// ini_set('error_log', '/path/to/your/php_error.log');

// --- Database Connection ---
$dbconn = pg_connect("host=localhost dbname=nws user=nws password=nws");

if (!$dbconn) {
    error_log('Database connection failed: ' . pg_last_error());
    http_response_code(503);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode(['error' => 'Service temporarily unavailable due to database connection issue.']);
    exit;
}

// --- Helper Functions ---

/**
 * Sends a JSON error response and terminates the script.
 * @param int $http_code The HTTP status code.
 * @param string $message The error message for the client.
 * @param ?string $log_message Optional detailed message for the server error log.
 */
function send_error(int $http_code, string $message, ?string $log_message = null): void {
    if ($log_message) { error_log($log_message); }
    elseif ($http_code >= 500) { error_log("Server Error (" . $http_code . "): " . $message); }
    http_response_code($http_code);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode(['error' => $message]);
    exit;
}

/**
 * Sends a GeoJSON FeatureCollection response and terminates the script.
 * @param array $features An array of GeoJSON Feature objects.
 */
function send_geojson(array $features): void {
    $geojson_output = ['type' => 'FeatureCollection', 'features' => $features];
    header('Content-Type: application/geo+json; charset=utf-8');
    echo json_encode($geojson_output);
    exit;
}

// --- Main Request Handling ---

if ($_SERVER['REQUEST_METHOD'] === 'POST') {

    error_log("--- New POST Request Received ---");
    $input_data = null;
    $request_type = null;
    $contentType = trim(strtolower($_SERVER['HTTP_CONTENT_TYPE'] ?? $_SERVER['CONTENT_TYPE'] ?? ''));
    error_log("Received Content-Type: " . $contentType);

    // ***********************************************************
    // ***** START: CRUCIAL JSON INPUT HANDLING BLOCK *****
    // ***********************************************************
    if (strpos($contentType, 'application/json') === 0) {
        error_log("Content-Type identified as JSON.");
        $raw_post_data = file_get_contents('php://input');
        error_log("Raw php://input length: " . strlen($raw_post_data));

        if ($raw_post_data === false || $raw_post_data === '') {
             send_error(400, 'Received empty request body or could not read input.', "Error: Could not read php://input or it was empty.");
        }

        // Decode JSON into an associative array
        $input_data = json_decode($raw_post_data, true); // Use 'true' for array

        if (json_last_error() !== JSON_ERROR_NONE) {
            send_error(400, 'Invalid JSON payload received.', 'JSON Decode Error: ' . json_last_error_msg() . " | Raw data snippet: " . substr($raw_post_data, 0, 100));
        } elseif (!is_array($input_data)) {
             send_error(400, 'Invalid JSON payload: Expected a JSON object.', "JSON Decode Warning: Result is not an array. Data: " . print_r($input_data, true));
        } else {
             error_log("JSON Decode Successful.");
             // ** GET request_type FROM THE DECODED ARRAY **
             $request_type = $input_data['request_type'] ?? null;
             error_log("Extracted request_type from JSON: " . ($request_type ?? 'null'));
        }
    } else {
        // If JSON is strictly required, reject other types
        send_error(415, 'Unsupported Media Type. This endpoint requires application/json.', "Unsupported Media Type Received: " . $contentType);
    }
    // ***********************************************************
    // ***** END: CRUCIAL JSON INPUT HANDLING BLOCK *****
    // ***********************************************************


    // --- Final Check and Routing ---
    if ($request_type === null) {
        if (is_array($input_data) && !isset($input_data['request_type'])) {
            send_error(400, 'Missing "request_type" field within the request payload.');
        } else {
            error_log("Routing check reached but request_type is null without prior exit.");
            send_error(400, 'Missing required parameter: request_type (or processing error).');
        }
    }

    error_log("Routing request for type: " . $request_type);
    switch ($request_type) {
        case 'ohgo':
            // ** Pass the $input_data array **
            handle_ohgo_request($dbconn, $input_data);
            break;
        case 'ohgonopoly':
            // ** Pass the $input_data array **
            handle_ohgo_request_no_poly($dbconn, $input_data);
            break;            
        case 'power':
            // ** Pass the $input_data array **
            handle_power_request($dbconn, $input_data);
            break;
        case 'powernopoly':
            // ** Pass the $input_data array **
            handle_power_request_no_poly($dbconn, $input_data);
            break;
        case 'wupoly':
            // ** Pass the $input_data array **
            handle_wu_request_poly($dbconn, $input_data);
            break;   
        case 'campoly':
            // ** Pass the $input_data array **
            handle_cam_request($dbconn, $input_data);
            break; 	
        
            
            
        default:
            send_error(400, 'Invalid request_type specified: ' . htmlspecialchars($request_type));
            break;
    }

} else {
    http_response_code(405);
    header('Allow: POST');
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode(['error' => 'Invalid request method. Only POST is allowed.']);
    exit;
}




// --- Request Handler Functions ---

 
 
function handle_cam_request($dbconn, array $data): void {
    error_log("Handling 'camera image' request.");

    // --- 1. Get Data from the $data array ---
    $start_time_str = $data['start_time'] ?? null;
    $end_time_str = $data['end_time'] ?? null;
    $geojson_str = $data['area_geojson'] ?? null;

    // --- 2. Validation ---
    if ($start_time_str === null || $end_time_str === null || $geojson_str === null) {
        send_error(400, 'Missing required parameters for camera request: start_time, end_time, area_geojson');
    }

    // Validate Timestamps (basic check, can be more robust)
    // Consider using DateTime objects for more rigorous validation if needed
    if (strtotime($start_time_str) === false) {
         send_error(400, 'Invalid start_time format.');
    }
     if (strtotime($end_time_str) === false) {
         send_error(400, 'Invalid end_time format.');
    }
    // Ensure start is before end? Optional, depends on requirements.
    // if (strtotime($start_time_str) >= strtotime($end_time_str)) {
    //     send_error(400, 'start_time must be before end_time.');
    // }


    // Validate GeoJSON
    $geojson_obj = json_decode($geojson_str);
    if (json_last_error() !== JSON_ERROR_NONE) {
        send_error(400, 'Invalid area_geojson provided: Contains invalid JSON string.', 'GeoJSON Decode Error: ' . json_last_error_msg());
    }
    if (!is_object($geojson_obj) || !isset($geojson_obj->type) || !in_array($geojson_obj->type, ['Polygon', 'MultiPolygon'])) {
        send_error(400, 'Invalid area_geojson provided: Decoded JSON must be a Polygon or MultiPolygon object.');
    }

    // --- 3. Prepare and Execute Query ---
    // This query finds active cameras within the GeoJSON area,
    // then LEFT JOINs aggregated image data from camdb within the time range.
    // We use jsonb_agg for efficiency and COALESCE to return an empty array []
    // for cameras with no images in the range, instead of NULL.
    // NOTE: Selecting c.* assumes 'geom' is not excessively large or problematic
    //       when fetched directly. If it is, list all columns except 'geom'.
    //       We explicitly fetch ST_AsGeoJSON for the geometry representation.
    $query = "
        SELECT
            c.*,                                    -- Select all columns from cams
            ST_AsGeoJSON(c.geom) as geometry_geojson, -- Get geometry as GeoJSON string
            COALESCE(img_agg.images, '[]'::jsonb) AS images -- Get aggregated images or empty JSON array
        FROM
            cams c
        LEFT JOIN (
            SELECT
                camid,
                jsonb_agg(
                    jsonb_build_object(
                        'timestamp', dateutc,
                        'url', filepath -- Assuming filepath is the relative URL path
                    ) ORDER BY dateutc ASC -- Order images chronologically
                ) AS images
            FROM
                camdb
            WHERE
                dateutc >= $1::timestamp -- start_time
                AND dateutc <= $2::timestamp -- end_time
            GROUP BY
                camid
        ) AS img_agg ON c.camid = img_agg.camid
        WHERE
            c.active = TRUE                         -- Only active cameras
            AND ST_Within(c.geom, ST_GeomFromGeoJSON($3)) -- Camera location within area
        ORDER BY
            c.camid; -- Optional: Order cameras by ID
    ";

    $params = array(
        $start_time_str, // $1: start_time
        $end_time_str,   // $2: end_time
        $geojson_str     // $3: area_geojson string
    );

    $result = pg_query_params($dbconn, $query, $params);

    if (!$result) {
        send_error(500, 'Database query failed for camera data.', 'Camera Query Failed: ' . pg_last_error($dbconn) . " | Query: " . $query . " | Params: " . print_r($params, true));
    }

    // --- 4. Process Results ---
    $cameras_output = [];
    while ($row = pg_fetch_assoc($result)) {
        // Decode the geometry GeoJSON string into a PHP object/array
        $geometry = json_decode($row['geometry_geojson']);
        if (json_last_error() !== JSON_ERROR_NONE) {
            error_log('Failed to decode geometry for camid ' . ($row['camid'] ?? 'N/A') . ': ' . json_last_error_msg());
            // Decide how to handle: skip camera, set geometry to null, etc.
            $geometry = null; // Example: Set to null on error
        }

        // Decode the images JSON string (from jsonb_agg) into a PHP array
        $images = json_decode($row['images']);
         if (json_last_error() !== JSON_ERROR_NONE) {
            error_log('Failed to decode images JSON for camid ' . ($row['camid'] ?? 'N/A') . ': ' . json_last_error_msg());
            // Decide how to handle: skip camera, set images to empty array, etc.
            $images = []; // Example: Set to empty array on error
        }

        // Prepare the output structure for this camera
        $camera_data = $row; // Start with all columns fetched via c.*

        // Replace/remove raw JSON strings and potentially the original binary geom
        unset($camera_data['geometry_geojson']); // Remove the raw GeoJSON string
        unset($camera_data['geom']); // Remove the raw binary geometry if it was fetched by c.*
        $camera_data['geometry'] = $geometry; // Add the decoded geometry object/array
        $camera_data['images'] = $images;     // Add the decoded images array

        $cameras_output[] = $camera_data;
    }
    pg_free_result($result);
    error_log("Found " . count($cameras_output) . " cameras matching criteria.");

    // --- 5. Send Response ---
    // Use a function like send_json defined above, or inline the logic:
    header('Content-Type: application/json');
    echo json_encode($cameras_output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
    exit; // Important to stop script execution here
    // Alternatively, if you have the helper:
    // send_json($cameras_output);
}

function handle_wu_request_poly($dbconn, array $data): void { // Takes $data array

$polygons = $data['polygons'] ?? []; // Array of WKT polygons, e.g., ['POLYGON((...))', 'POLYGON((...))']
$start_time = $data['start_time'] ?? '2025-01-01 00:00:00'; // e.g., '2025-01-01 00:00:00'
$end_time = $data['end_time'] ?? '2025-01-02 00:00:00'; // e.g., '2025-01-02 00:00:00'

if (empty($polygons)) {
    http_response_code(500);
    echo json_encode(['error' => 'No polygons provided']);
    pg_close($dbconn);
    exit;
}

$polygon_placeholders = [];
$params = [];
$param_index = 1;

foreach ($polygons as $polygon) {
    $polygon_placeholders[] = "ST_GeomFromText(\$$param_index, 4326)";
    $params[] = $polygon;
    $param_index++;
}

$params[] = $start_time;
$params[] = $end_time;
$start_time_placeholder = "\$$param_index";
$param_index++;
$end_time_placeholder = "\$$param_index";

$polygon_sql = implode(', ', $polygon_placeholders);

$sql = "
    SELECT wo.*
    FROM wuobs wo
    JOIN wusites ws ON wo.stationid = ws.stationid
    WHERE ws.geom && ST_Union(ARRAY[$polygon_sql])::geometry
    AND ST_Within(ws.geom, ST_Union(ARRAY[$polygon_sql])::geometry)
    AND wo.observation_time BETWEEN $start_time_placeholder AND $end_time_placeholder
";

$result = pg_query_params($dbconn, $sql, $params);

if ($result === false) {
    http_response_code(500);
    echo json_encode(['error' => pg_last_error($dbconn)]);
    pg_close($dbconn);
    exit;
}

// Fetch results
$results = [];
while ($row = pg_fetch_assoc($result)) {
    $results[] = $row;
}

// Free result and close connection
pg_free_result($result);

// Output results as JSON
header('Content-Type: application/json');
echo json_encode($results);
}

/**
 * Handles the 'ohgo' data request.
 * @param resource $dbconn The active database connection resource.
 * @param array $data The associative array of input parameters (from JSON).
 */
function handle_ohgo_request($dbconn, array $data): void { // Takes $data array
    error_log("Handling 'ohgo' request.");
    // --- 1. Get Data from the $data array ---
    $start = $data['start_time'] ?? null;         // Use $data, use correct key
    $geojson_str = $data['area_geojson'] ?? null; // Use $data, not $_POST
    $end = $data['end_time'] ?? null;             // Use $data, use correct key

    // --- 2. Validation ---
    if ($start === null || $geojson_str === null || $end === null) { send_error(400, 'Missing required parameters for ohgo request: start, geojson, end'); }
    $geojson_obj = json_decode($geojson_str);
    if (json_last_error() !== JSON_ERROR_NONE) { send_error(400, 'Invalid GeoJSON provided: Not valid JSON.', 'GeoJSON Decode Error: ' . json_last_error_msg()); }
    if (!isset($geojson_obj->type) || !in_array($geojson_obj->type, ['Polygon', 'MultiPolygon'])) { send_error(400, 'Invalid GeoJSON provided: Type must be Polygon or MultiPolygon.'); }

    // --- 3. Prepare and Execute Query ---
    $query = "SELECT ST_AsGeoJSON(geom) AS geometry, category, roadstatus, county, state, location, routename, description, start AS start_timestamp, endtime AS end_timestamp, lastupdate FROM ohgo WHERE start > $1::timestamp AND start < $3::timestamp AND ST_Within(geom, ST_GeomFromGeoJSON($2)) ORDER BY start ASC";
    $params = array($start, $geojson_str, $end);
    $result = pg_query_params($dbconn, $query, $params);
    if (!$result) { send_error(500, 'Database query failed for ohgo data.', 'OHGO Query Failed: ' . pg_last_error($dbconn)); }

    // --- 4. Process Results ---
    $features = [];
    while ($line = pg_fetch_assoc($result)) {
        $geometry = json_decode($line['geometry']);
        if (json_last_error() !== JSON_ERROR_NONE) { error_log('Failed to decode geometry for ohgo row: ' . json_last_error_msg()); continue; }
        $properties = $line; unset($properties['geometry']);
        $features[] = ['type' => 'Feature', 'geometry' => $geometry, 'properties' => $properties];
    }
    pg_free_result($result);
    error_log("Found " . count($features) . " features for ohgo request.");

    // --- 5. Send Response ---
    send_geojson($features);
}


/**
 * Handles the 'power' data request.
 * @param resource $dbconn The active database connection resource.
 * @param array $data The associative array of input parameters (from JSON).
 */
function handle_power_request($dbconn, array $data): void { // Takes $data array
    error_log("Handling 'power' request.");
    // --- 1. Get Data from the $data array ---
    // ** Match keys from your fetch request body: start_time, area_geojson, etc. **
    $start = $data['start_time'] ?? null;         // Use $data, use correct key
    $geojson_str = $data['area_geojson'] ?? null; // Use $data, use correct key
    $end = $data['end_time'] ?? null;             // Use $data, use correct key
    $buffer_hours = $data['buffer'] ?? 0;// Use $data, use correct key

    // --- 2. Validation ---
    if ($start === null || $geojson_str === null || $end === null || $buffer_hours === null) {
        // Update error message to reflect the actual keys expected from JSON
        send_error(400, 'Missing required parameters for power request: start_time, area_geojson, end_time, buffer_hours');
    }
    if (!is_numeric($buffer_hours) || ($buffer_hours_float = floatval($buffer_hours)) < 0) { send_error(400, 'Invalid buffer_hours provided: Must be a non-negative number.'); }
    $buffer_hours_int = (int)$buffer_hours_float;
    $geojson_obj = json_decode($geojson_str); // Decode the *string* value from the JSON input
    if (json_last_error() !== JSON_ERROR_NONE) { send_error(400, 'Invalid area_geojson provided: Contains invalid JSON string.', 'GeoJSON Decode Error: ' . json_last_error_msg()); }
    if (!is_object($geojson_obj) || !isset($geojson_obj->type) || !in_array($geojson_obj->type, ['Polygon', 'MultiPolygon'])) { send_error(400, 'Invalid area_geojson provided: Decoded JSON must be a Polygon or MultiPolygon object.'); }
    // ** Crucial Fix: Use the decoded $geojson_str for the query parameter, not $geojson_obj **

    // --- 3. Prepare and Execute Query ---
    // ** VERIFY TABLE/COLUMN NAMES FOR POWER TABLE **
    $query = "SELECT ST_AsGeoJSON(realgeom) AS geometry, derivedstart AS start_timestamp, cause, peakoutage, lastchange AS end_timestamp FROM power WHERE derivedstart >= $1::timestamp AND derivedstart < ($3::timestamp + make_interval(hours => $4::integer)) AND ST_Within(realgeom, ST_GeomFromGeoJSON($2)) ORDER BY derivedstart ASC";
    $params = array(
        $start,            // $1: start_time from JSON
        $geojson_str,      // $2: area_geojson STRING from JSON
        $end,              // $3: end_time from JSON
        $buffer_hours_int  // $4: buffer_hours from JSON (as integer)
    );
    $result = pg_query_params($dbconn, $query, $params);
    if (!$result) { send_error(500, 'Database query failed for power data.', 'Power Query Failed: ' . pg_last_error($dbconn) . " | Query: " . $query . " | Params: " . print_r($params, true)); }

    // --- 4. Process Results ---
    $features = [];
    while ($line = pg_fetch_assoc($result)) {
        $geometry = json_decode($line['geometry']);
        if (json_last_error() !== JSON_ERROR_NONE) { error_log('Failed to decode geometry for power row: ' . json_last_error_msg()); continue; }
        $properties = $line; unset($properties['geometry']);
        $features[] = ['type' => 'Feature', 'geometry' => $geometry, 'properties' => $properties];
    }
    pg_free_result($result);
    error_log("Found " . count($features) . " features for power request.");

    // --- 5. Send Response ---
    send_geojson($features);
}


/**
 * Handles the 'ohgo' data request.
 * @param resource $dbconn The active database connection resource.
 * @param array $data The associative array of input parameters (from JSON).
 */
function handle_ohgo_request_no_poly($dbconn, array $data): void { // Takes $data array
    error_log("Handling 'ohgo' request.");
    // --- 1. Get Data from the $data array ---
    $start = $data['start_time'] ?? null;         // Use $data, use correct key

    $end = $data['end_time'] ?? null;             // Use $data, use correct key

    // --- 2. Validation ---
    if ($start === null || $end === null) { send_error(400, 'Missing required parameters for ohgo request: start, geojson, end'); }

    // --- 3. Prepare and Execute Query ---
    $query = "SELECT ST_AsGeoJSON(geom) AS geometry, county, state AS st, location, routename AS city, upper(cwa) AS wfo, 'FLOOD' AS typetext, 'Department of Highways' AS source, description AS remark, 
        TO_CHAR(start, 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') AS valid 
        FROM ohgo 
        WHERE start > $1::timestamp 
        AND start < $2::timestamp 
        AND cwa = 'RLX' 
        ORDER BY start ASC";
    $params = array($start, $end);
    $result = pg_query_params($dbconn, $query, $params);
    if (!$result) { send_error(500, 'Database query failed for ohgo data.', 'OHGO Query Failed: ' . pg_last_error($dbconn)); }

    // --- 4. Process Results ---
    $features = [];
    while ($line = pg_fetch_assoc($result)) {
        $geometry = json_decode($line['geometry']);
        if (json_last_error() !== JSON_ERROR_NONE) { error_log('Failed to decode geometry for ohgo row: ' . json_last_error_msg()); continue; }
        $properties = $line; unset($properties['geometry']);
        $features[] = ['type' => 'Feature', 'geometry' => $geometry, 'properties' => $properties];
    }
    pg_free_result($result);
    error_log("Found " . count($features) . " features for ohgo request.");

    // --- 5. Send Response ---
    send_geojson($features);
}


/**
 * Handles the 'power' data request.
 * @param resource $dbconn The active database connection resource.
 * @param array $data The associative array of input parameters (from JSON).
 */
function handle_power_request_no_poly($dbconn, array $data): void { // Takes $data array
    error_log("Handling 'power' request.");
    // --- 1. Get Data from the $data array ---
    // ** Match keys from your fetch request body: start_time, area_geojson, etc. **
    $start = $data['start_time'] ?? null;         // Use $data, use correct key
    $end = $data['end_time'] ?? null;             // Use $data, use correct key
	$outage_threshold = $data['outage_threshold'] ?? 9;
    $buffer_hours = $data['buffer'] ?? 0;// Use $data, use correct key

    // --- 2. Validation ---
    if ($start === null || $end === null || $buffer_hours === null) {
        // Update error message to reflect the actual keys expected from JSON
        send_error(400, 'Missing required parameters for power request: start_time, area_geojson, end_time, buffer_hours');
    }
    if (!is_numeric($buffer_hours) || ($buffer_hours_float = floatval($buffer_hours)) < 0) { send_error(400, 'Invalid buffer_hours provided: Must be a non-negative number.'); }
    $buffer_hours_int = (int)$buffer_hours_float;
    $outage_thresh = (float)$outage_threshold;
    
    
    

    // --- 3. Prepare and Execute Query ---
    // ** VERIFY TABLE/COLUMN NAMES FOR POWER TABLE **
    $query = "SELECT ST_AsGeoJSON(realgeom) AS geometry, 
              TO_CHAR(derivedstart, 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') AS valid, 
              ('Power Outage affecting ' || peakoutage || ' customers caused by ' || COALESCE(cause, 'unknown')) AS remark, 
              'Utility Company' as source, 
              'POWER OUTAGE' as typetext, 
              'U' as type, 
              (ROUND(ST_Y(realgeom)::numeric, 3) || ', ' || ROUND(ST_X(realgeom)::numeric, 3)) AS city, 
			  county as county,
			  state as state,
              state as st			  
              FROM power 
              WHERE derivedstart >= $1::timestamp 
              AND derivedstart < ($2::timestamp + make_interval(hours => $3::integer)) 
			  and peakoutage > $4
              AND ST_Within(realgeom, (SELECT geom FROM public.cwa WHERE cwa = 'RLX')) 
              ORDER BY derivedstart ASC";

	
	
	
	
    $params = array(
        $start,            // $1: start_time from JSON
        $end,              // $2: end_time from JSON
        $buffer_hours_int,  // $3: buffer_hours from JSON (as integer)
		$outage_thresh // $4
    );
    $result = pg_query_params($dbconn, $query, $params);
    if (!$result) { send_error(500, 'Database query failed for power data.', 'Power Query Failed: ' . pg_last_error($dbconn) . " | Query: " . $query . " | Params: " . print_r($params, true)); }

    // --- 4. Process Results ---
    $features = [];
    while ($line = pg_fetch_assoc($result)) {
        $geometry = json_decode($line['geometry']);
        if (json_last_error() !== JSON_ERROR_NONE) { error_log('Failed to decode geometry for power row: ' . json_last_error_msg()); continue; }
        $properties = $line; unset($properties['geometry']);
        $features[] = ['type' => 'Feature', 'geometry' => $geometry, 'properties' => $properties];
    }
    pg_free_result($result);
    error_log("Found " . count($features) . " features for power request.");

    // --- 5. Send Response ---
    send_geojson($features);
}











// --- Close Database Connection ---
if ($dbconn) {
    pg_close($dbconn);
    error_log("Database connection closed.");
}

?>