/* Standalone C11 host; pure and script-enabled builds both implement these ABI symbols. */
#include "sqs_map.h"
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if !defined(_WIN32)
#include <pthread.h>
#endif
static void ok(int32_t status) {
    if(status != SQSM_MAP_OK) { fprintf(stderr,"status=%" PRId32 ": %s\n",status,sqsm_map_last_error()); exit(1); }
}
static int contains(sqsm_map_bytes_t b,const char *needle) {
    char *copy=malloc((size_t)b.len+1);assert(copy);memcpy(copy,b.data,(size_t)b.len);copy[b.len]=0;
    int found=strstr(copy,needle)!=NULL;free(copy);return found;
}
static sqsm_map_bytes_t run(sqsm_map_t map,const char *source) {
    sqsm_map_bytes_t out={0};ok(sqsm_map_script_run_json(map,(const uint8_t*)source,strlen(source),&out));return out;
}
static void rejected(sqsm_map_t map,const char *source,int32_t expected) {
    sqsm_map_bytes_t out={0};int32_t actual=sqsm_map_script_run_json(map,(const uint8_t*)source,strlen(source),&out);
    if(actual!=expected)fprintf(stderr,"expected=%" PRId32 " actual=%" PRId32 ": %s\n",expected,actual,sqsm_map_last_error());
    if(actual!=expected)fprintf(stderr,"source: %s\n",source);
    assert(actual==expected);
    assert(out.data==NULL && out.len==0 && out.allocation==0);assert(strlen(sqsm_map_last_error())>0);
}
#if !defined(_WIN32)
static void *wrong_thread(void *raw) {
    rejected(*(sqsm_map_t*)raw,"export default 1;",SQSM_MAP_CONFLICT);return NULL;
}
#endif
int main(void) {
    sqsm_map_t map=0;ok(sqsm_map_create(64,64,&map));
    if(!(sqsm_map_runtime_flags() & SQSM_MAP_SCRIPT)) {
        assert(sqsm_map_script_api_version()==0);rejected(map,"export default 1;",SQSM_MAP_CAPABILITY_UNAVAILABLE);
        ok(sqsm_map_destroy(map));puts("pure map build: optional interpreter unavailable, old map ABI usable: PASS");return 0;
    }
    assert(sqsm_map_script_api_version()==1);
    sqsm_map_bytes_t result=run(map,"import {map} from 'sqsmap';await map.sqs.ready();export default {connected:true};");
    assert(contains(result,"\"connected\":true"));ok(sqsm_map_bytes_free(result.allocation));
    result=run(map,"import {map} from 'sqsmap';map.setBearing(30).setBearing(42);await map.sqs.flush();export default {bearing:await map.getBearing(),state:await map.sqs.state()};");
    assert(contains(result,"\"bearing\":42") && contains(result,"\"layerCount\":0") && contains(result,"\"mapInstanceId\":\"") && contains(result,"\"sequence\":\""));ok(sqsm_map_bytes_free(result.allocation));
    sqsm_map_bytes_t blank={0},frame={0};ok(sqsm_map_render_rgba(map,&blank));
    result=run(map,"import {map} from 'sqsmap';const id=await map.sqs.addGeoJSON({type:'Feature',properties:{},geometry:{type:'Polygon',coordinates:[[[0,0],[1,0],[1,1],[0,1],[0,0]]]}});export default {id,state:await map.sqs.state()};");
    assert(contains(result,"\"layerCount\":1"));ok(sqsm_map_bytes_free(result.allocation));
    ok(sqsm_map_render_rgba(map,&frame));assert(frame.len==64*64*4 && blank.len==frame.len && memcmp(blank.data,frame.data,(size_t)frame.len)!=0);
    ok(sqsm_map_bytes_free(blank.allocation));ok(sqsm_map_bytes_free(frame.allocation));
    result=run(map,"import {map} from 'sqsmap';const id=await map.sqs.addGeoJSON({type:'Point',coordinates:[0,0]});await map.sqs.removeNativeLayer(id);export default await map.sqs.state();");
    assert(contains(result,"\"layerCount\":1") && contains(result,"\"id\":\""));ok(sqsm_map_bytes_free(result.allocation));
    rejected(map,"import {map} from 'sqsmap';await Promise.all(Array.from({length:33},()=>map.getBearing()));",SQSM_MAP_RESOURCE_LIMIT);
    rejected(map,"import {map} from 'sqsmap';for(let i=0;i<257;i++)await map.getBearing();",SQSM_MAP_RESOURCE_LIMIT);
    rejected(map,"import {map} from 'sqsmap';map.on('load',()=>{});",SQSM_MAP_CAPABILITY_UNAVAILABLE);
    rejected(map,"export const sessionMode='persistent';",SQSM_MAP_CAPABILITY_UNAVAILABLE);
    rejected(map,"import {map} from 'sqsmap';map.setBearing('bad');",SQSM_MAP_INVALID_ARGUMENT);
    rejected(map,"import {map} from 'sqsmap';map.setBearing(NaN);",SQSM_MAP_INVALID_ARGUMENT);
    rejected(map,"import anything from 'filesystem';",SQSM_MAP_INVALID_ARGUMENT);
    rejected(map,"Promise.reject(new Error('unhandled'));export default 1;",SQSM_MAP_INVALID_ARGUMENT);
    rejected(map,"const a=new Array(10000000).fill(1);export default a.length;",SQSM_MAP_RESOURCE_LIMIT);
    rejected(map,"function f(){return f()}f();",SQSM_MAP_RESOURCE_LIMIT);
    rejected(map,"while(true){}",SQSM_MAP_RESOURCE_LIMIT);
    rejected(map,"const spin=()=>Promise.resolve().then(spin);spin();export default 1;",SQSM_MAP_RESOURCE_LIMIT);
    rejected(map,"export default 'x'.repeat(65537);",SQSM_MAP_RESOURCE_LIMIT);
    for(unsigned i=0;i<16;i++){result=run(map,"export default 7;");assert(contains(result,"\"value\":7"));ok(sqsm_map_bytes_free(result.allocation));}
#if !defined(_WIN32)
    pthread_t thread;assert(pthread_create(&thread,NULL,wrong_thread,&map)==0);assert(pthread_join(thread,NULL)==0);
#endif
    result=run(map,"import {map} from 'sqsmap';export default await map.sqs.state();");
    ok(sqsm_map_destroy(map));assert(contains(result,"\"layerCount\":1"));ok(sqsm_map_bytes_free(result.allocation));
    rejected(map,"export default 1;",SQSM_MAP_STALE_HANDLE);
    puts("embedded QuickJS C ABI: real GPU map, JSON calls, chaining, errors/budgets, rerun, thread/output ownership: PASS");return 0;
}
