#include "lamp_guard.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
static lg_config config(void) {
 lg_config c={0};c.channels=61;c.max_run_ms=120000;c.ticket_lifetime_ms=200;
 c.max_poll_gap_ms=20;c.calibration_id=17;c.fixed_input_mw=100;c.input_budget_mw=10000;
 for(unsigned i=0;i<LG_MAX_CHANNELS;i++) c.channel_input_mw[i]=100;
 return c;
}
static void dark(lg_guard *g) {
 assert(!g->output_enable);for(unsigned i=0;i<LG_MAX_CHANNELS;i++)assert(!g->pwm[i]);
}
static lg_frame frame(lg_guard *g,uint32_t now,lg_mode mode) {
 lg_frame f;assert(lg_issue_ticket(g,now,&f));f.source_frame_seq=g->ticket_counter;
 f.capture_baseline_seq=f.source_frame_seq-1;f.hand_present=true;
 if(mode==LG_AI)f.pwm[0]=255;
 return f;
}
static void light(lg_guard *g,lg_frame *f,uint32_t now,lg_mode mode) {
 assert(lg_accept_ai(g,f,now));
 if(mode==LG_MANUAL) {uint8_t p[61]={255};assert(lg_set_manual(g,p,61,now));}
 assert(g->output_enable);
}
static void scenario(lg_mode mode,uint32_t start) {
 lg_guard g;lg_config c=config();lg_frame f;
 lg_inputs absent={true,true,true,true,false},present=absent;present.infrared_hand_present=true;
 assert(lg_init(&g,&c,123,start));
 lg_poll(&g,start,absent);assert(lg_arm(&g,mode,100,start));
 assert(!lg_issue_ticket(&g,start,&f));dark(&g);
 lg_poll(&g,start+1,present);f=frame(&g,start+1,mode);light(&g,&f,start+1,mode);
 lg_frame pending=frame(&g,start+2,mode);
 lg_poll(&g,start+3,absent);dark(&g);
 assert(g.armed && g.mode==mode);assert(!g.pending_ticket && !g.vision_status_seen);
 assert(lg_remaining_ms(&g,start+3)==97); /* IR removal does not extend exposure session. */
 assert(!lg_accept_ai(&g,&pending,start+3));dark(&g);
 lg_poll(&g,start+4,present);dark(&g);
 assert(!lg_accept_ai(&g,&pending,start+4));dark(&g); /* Cancelled ticket stays invalid. */
 uint8_t p[61]={255};if(mode==LG_MANUAL)assert(!lg_set_manual(&g,p,61,start+4));
 f=frame(&g,start+5,mode);light(&g,&f,start+5,mode);
 for(uint32_t dt=6;dt<=100;dt++)lg_poll(&g,start+dt,present);
 assert(!g.armed && g.mode==LG_OFF);dark(&g);
 assert(!lg_accept_ai(&g,&f,start+101));dark(&g); /* Expiry never restarts automatically. */
 /* An omitted IR input remains false even when all former interlocks are true. */
 assert(lg_init(&g,&c,124,start));
 lg_inputs legacy={.temperature_ok=true,.supply_ok=true,.led_driver_ok=true,.hardware_interlock_ok=true};
 lg_poll(&g,start,legacy);assert(lg_arm(&g,mode,100,start));
 assert(!lg_issue_ticket(&g,start,&f));dark(&g);
}
int main(void) {
 scenario(LG_AI,0);scenario(LG_MANUAL,0);
 scenario(LG_AI,UINT32_MAX-10u);scenario(LG_MANUAL,UINT32_MAX-10u);
 puts("PASS: independent IR absence/removal blocks AI and manual light; late ticket rejected; reentry needs fresh vision; timers preserved incl rollover; omitted input dark.");
}
