<?php

declare(strict_types=1);

/**
 * APO-186 Card 3 / Card 5 — /api/v1/... front-controller dispatch.
 *
 * Reads the incoming request, normalises headers + JSON body, hands off to
 * the action dispatcher, and emits the canonical envelope as JSON. The
 * dispatcher contains all auth, scope, schema, and routing logic.
 */

$method = strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET'));
$uri    = (string) ($_SERVER['REQUEST_URI'] ?? '/');

// Strip any base-path prefix that AppFrontController has already resolved
// to leave the canonical /api/v1/... slug.
$path = parse_url($uri, PHP_URL_PATH) ?: $uri;
$basePath = (string) (function_exists('myronAdminBasePath') ? myronAdminBasePath() : '');
if ($basePath !== '' && ($path === $basePath || str_starts_with($path, $basePath . '/'))) {
    $path = substr($path, strlen($basePath));
}
if ($path === '' || $path[0] !== '/') {
    $path = '/' . ltrim($path, '/');
}

// Normalise request headers into a case-insensitive map.
$headers = [];
if (function_exists('getallheaders')) {
    $raw = getallheaders();
    if (is_array($raw)) {
        foreach ($raw as $name => $value) {
            $headers[(string) $name] = (string) $value;
        }
    }
}
// Fallback: scan $_SERVER for HTTP_* prefix.
foreach ($_SERVER as $key => $value) {
    if (!is_string($key) || !str_starts_with($key, 'HTTP_')) {
        continue;
    }
    $headerName = strtolower(substr($key, 5));
    $headerName = str_replace('_', '-', $headerName);
    if (!isset($headers[$headerName]) && !isset($headers[ucwords($headerName, '-')])) {
        $headers[ucwords($headerName, '-')] = (string) $value;
    }
}
foreach (['HTTP_AUTHORIZATION', 'REDIRECT_HTTP_AUTHORIZATION'] as $key) {
    $value = $_SERVER[$key] ?? null;
    if (!is_string($value) || trim($value) === '') {
        continue;
    }
    if (!isset($headers[$key])) {
        $headers[$key] = $value;
    }
    if (!isset($headers['Authorization']) && str_ends_with($key, 'AUTHORIZATION')) {
        $headers['Authorization'] = $value;
    }
}

/**
 * @param array<string, mixed> $input
 * @return array<string, string>
 */
function myronApiV1FileUploadMetadata(array $input): array
{
    $metadata = [];
    foreach (['alt_text', 'title', 'description', 'caption', 'asset_class'] as $key) {
        if (array_key_exists($key, $input)) {
            $metadata[$key] = trim((string) $input[$key]);
        }
    }

    return $metadata;
}

$contentType = strtolower(trim(explode(';', (string) ($_SERVER['CONTENT_TYPE'] ?? ''), 2)[0]));
$isMultipartFileUpload = $method === 'POST'
    && in_array($path, ['/api/v1/files', '/api/v1/files/multipart'], true)
    && $contentType === 'multipart/form-data';

// Decode JSON body for non-GET methods.
$body = [];
if ($isMultipartFileUpload) {
    $path = '/api/v1/files/multipart';
    $body = [
        'metadata' => myronApiV1FileUploadMetadata($_POST),
    ];
    if (isset($_FILES['file']) && is_array($_FILES['file'])) {
        $body['uploadedFile'] = $_FILES['file'];
    }
} elseif ($method !== 'GET' && $method !== 'DELETE') {
    $raw = (string) file_get_contents('php://input');
    if ($raw !== '') {
        $decoded = json_decode($raw, true);
        if (is_array($decoded)) {
            $body = $decoded;
        }
    }
}
// GET requests fold $_GET into the input so query-string filters reach actions.
if ($method === 'GET' && !empty($_GET)) {
    $body = array_merge($body, $_GET);
}

$response = myronActionDispatcher()->dispatch($method, $path, $headers, $body);
if ($method === 'GET' && preg_match('#^/api/v1/content/pages/[^/]+/markup$#', $path) === 1 && empty($response['envelope']['ok'])) {
    $code = (string) ($response['envelope']['error']['code'] ?? '');
    if ($code === 'not_found') {
        $response['status'] = 404;
    } elseif ($code === 'not_indexable') {
        $response['status'] = 403;
    }
}
if ($method === 'GET' && preg_match('#^/api/v1/content/pages/[^/]+/markdown$#', $path) === 1 && empty($response['envelope']['ok'])) {
    $code = (string) ($response['envelope']['error']['code'] ?? '');
    if ($code === 'not_found') {
        $response['status'] = 404;
    } elseif ($code === 'not_indexable') {
        $response['status'] = 403;
    }
}

http_response_code((int) ($response['status'] ?? 500));
header('Content-Type: application/json; charset=utf-8');
header('X-Content-Type-Options: nosniff');
if ($method === 'GET' && $path === '/api/v1/site/snapshot/export' && (bool) ($response['envelope']['ok'] ?? false)) {
    $filename = (string) ($response['envelope']['result']['filename'] ?? 'myroncms-site-snapshot.json');
    header('Content-Disposition: attachment; filename="' . str_replace('"', '', $filename) . '"');
}
if ($method === 'GET' && preg_match('#^/api/v1/content/pages/[^/]+/markup$#', $path) === 1 && (bool) ($response['envelope']['ok'] ?? false)) {
    $result = is_array($response['envelope']['result'] ?? null) ? $response['envelope']['result'] : [];
    $filename = (string) ($result['filename'] ?? 'page.html');
    $body = (string) ($result['body'] ?? '');

    header_remove('Content-Type');
    header('Content-Type: text/html; charset=utf-8');
    header('Content-Disposition: attachment; filename="' . str_replace('"', '', $filename) . '"');
    echo $body;
    return;
}
if ($method === 'GET' && preg_match('#^/api/v1/content/pages/[^/]+/markdown$#', $path) === 1 && (bool) ($response['envelope']['ok'] ?? false)) {
    $result = is_array($response['envelope']['result'] ?? null) ? $response['envelope']['result'] : [];
    $filename = (string) ($result['filename'] ?? 'page.md');
    $body = (string) ($result['body'] ?? '');

    header_remove('Content-Type');
    header('Content-Type: text/markdown; charset=utf-8');
    header('Content-Disposition: attachment; filename="' . str_replace('"', '', $filename) . '"');
    echo $body;
    return;
}
echo json_encode($response['envelope'], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
