<?php
// generate_report.php (same-folder version)
// Place alongside tas_spp_index.json
ini_set('display_errors','0'); error_reporting(E_ALL);
header('Content-Type: application/json; charset=utf-8');

// ---------- Helpers ----------
// Build a quick lookup map from an array of zones
function build_zone_index(array $zones): array {
    $idx = [];
    foreach ($zones as $z) {
        $name = trim((string)($z['name'] ?? ''));
        if ($name === '') continue;
        $canon = $z; $canon['name'] = $name; // ensure name present

        $labels = [$name];
        if (!empty($z['short_name'])) $labels[] = $z['short_name'];
        if (!empty($z['aliases']) && is_array($z['aliases']))
            $labels = array_merge($labels, $z['aliases']);

        foreach ($labels as $lab) {
            $k = mb_strtolower(trim((string)$lab));
            if ($k !== '') $idx[$k] = $canon;
        }
    }
    return $idx;
}

// Build a quick lookup map from codes (object or array) + optional crosswalk
function build_code_index(array $codes, array $crosswalkAliases = []): array {
    $idx = [];
    $add = function($label, $entry) use (&$idx) {
        $k = mb_strtolower(trim((string)$label));
        if ($k !== '') $idx[$k] = $entry;
    };

    // Dict-style (e.g., "C13": {...}) or array of objects
    $isAssoc = !empty($codes) && array_keys($codes) !== range(0, count($codes)-1);
    if ($isAssoc) {
        foreach ($codes as $key => $c) {
            $name  = $c['name']  ?? $c['title'] ?? $key;
            $entry = $c; $entry['name'] = $name; $entry['id'] = $key;
            $add($key,  $entry);
            $add($name, $entry);
            if (!empty($c['aliases']) && is_array($c['aliases']))
                foreach ($c['aliases'] as $al) $add($al, $entry);
        }
    } else {
        foreach ($codes as $c) {
            $name  = $c['name'] ?? $c['title'] ?? '';
            if ($name === '') continue;
            $entry = $c; $entry['name'] = $name;
            $add($name, $entry);
            if (!empty($c['aliases']) && is_array($c['aliases']))
                foreach ($c['aliases'] as $al) $add($al, $entry);
        }
    }

    // Crosswalk (e.g., "bushfire" -> "C13")
    foreach ($crosswalkAliases as $alias => $target) {
        $targetKey = mb_strtolower(trim((string)$target));
        if (isset($idx[$targetKey])) $add($alias, $idx[$targetKey]);
    }

    return $idx;
}
function load_index(string $path) {
    if (!file_exists($path)) throw new RuntimeException("tas_spp_index.json not found");
    $raw = file_get_contents($path);
    // Strip BOM
    if (substr($raw,0,3) === "\xEF\xBB\xBF") $raw = substr($raw,3); // strip UTF-8 BOM
    $json = json_decode($raw, true);
    if (!is_array($json)) throw new RuntimeException("tas_spp_index.json is not valid JSON");
    return $json;
}

function spp_url(string $section) {
    static $tpso;
    if ($tpso === null) {
        $idx  = json_decode(file_get_contents(__DIR__.'/tas_spp_index.json'), true);
        $tpso = $idx['tpso'] ?? [];
    }
    $id  = $tpso['map'][$section] ?? null;
    $base = $tpso['base'] ?? 'https://tpso.planning.tas.gov.au/tpso/external/planning-scheme-viewer/30/section/';
    $eff  = $tpso['effectiveForDate'] ?? '2025-08-27';
    return $id ? ($base . $id . '?effectiveForDate=' . rawurlencode($eff))
        : ($base . rawurlencode($section) . '?effectiveForDate=' . rawurlencode($eff));
}


function tpso_url_for(string $code, array $idx, ?string $fallbackType = null): ?string {
    $base = $idx['tpso']['base'] ?? 'https://tpso.planning.tas.gov.au/tpso/external/planning-scheme-viewer/30/section/';
    $eff  = $idx['tpso']['effectiveForDate'] ?? '2025-08-27';
    $map  = (array)($idx['tpso']['map'] ?? []);

    // Normalised key lookup (e.g. "8.1", "C13.6.2")
    $key = strtoupper(trim($code));
    if (isset($map[$key])) {
        return $base . $map[$key] . '?effectiveForDate=' . rawurlencode($eff);
    }

    // Fallback to an index page if provided
    $fbKey = $fallbackType === 'zone' ? 'ZONES_INDEX' :
    ($fallbackType === 'code' ? 'CODES_INDEX' : null);
    if ($fbKey && isset($map[$fbKey])) {
        return $base . $map[$fbKey] . '?effectiveForDate=' . rawurlencode($eff);
    }

    return null; // render plain text if we don't know the id yet
}

function render_clause_link(string $label, ?string $clauseCode, array $idx, ?string $fallbackType = null): string {
    if (!$clauseCode) return '';
    $url = tpso_url_for($clauseCode, $idx, $fallbackType);
    $labelEsc  = htmlspecialchars($label);
    $codeEsc   = htmlspecialchars($clauseCode);
    if ($url) {
        $urlEsc = htmlspecialchars($url);
        return "<li><strong>{$labelEsc}:</strong> <a href=\"{$urlEsc}\" target=\"_blank\" rel=\"noopener\">{$codeEsc}</a></li>";
    }
    // No mapping yet → show plain code (no broken link)
    return "<li><strong>{$labelEsc}:</strong> {$codeEsc}</li>";
}


// Normalise zone names so "General Residential" == "General Residential Zone"
function norm_zone($s) {
    $s = trim(strtolower($s));
    $s = preg_replace('/\s+zone$/i','', $s);
    return $s;
}

// Fuzzy zone lookup against index (uses suffix stripping + contains matches)
function find_zone($indexZones, $inputName) {
    $needle = norm_zone($inputName);
    foreach ($indexZones as $z) {
        if (empty($z['name'])) continue;
        if (norm_zone($z['name']) === $needle) return $z;
    }
    foreach ($indexZones as $z) {
        $n = norm_zone($z['name'] ?? '');
        if ($n !== '' && ($n === $needle || str_contains($n, $needle) || str_contains($needle, $n))) return $z;
    }
    return null;
}

// Map LIST overlay names to C-code ids (heuristics)
function code_id_from_name($name) {
    $n = strtolower($name);
    if (str_contains($n,'bushfire')) return 'C13';
    if (str_contains($n,'heritage')) return 'C6';
    if (str_contains($n,'parking') || str_contains($n,'transport')) return 'C2';
    if (str_contains($n,'road') || str_contains($n,'rail')) return 'C3';
    if (str_contains($n,'telecom')) return 'C5';
    if (str_contains($n,'flood')) return 'C12';
    if (str_contains($n,'coastal inund')) return 'C11';
    if (str_contains($n,'coastal ero')) return 'C10';
    if (str_contains($n,'landslide') || str_contains($n,'landslip')) return 'C15';
    if (str_contains($n,'contamin')) return 'C14';
    if (str_contains($n,'electric') && str_contains($n,'trans')) return 'C4';
    if (str_contains($n,'scenic')) return 'C8';
    if (str_contains($n,'natural') || str_contains($n,'biodiv') || str_contains($n,'waterway') || str_contains($n,'coastal protection')) return 'C7';
    if (str_contains($n,'attenuation')) return 'C9';
    if (str_contains($n,'sign')) return 'C1';
    if (str_contains($n,'airport') || str_contains($n,'airfield')) return 'C16';
    return null; // unknown -> leave as plain text
}

try {
    // ---------- Input ----------
    $in = json_decode(file_get_contents('php://input'), true);
    if (!$in || empty($in['address']) || empty($in['planning_zones'])) {
        http_response_code(400);
        echo json_encode(['ok'=>false,'error'=>'Missing property payload']); exit;
    }

    // ---------- DEBUG ----------
    $debug = !empty($in['debug']);
    $log = [];
    $DLOG = function($m) use (&$log) {
        $s = is_string($m) ? $m : json_encode($m, JSON_UNESCAPED_SLASHES);
        $log[] = $s;
        error_log('[AIReport] '.$s);
    };
    $DLOG(['received_zones'=>$in['planning_zones'] ?? [], 'received_codes'=>$in['planning_codes'] ?? []]);

    // ---------- Load index ----------
    $indexPath = __DIR__ . '/tas_spp_index.json';
    if (!is_file($indexPath)) throw new RuntimeException('tas_spp_index.json not found');
    $idx = json_decode(file_get_contents($indexPath), true);
    if (!$idx) throw new RuntimeException('tas_spp_index.json is not valid JSON');
    $sppVersion = $idx['meta']['version'] ?? 'unknown';

    $zonesIndexArr = (array)($idx['zones'] ?? []);
    $codesIndex    = (array)($idx['codes'] ?? []);
    $aliasMap      = (array)($idx['crosswalk']['aliases'] ?? []);

    /* ---------- Names (as shown at top of report) ---------- */
    $zoneNames = array_values((array)$in['planning_zones']);
    $codeNames = array_values((array)$in['planning_codes']);

    /* ---------- Zone matching (fuzzy) ---------- */
    // map zones by case-insensitive name match against idx["zones"] (array of objects)
    $zoneBlobs = [];
    foreach ($zoneNames as $zn) {
        $match = find_zone($zonesIndexArr, $zn);
        if ($match) {
            $zoneBlobs[] = $match;
            $DLOG(['zone_match'=>$zn, 'matched_to'=>$match['name'] ?? null, 'doc_section'=>$match['doc_section'] ?? null]);
        } else {
            $zoneBlobs[] = ['name'=>$zn]; // fall back to plain name
            $DLOG(['zone_match'=>$zn, 'matched_to'=>null]);
        }
    }


    $codeBlobs = [];
    $alias = (array)($idx['crosswalk']['aliases'] ?? []);
    $codesIndex = (array)($idx['codes'] ?? []);
    foreach ($codeNames as $cn) {
        $matchKey = null;
        // try direct key like "C13"
        if (isset($codesIndex[$cn])) { $matchKey = $cn; }
        // try alias like "bushfire" -> "C13"
        else {
            foreach ($alias as $k=>$v) {
                if (strcasecmp($k, $cn)===0 || strcasecmp($v, $cn)===0) { $matchKey = $v; break; }
            }
        }
        $codeBlobs[] = $matchKey && isset($codesIndex[$matchKey])
            ? ['name'=>$matchKey] + $codesIndex[$matchKey]
            : ['name'=>$cn];
    }

    /* ---------- Code matching (heuristic) ---------- */
    // Build friendly code objects preserving the original label + inferred id (if any)
    $codeMatches = [];
    foreach ($codeNames as $label) {
        $id = null;
        // direct dictionary hit (e.g. "C13")
        if (isset($codesIndex[$label])) $id = $label;
        // alias map hit (e.g. "bushfire" -> "C13")
        if (!$id) {
            foreach ($aliasMap as $k=>$v) {
                if (strcasecmp($k, $label)===0 || strcasecmp($v, $label)===0) { $id = $v; break; }
            }
        }
        // heuristic from LIST label (e.g. "Safeguarding of Airports Code" -> "C16")
        if (!$id) {
            $heur = code_id_from_name($label);
            if ($heur && isset($codesIndex[$heur])) $id = $heur;
        }
        $def = $id && isset($codesIndex[$id]) ? $codesIndex[$id] : null;
        $codeMatches[] = ['id'=>$id, 'label'=>$label, 'def'=>$def];
        $DLOG(['code_match'=>$label, 'id'=>$id, 'has_key_clauses'=> $def && !empty($def['key_clauses'])]);
    }

    // ----- Intended-use rules (optional section) -----
    $intended = trim((string)($in['intended_use'] ?? ''));
    $intentRows = [];

    if ($intended !== '') {
        // Load rules
        $rulesPath = __DIR__ . '/tas_use_rules.json';
        $useRules = is_file($rulesPath) ? json_decode(file_get_contents($rulesPath), true) : null;

        if (is_array($useRules)) {
            // Normalise the user's intended use via aliases
            $canonUse = strtolower($intended);
            foreach (($useRules['aliases'] ?? []) as $k => $arr) {
                if (strtolower($k) === $canonUse) { $canonUse = $k; break; }
                foreach ((array)$arr as $alt) {
                    if (strtolower($alt) === strtolower($intended)) { $canonUse = $k; break 2; }
                }
            }

            foreach ($zoneBlobs as $z) {
                $zname = $z['name'] ?? '';
                $zrules = $useRules['rules'][$zname] ?? [];
                $match = null;
                foreach ($zrules as $r) {
                    if (strtolower($r['use'] ?? '') === $canonUse) { $match = $r; break; }
                }
                // Build a row per zone
                $useTable = $z['key_clauses']['use_table'] ?? ($match['refs']['use_table'] ?? null);
                $standards = $match['refs']['standards'] ?? [];

                $statusCode = $match['status'] ?? 'CHECK';
                $statusText = $useRules['enums']['status'][$statusCode] ?? 'Check Use Table';

                $intentRows[] = [
                    'zone'      => $zname ?: 'Zone',
                    'status'    => $statusText,
                    'use_table' => $useTable,
                    'standards' => $standards,
                    'notes'     => $match['notes'] ?? null
                ];
            }
        }
    }


    /* ---------- Small trimmed RAW sample for future LLM ---------- */
    // ---- Prepare RAW (optional) for LLM context; trim to keep prompts lean ----
    $raw = $in['raw'] ?? null;
    // pick first feature’s attrs for each layer, keep up to ~40 keys to avoid giant prompts
    $pickFirst = function($arr){
        $a = (array)$arr;
        if (isset($a[0]) && is_array($a[0])) {
            $attrs = $a[0];
            return array_slice($attrs, 0, 40, true);
        }
        return null;
    };

    $raw_for_prompt = [
        'parcel_attrs' => $pickFirst($raw['parcels'] ?? []),
        'lga_attrs'    => $pickFirst($raw['lga'] ?? []),
        'zone_attrs'   => $pickFirst($raw['zones'] ?? []),
        'code_attrs'   => $pickFirst($raw['codes'] ?? []),
    ];

    // ---- Build a concise, LLM-friendly context blob (you can expand later) ----
    $context = [
        'address'         => (string)$in['address'],
        'pid'             => $in['pid'] ?? null,
        'title_id'        => $in['title_id'] ?? null,
        'council'         => $in['council'] ?? null,
        'planning_scheme' => $in['planning_scheme'] ?? null,
        'zones'           => array_map(fn($z)=>[
            'name'		  => $z['name'] ?? null,
            'doc_section' => $z['doc_section'] ?? null,
            'key_clauses' => $z['key_clauses'] ?? null], $zoneBlobs),
        'codes'           => array_map(fn($c)=>[
            'id'    	  => $c['id'] ?? null,
            'name'	 	  => $c['name'] ?? null,
            'key_clauses' => $c['key_clauses'] ?? null], $codeMatches),
        'area'            => [
            'sqm'         => $in['total_area']['sqm'] ?? null,
            'sqm_label'   => $in['total_area']['sqm_label'] ?? null,
            'ha'          => $in['total_area']['ha'] ?? null,
            'ha_label'    => $in['total_area']['ha_label'] ?? null,
        ],
        'spp_version'     => $sppVersion,
        'raw_sample'      => $raw_for_prompt, // safe, trimmed
    ];

    // ====== (optional) Hook an LLM here ======
    // Leave disabled by default. When ready, implement call_llm() to your provider.
    $USE_LLM = false;
    $llm_html = null;
    if ($USE_LLM) {
        $prompt = [
            "role" => "user",
            "content" =>
            "You are an expert planning assistant for the Tasmanian Planning Scheme (SPPs). Please don't make anything up, strictly stick to the wording and context of the State Planning Provisions.".
            "Using the JSON context below, write a concise property planning summary: ".
            "1) Identify applicable zones and codes (with clause references from key_clauses). ".
            "2) For an *intended use* placeholder (like dwelling, shed, café), outline likely status (Permitted/Discretionary/Prohibited) and key standards to check. ".
            "3) Include hyperlinks to the relevant SPP sections when doc_section is available (base URL: https://tpso.planning.tas.gov.au/tpso/external/planning-scheme-viewer/30/section/<DOC>?effectiveForDate={$sppVersion}). ".
            "4) Keep it practical, note uncertainty and that LPS may vary locally.\n\n".
            "Context JSON:\n".
            json_encode($context, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)
        ];

        // $llm_html = call_llm([$prompt]); // TODO: implement your provider call
    }

    /* ---------- Map image (accept data URL or raw base64) ---------- */
    $mapSrc = null;
    if (!empty($in['map_png'])) {
        $mapSrc = (stripos($in['map_png'], 'data:image') === 0)
            ? $in['map_png']
            : ('data:image/png;base64,' . $in['map_png']);
    }

    // ---------- HTML (template) ----------
    ob_start(); ?>
<div>
    <h3 class="mb-2">AI-Assisted Property Report (Tasmania)</h3>
    <div class="text-muted mb-3">
        Generated <?= htmlspecialchars(gmdate('Y-m-d H:i')) ?> · SPP version: <?= htmlspecialchars($sppVersion) ?>
    </div>

    <?php if ($mapSrc): ?>
    <div class="mb-3">
        <img src="<?= htmlspecialchars($mapSrc) ?>" alt="Property map" style="max-width:100%;border-radius:.5rem;border:1px solid rgba(0,0,0,.1)" />
    </div>
    <?php endif; ?>

    <h5 class="mb-1"><?= htmlspecialchars($in['address']) ?></h5>
    <ul class="list-unstyled mb-3">
        <?php if (!empty($in['pid'])): ?><li><strong>PID:</strong> <?= htmlspecialchars($in['pid']) ?></li><?php endif; ?>
        <?php if (!empty($in['title_id'])): ?><li><strong>Title:</strong> <?= htmlspecialchars($in['title_id']) ?></li><?php endif; ?>
        <?php if (!empty($in['council'])): ?><li><strong>Council (LGA):</strong> <?= htmlspecialchars($in['council']) ?></li><?php endif; ?>
        <?php if (!empty($in['total_area']['sqm_label'])): ?>
        <li><strong>Area:</strong> <?= htmlspecialchars($in['total_area']['sqm_label']) ?>
            <?php if (!empty($in['total_area']['ha_label'])): ?> (<?= htmlspecialchars($in['total_area']['ha_label']) ?>)<?php endif; ?>
        </li>
        <?php endif; ?>
    </ul>

    <h6>Primary zone & overlays</h6>
    <p>
        <strong>Zone(s):</strong>
        <?= htmlspecialchars(implode(', ', $zoneNames)) ?: '—' ?><br/>
        <strong>Overlays/Codes:</strong>
        <?= htmlspecialchars(implode(', ', $codeNames)) ?: '—' ?>
    </p>

    <h6>Key scheme parts to review</h6>
    <ul>
        <?php foreach ($zoneBlobs as $z): ?>
        <li>
            <strong><?= htmlspecialchars($z['name'] ?? 'Zone') ?>:</strong>
            <?php if (!empty($z['key_clauses'])): ?>
            <ul class="mb-1">
                <?= render_clause_link('Purpose',       $z['key_clauses']['purpose'] ?? null, $idx, 'zone') ?>
                <?= render_clause_link('Use table',     $z['key_clauses']['use_table'] ?? null, $idx, 'zone') ?>
                <?= render_clause_link('Use standards', $z['key_clauses']['use_standards'] ?? null, $idx, 'zone') ?>
                <?= render_clause_link('Development standards — dwellings',        $z['key_clauses']['dev_standards_dwellings'] ?? null, $idx, 'zone') ?>
                <?= render_clause_link('Development standards — buildings & works', $z['key_clauses']['dev_standards_buildings_works'] ?? null, $idx, 'zone') ?>
                <?= render_clause_link('Subdivision',    $z['key_clauses']['subdivision'] ?? null, $idx, 'zone') ?>
            </ul>
            <?php if (!empty($z['doc_section'])): $zUrl = tpso_url_for($z['doc_section'], $idx, 'zone'); if ($zUrl): ?>
            <div class="small">Full zone chapter: <a href="<?= htmlspecialchars($zUrl) ?>" target="_blank" rel="noopener"><?= htmlspecialchars($z['doc_section']) ?></a></div>
            <?php else: ?>
            <div class="small">Full zone chapter: <?= htmlspecialchars($z['doc_section']) ?></div>
            <?php endif; endif; ?>
            <?php else: ?>
            Refer to the State Planning Provisions zone chapter.
            <?php endif; ?>
        </li>
        <?php endforeach; ?>

        <?php foreach ($codeMatches as $cm): ?>
        <li>
            <strong><?= htmlspecialchars($cm['label']) ?><?= $cm['id'] ? ' ('.$cm['id'].')' : '' ?>:</strong>
            <?php if ($cm['def'] && !empty($cm['def']['key_clauses'])): $kc = $cm['def']['key_clauses']; ?>
            <ul class="mb-1">
                <?= render_clause_link('Purpose',            $kc['purpose'] ?? null, $idx, 'code') ?>
                <?= render_clause_link('Application',        $kc['application'] ?? null, $idx, 'code') ?>
                <?= render_clause_link('Definitions',        $kc['definitions'] ?? null, $idx, 'code') ?>
                <?= render_clause_link('Exempt development / use', ($kc['exempt_development'] ?? $kc['exempt_use'] ?? null), $idx, 'code') ?>
                <?= render_clause_link('Use standards',      $kc['use_standards'] ?? null, $idx, 'code') ?>
                <?php if (!empty($kc['use_standards_detail']) && is_array($kc['use_standards_detail'])): ?>
                <?php foreach ($kc['use_standards_detail'] as $it): 
    $id = $it['id'] ?? null;
    if ($id) {
        $u = tpso_url_for($id, $idx, 'code');
        if ($u) {
            echo '<li><a href="'.htmlspecialchars($u).'" target="_blank" rel="noopener">'.htmlspecialchars($id).'</a>'
                .($title ? ' – '.htmlspecialchars($title) : '')
                .'</li>';
        } else {
            echo '<li>'.htmlspecialchars($id).($title ? ' – '.htmlspecialchars($title) : '').'</li>';
        }
    }
    continue; ?>
                <li>
                    <a href="<?= htmlspecialchars(spp_url($id)) ?>" target="_blank" rel="noopener"><?= htmlspecialchars($id) ?></a>
                    <?= $title ? ' – '.htmlspecialchars($title) : '' ?>
                </li>
                <?php endforeach; ?>
                <?php endif; ?>
                <?= render_clause_link('Development standards', $kc['development_standards'] ?? null, $idx, 'code') ?>
                <?php if (!empty($kc['development_standards_detail']) && is_array($kc['development_standards_detail'])): ?>
                <?php foreach ($kc['development_standards_detail'] as $it): 
    $id = $it['id'] ?? null;
    if ($id) {
        $u = tpso_url_for($id, $idx, 'code');
        if ($u) {
            echo '<li><a href="'.htmlspecialchars($u).'" target="_blank" rel="noopener">'.htmlspecialchars($id).'</a>'
                .($title ? ' – '.htmlspecialchars($title) : '')
                .'</li>';
        } else {
            echo '<li>'.htmlspecialchars($id).($title ? ' – '.htmlspecialchars($title) : '').'</li>';
        }
    }
    continue; ?>
                <li>
                    <a href="<?= htmlspecialchars(spp_url($id)) ?>" target="_blank" rel="noopener"><?= htmlspecialchars($id) ?></a>
                    <?= $title ? ' – '.htmlspecialchars($title) : '' ?>
                </li>
                <?php endforeach; ?>
                <?php endif; ?>
            </ul>
            <?php else: ?>
            Review the relevant SPP code chapter for triggers and standards.
            <?php endif; ?>
        </li>
        <?php endforeach; ?>
    </ul>

    <?php if ($intended !== ''): ?>
    <h6>Intended use (beta): <?= htmlspecialchars($intended) ?></h6>
    <ul class="mb-3">
        <?php foreach ($intentRows as $row): ?>
        <li>
            <strong><?= htmlspecialchars($row['zone']) ?>:</strong>
            <?= htmlspecialchars($row['status']) ?> —
            <?php if (!empty($row['use_table'])): ?>
            <a href="<?= htmlspecialchars(spp_url($row['use_table'])) ?>" target="_blank" rel="noopener">
                Use Table (<?= htmlspecialchars($row['use_table']) ?>)
            </a>
            <?php else: ?>Use Table<?php endif; ?>
            <?php if (!empty($row['standards'])): ?>
            · standards
            <?php foreach ($row['standards'] as $i => $st): ?>
            <a href="<?= htmlspecialchars(spp_url($st)) ?>" target="_blank" rel="noopener"><?= htmlspecialchars($st) ?></a><?= $i < count($row['standards']) - 1 ? ', ' : '' ?>
            <?php endforeach; ?>
            <?php endif; ?>
            <?php if (!empty($row['notes'])): ?> · <?= htmlspecialchars($row['notes']) ?><?php endif; ?>
        </li>
        <?php endforeach; ?>
    </ul>
    <?php endif; ?>


    <h6>Typical constraints to check</h6>
    <ul>
        <li>Use Table: whether your intended use is Permitted, Discretionary, or Prohibited.</li>
        <li>Zone development standards: height, setbacks, site coverage, privacy, access and parking.</li>
        <li>Code triggers (if mapped): bushfire, heritage, inundation, landslide, biodiversity, scenic/attenuation, coastal, roads/rail, parking.</li>
        <li>Local Provisions Schedule (<?= htmlspecialchars($in['council'] ?? 'Council') ?>): any local variations, Specific Area Plans, heritage list entries.</li>
    </ul>

    <h6>Next steps</h6>
    <ol>
        <li>Confirm zone and all overlays using the LIST map and <?= htmlspecialchars($in['council'] ?? 'Council') ?> LPS.</li>
        <li>Check your intended use against the zone Use Table; note any qualifications.</li>
        <li>Review development and code standards; obtain specialist reports where required.</li>
        <li>Seek pre-application advice from <?= htmlspecialchars($in['council'] ?? 'the local Council') ?> planning.</li>
    </ol>

    <div class="text-muted small mt-3">Disclaimer: This report summarises public datasets and references the Tasmanian Planning Scheme (SPPs). It is not legal advice. Verify all controls with the responsible authority.</div>
</div>
<?php if ($llm_html): ?>
<div class="mt-3"><?= $llm_html ?></div>
<?php endif; ?>

<div class="mt-3">
    <?php if (!empty($in['debug'])): ?>
    <details class="mt-3"><summary>Debug context</summary><pre class="small" style="white-space:pre-wrap"><?= htmlspecialchars(json_encode($context, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)) ?></pre></details>
    <?php endif; ?>
</div>
<?php
    $html = ob_get_clean();

    echo json_encode(['ok'=>true,'html'=>$html], JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);

} catch (Throwable $e) {
    http_response_code(500);
    echo json_encode(['ok'=>false,'error'=>$e->getMessage()]);
}
?>