#include "sqs_map.h"
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void ok(int32_t code){if(code){fprintf(stderr,"%s\n",sqsm_map_last_error());assert(!code);}}
static char *copy(sqsm_map_bytes_t out){char *s=malloc((size_t)out.len+1);assert(s);memcpy(s,out.data,(size_t)out.len);s[out.len]=0;ok(sqsm_map_bytes_free(out.allocation));return s;}
static int32_t camera(sqsm_map_t map,const char *request,sqsm_map_bytes_t *out){return sqsm_map_camera_json(map,(const uint8_t*)request,strlen(request),out);}
static char *read(sqsm_map_t map,const char *request){sqsm_map_bytes_t out={0};ok(camera(map,request,&out));return copy(out);}
static char *poll(sqsm_map_t map){sqsm_map_bytes_t out={0};ok(sqsm_map_poll_events_json(map,&out));return copy(out);}
static double field(const char *json,const char *name){char key[80];snprintf(key,sizeof key,"\"%s\":",name);const char *p=strstr(json,key);assert(p);return strtod(p+strlen(key),NULL);}
int main(void){
 assert(sqsm_map_runtime_flags()&SQSM_MAP_CAMERA_LIFECYCLE);sqsm_map_t map=0;ok(sqsm_map_create(128,96,&map));sqsm_map_bytes_t out={0};
 char *s=read(map,"{\"method\":\"jumpTo\",\"args\":[{\"center\":[0,20],\"zoom\":3,\"bearing\":0}]}");free(s);free(poll(map));
 s=read(map,"{\"method\":\"jumpTo\",\"args\":[{\"center\":[1,20],\"zoom\":4,\"bearing\":30}]}");free(s);s=poll(map);
 const char *types[]={"sqs.viewchange","movestart","zoomstart","rotatestart","move","zoom","rotate","zoomend","rotateend","moveend"};const char *at=s;
 for(size_t i=0;i<sizeof(types)/sizeof(types[0]);i++){at=strstr(at,"\"type\":\"");assert(at);at+=8;assert(!strncmp(at,types[i],strlen(types[i]))&&at[strlen(types[i])]=='"');if(i){assert(fabs(field(at,"zoom")-(i<4?3:4))<1e-4);assert(strstr(at,"cameraState"));}at++;}
 assert(!strstr(at,"\"type\":\""));free(s);
 s=read(map,"{\"method\":\"getZoom\",\"args\":[]}");free(s);s=poll(map);assert(!strcmp(s,"[]"));free(s);
 if(sqsm_map_runtime_flags()&SQSM_MAP_SCRIPT_SESSIONS){
  const char *script="import {map} from 'sqsmap'; const event=await map.once('moveend');export default {type:event.type,zoom:event.cameraState.camera.zoom};";uint64_t session=0;
  ok(sqsm_map_script_session_start(map,(const uint8_t*)script,strlen(script),&session));ok(sqsm_map_script_session_pump_json(session,32,&out));free(copy(out));
  s=read(map,"{\"method\":\"setZoom\",\"args\":[5]}");free(s);free(poll(map));
  int succeeded=0;for(int i=0;i<16;i++){ok(sqsm_map_script_session_pump_json(session,32,&out));free(copy(out));ok(sqsm_map_script_session_state_json(session,&out));s=copy(out);assert(!strstr(s,"\"state\":\"failed\""));if(strstr(s,"\"state\":\"succeeded\"")){assert(strstr(s,"moveend"));succeeded=1;}free(s);if(succeeded)break;}
  assert(succeeded);ok(sqsm_map_script_session_stop(session));ok(sqsm_map_script_session_release(session));free(poll(map));
 }
 int limited=0;for(int i=0;i<64;i++){
  char request[256];snprintf(request,sizeof request,"{\"method\":\"jumpTo\",\"args\":[{\"center\":[%d,20],\"zoom\":4,\"bearing\":30}]}",i+10);
  char *before=read(map,"{\"method\":\"getCenter\",\"args\":[]}");out=(sqsm_map_bytes_t){0};int32_t code=camera(map,request,&out);
  if(code==SQSM_MAP_RESOURCE_LIMIT){assert(!out.data&&!out.len&&!out.allocation);char *after=read(map,"{\"method\":\"getCenter\",\"args\":[]}");assert(!strcmp(before,after));free(after);limited=1;free(before);break;}
  ok(code);free(copy(out));free(before);
 }
 assert(limited);free(poll(map));s=read(map,"{\"method\":\"setCenter\",\"args\":[[3,20]]}");free(s);free(poll(map));ok(sqsm_map_destroy(map));
 puts("camera lifecycle C host: PASS (order, snapshots, independent subscription, budget before mutation)");return 0;
}
