import http from 'node:http';
import {randomBytes} from 'node:crypto';
import {PGlite} from '@electric-sql/pglite';
import {authorize,policy} from './engine.mjs';
const db=new PGlite();await db.exec("CREATE TABLE documents(id int PRIMARY KEY,tenant_id text NOT NULL,title text NOT NULL);INSERT INTO documents VALUES(11,'orchard','Orchard budget'),(22,'harbor','Harbor payroll');");await db.exec(policy);
const sessions=new Map();let busy=false;
const json=(res,status,data,headers={})=>res.writeHead(status,{'Content-Type':'application/json','Cache-Control':'no-store',...headers}).end(JSON.stringify(data));
async function body(req){let text='';for await(const chunk of req){text+=chunk;if(text.length>1024)throw new Error('Body limit exceeded');}return JSON.parse(text||'{}');}
const server=http.createServer(async(req,res)=>{try{
 const url=new URL(req.url,'http://127.0.0.1');
 if(req.headers.origin&&req.headers.origin!=='http://127.0.0.1:4200')return json(res,403,{error:'Unexpected origin'});
 if(req.method==='POST'&&url.pathname==='/fixture-session'){
  const input=await body(req);if(!['editor','viewer','expired'].includes(input.account)||Object.keys(input).some(k=>k!=='account'))return json(res,400,{error:'Select a listed fictional account'});
  const id=randomBytes(24).toString('hex');sessions.set(id,{tenant:'orchard',memberships:['orchard'],role:input.account==='viewer'?'viewer':'editor',expires:Date.now()+(input.account==='expired'?-1:15*60*1000)});
  return json(res,200,{fixture:true,account:input.account},{'Set-Cookie':`lab_session=${id}; HttpOnly; SameSite=Strict; Path=/; Max-Age=900`});
 }
 if(req.method!=='GET'&&req.method!=='PATCH')return json(res,405,{error:'Use GET or PATCH'});
 const id=/^\/documents\/(\d+)$/.exec(url.pathname);if(!id&&url.pathname!=='/documents')return json(res,404,{error:'Unknown route'});
 const token=/(?:^|;\s*)lab_session=([a-f0-9]{48})(?:;|$)/.exec(req.headers.cookie||'')?.[1],session=sessions.get(token);
 const verdict=authorize(session,req.method==='PATCH'?'update':'read',Date.now());if(!verdict.allowed)return json(res,verdict.status,{error:verdict.reason});
 if(req.method==='PATCH'&&!id)return json(res,400,{error:'Choose one document'});
 let title;if(req.method==='PATCH'){const input=await body(req);if(typeof input.title!=='string'||input.title.length<1||input.title.length>80||Object.keys(input).some(k=>k!=='title'))return json(res,400,{error:'Only a title of 1–80 characters is accepted'});title=input.title;}
 if(busy)return json(res,429,{error:'One database transaction at a time'});busy=true;
 try{
  const rows=await db.transaction(async tx=>{await tx.exec('SET LOCAL ROLE lab_reader');await tx.query("SELECT set_config('app.tenant',$1,true)",[session.tenant]);
   if(req.method==='PATCH')return(await tx.query('UPDATE documents SET title=$1 WHERE id=$2 RETURNING id,tenant_id,title',[title,Number(id[1])])).rows;
   return id?(await tx.query('SELECT id,tenant_id,title FROM documents WHERE id=$1',[Number(id[1])])).rows:(await tx.query('SELECT id,tenant_id,title FROM documents ORDER BY id')).rows;
  });return json(res,id&&!rows.length?404:200,{rows});
 }finally{busy=false;}
 }catch(e){json(res,400,{error:'Invalid fixture request'});}
});
server.listen(4200,'127.0.0.1',()=>console.log('Fictional-account API: http://127.0.0.1:4200 (not a production identity provider)'));
