<?php

/**
 * Exam Diagram Adapter (Phase 4)
 *
 * Bridges the clean Phase-3 diagram spec ({type, title, data, labels, caption})
 * to the mature renderer in includes/diagram_renderer.php (renderDiagram()),
 * which emits exact, code-rendered SVG/HTML.
 *
 * Flow: adapt spec -> renderDiagram() [accurate, primary]
 *       -> AI raster fallback [extension point]
 *       -> descriptive placeholder [last resort].
 */

require_once dirname(__DIR__) . '/includes/diagram_renderer.php';

/**
 * Translate a normalized Phase-3 diagram spec into the shape renderDiagram()
 * expects for that type. Returns null when the type cannot be drawn exactly
 * (caller then falls back).
 */
function adaptExamDiagramSpec(array $spec): ?array {
    $type    = strtolower(trim((string) ($spec['type'] ?? '')));
    $data    = is_array($spec['data'] ?? null) ? $spec['data'] : [];
    $labels  = is_array($spec['labels'] ?? null) ? array_values($spec['labels']) : [];
    $caption = (string) ($spec['caption'] ?? '');

    switch ($type) {
        case 'bar_chart':
            $map = _examDiagramPairsToMap($data, $labels);
            return $map === null ? null : ['type' => 'bar_chart', 'data' => $map, 'caption' => $caption];

        case 'pie_chart':
            $map = _examDiagramPairsToMap($data, $labels);
            return $map === null ? null : ['type' => 'pie_chart', 'data' => $map, 'caption' => $caption];

        case 'line_chart':
            $points = [];
            if (isset($data['x'], $data['y']) && is_array($data['x']) && is_array($data['y'])) {
                foreach (array_values($data['x']) as $i => $x) {
                    $points[(string) $x] = (float) (array_values($data['y'])[$i] ?? 0);
                }
            } elseif (isset($data['points']) && is_array($data['points'])) {
                $points = $data['points'];
            } else {
                $map = _examDiagramPairsToMap($data, $labels);
                if ($map === null) { return null; }
                $points = $map;
            }
            return [
                'type'    => 'line_chart',
                'points'  => $points,
                'x_label' => $labels[0] ?? '',
                'y_label' => $labels[1] ?? '',
                'caption' => $caption,
            ];

        case 'number_line':
            $points = [];
            if (isset($data['points']) && is_array($data['points'])) {
                $points = $data['points'];
            } elseif (isset($data['marks']) && is_array($data['marks'])) {
                foreach ($data['marks'] as $m) { $points[] = ['value' => (float) $m]; }
            }
            return [
                'type'    => 'number_line',
                'min'     => (float) ($data['min'] ?? -5),
                'max'     => (float) ($data['max'] ?? 10),
                'step'    => (float) ($data['step'] ?? 1),
                'points'  => $points,
                'caption' => $caption,
            ];

        case 'coordinate_plane':
            return [
                'type'    => 'coordinate_plane',
                'x_range' => is_array($data['x_range'] ?? null) ? $data['x_range'] : [-5, 5],
                'y_range' => is_array($data['y_range'] ?? null) ? $data['y_range'] : [-5, 5],
                'points'  => is_array($data['points'] ?? null) ? $data['points'] : [],
                'lines'   => is_array($data['lines'] ?? null) ? $data['lines'] : [],
                'caption' => $caption,
            ];

        case 'table':
            $headers = is_array($data['headers'] ?? null) ? $data['headers'] : $labels;
            $rows    = is_array($data['rows'] ?? null) ? $data['rows'] : [];
            if (empty($headers) && empty($rows)) { return null; }
            return ['type' => 'table', 'headers' => $headers, 'rows' => $rows, 'caption' => $caption];

        case 'triangle':
            return ['type' => 'geometry', 'svg_hint' => 'triangle label=ABC', 'caption' => $caption];

        case 'right_triangle':
            $sides = is_array($data['sides'] ?? null) ? array_values($data['sides']) : [];
            $hint  = 'right_triangle';
            if (count($sides) >= 3) {
                $hint .= ' a=' . (int) $sides[0] . ' b=' . (int) $sides[1] . ' c=' . (int) $sides[2];
            }
            return ['type' => 'geometry', 'svg_hint' => $hint, 'caption' => $caption];

        case 'rectangle':
            $w = (int) ($data['width'] ?? $data['w'] ?? 120);
            $h = (int) ($data['height'] ?? $data['h'] ?? 80);
            return ['type' => 'geometry', 'svg_hint' => "rect w={$w} h={$h} label=ABCD", 'caption' => $caption];

        case 'circle':
            $r = (int) ($data['radius'] ?? $data['r'] ?? 60);
            return ['type' => 'geometry', 'svg_hint' => "circle r={$r}", 'caption' => $caption];

        default:
            // 'angle' and any other type the SVG renderer cannot draw exactly.
            return null;
    }
}

/**
 * Render a Phase-3 diagram spec to print-ready SVG/HTML.
 * Always returns a string (accurate render, AI raster, or placeholder).
 */
function renderExamDiagram(array $spec): string {
    $adapted = adaptExamDiagramSpec($spec);
    if ($adapted !== null) {
        return renderDiagram($adapted);
    }

    // Fallback chain for specs the code renderer can't draw exactly.
    $raster = generateDiagramRasterFallback($spec);
    if ($raster !== null) {
        return $raster;
    }

    // Last resort: a clean, labelled placeholder describing the intended figure.
    return renderDiagram([
        'type'        => 'diagram',
        'description' => (string) ($spec['caption'] ?? $spec['title'] ?? ($spec['type'] ?? 'Diagram')),
        'caption'     => (string) ($spec['caption'] ?? ''),
    ]);
}

/**
 * AI raster-image fallback extension point.
 *
 * For specs the code renderer cannot draw exactly (e.g. labelled biology
 * figures, "angle"), an image-capable provider could generate a raster here.
 * Returns an <img> HTML string, or null to fall through to the placeholder.
 *
 * Not wired to a provider yet — text-only RBCAIEngine cannot produce images,
 * and image generation needs an explicitly configured image model. Kept as a
 * single, clearly-marked hook so it can be enabled without touching callers.
 */
function generateDiagramRasterFallback(array $spec): ?string {
    return null;
}

/** Convert {categories/labels:[...], values:[...]} or an assoc map to label=>value. */
function _examDiagramPairsToMap(array $data, array $labels): ?array {
    $cats = null; $vals = null;
    if (isset($data['categories']) && is_array($data['categories'])) { $cats = array_values($data['categories']); }
    elseif (isset($data['labels']) && is_array($data['labels']))     { $cats = array_values($data['labels']); }
    elseif (!empty($labels))                                          { $cats = $labels; }
    if (isset($data['values']) && is_array($data['values']))          { $vals = array_values($data['values']); }

    if ($cats !== null && $vals !== null) {
        $map = [];
        foreach ($cats as $i => $c) { $map[(string) $c] = (float) ($vals[$i] ?? 0); }
        return $map === [] ? null : $map;
    }

    // Already an associative label => value map (skip the structured keys).
    $assoc = [];
    foreach ($data as $k => $v) {
        if (in_array($k, ['categories', 'labels', 'values', 'x', 'y', 'points'], true)) { continue; }
        if (is_numeric($v)) { $assoc[(string) $k] = (float) $v; }
    }
    return $assoc === [] ? null : $assoc;
}
