const {useState, useEffect, useRef, useMemo} = React;
const BRAND = "Oakline Repairs";
const CATEGORIES = [
  {id:"plumbing", label:"Plumbing", icon:"droplets"},
  {id:"electrical", label:"Electrical", icon:"zap"},
  {id:"roofing", label:"Roofing", icon:"home"},
  {id:"hvac", label:"Heating & cooling", icon:"fan"},
  {id:"painting", label:"Painting & drywall", icon:"paintbrush"},
  {id:"carpentry", label:"Carpentry", icon:"hammer"},
  {id:"masonry", label:"Masonry & concrete", icon:"brick-wall"},
  {id:"handyman", label:"General repair", icon:"wrench"},
];
const URGENCIES = [
  {id:"emergency", label:"Emergency", sub:"Needs attention today", badge:"badge-emergency"},
  {id:"week", label:"This week", sub:"Urgent but not an emergency", badge:"badge-urgent"},
  {id:"month", label:"This month", sub:"Soon, at your convenience", badge:"badge-neutral"},
  {id:"flexible", label:"Flexible", sub:"Whenever it fits your schedule", badge:"badge-neutral"},
];
function Icon({name, size=18, style}){
  const html = useMemo(()=>{
    try{
      const pascal = name.split("-").map(s=>s.charAt(0).toUpperCase()+s.slice(1)).join("");
      const node = window.lucide && lucide.icons && lucide.icons[pascal];
      if(!node) return "";
      const el = lucide.createElement(node);
      el.setAttribute("width", size); el.setAttribute("height", size);
      return el.outerHTML;
    }catch(e){ return ""; }
  }, [name, size]);
  return <span aria-hidden="true" style={{display:"inline-flex", flex:"none", lineHeight:0, ...style}} dangerouslySetInnerHTML={{__html:html}}></span>;
}
function catOf(id){ return CATEGORIES.find(c=>c.id===id) || CATEGORIES[7]; }
function catsList(x){ return x.categories && x.categories.length ? x.categories : (x.category ? [x.category] : []); }
function catsLabel(x){ const l = catsList(x).map(id=>catOf(id).label); if(x.categoryOther && x.categoryOther.trim()) l.push(x.categoryOther.trim()); return l.join(" + ") || "General repair"; }
function urgOf(id){ return URGENCIES.find(u=>u.id===id) || URGENCIES[3]; }
function uid(){ return Math.random().toString(36).slice(2,9); }
function timeAgo(ts){
  const m = Math.max(1, Math.round((Date.now()-ts)/60000));
  if(m<60) return m+"m ago";
  const h = Math.round(m/60); if(h<24) return h+"h ago";
  return Math.round(h/24)+"d ago";
}
const STORE_KEY = "oakline_requests_v1";
function seedRequests(){
  const now = Date.now();
  return [
    {id:uid(), ref:"QR-2417", createdAt:now-1000*60*50, status:"new", category:"plumbing", urgency:"week",
     customer:{name:"Sarah Whitfield", phone:"(415) 555-0132", email:"sarah.w@example.com", pref:"Text"},
     property:{type:"House", age:"30+ years", access:"Home after 4pm, driveway parking", address:"84 Alder Street, Portsmouth"},
     description:"Water is dripping from the pipe under the kitchen sink. There's a damp patch spreading on the cabinet floor and it smells musty.",
     photos:[], transcript:[
       {role:"ai", text:"Is the drip constant, or only when the tap runs?"},
       {role:"user", text:"Only when the tap runs, but the cabinet floor stays wet."},
       {role:"ai", text:"Do you know if the pipework under the sink is plastic or metal?"},
       {role:"user", text:"Metal, I think copper."},
       {role:"ai", text:"Has the cabinet floor gone soft or dark anywhere?"},
       {role:"user", text:"One corner is darker and a bit soft."}],
     ai:{title:"Kitchen sink leak with cabinet water damage",
       summary:"Leak under the kitchen sink that only appears when the tap runs, pointing to a waste or joint failure rather than supply pipe. Copper pipework, and the cabinet base already shows soft, darkened board in one corner, so allow for a small carpentry repair alongside the plumbing fix.",
       keyFacts:[{label:"Leak trigger", value:"Only when tap runs"},{label:"Pipework", value:"Copper"},{label:"Damage", value:"Cabinet base soft in one corner"},{label:"Access", value:"After 4pm, driveway parking"},{label:"Property", value:"House, 30+ years"}],
       openQuestions:["Whether the leak is from the trap, waste joint, or sink seal", "Cabinet base material, for the repair board match"]},
     quote:null},
    {id:uid(), ref:"QR-2416", createdAt:now-1000*60*60*5, status:"new", category:"electrical", urgency:"emergency",
     customer:{name:"Marcus Bell", phone:"(415) 555-0198", email:"m.bell@example.com", pref:"Phone call"},
     property:{type:"Apartment", age:"10–30 years", access:"Buzzer 3B, home all day", address:"12 Croft Lane, Apt 3B, Portsmouth"},
     description:"All the sockets in both bedrooms stopped working last night. Lights still work. The breaker looks fine but I don't want to poke at it.",
     photos:[], transcript:[
       {role:"ai", text:"Did anything happen just before they cut out — a new appliance, a storm, a tripped switch?"},
       {role:"user", text:"I plugged in a space heater in the small bedroom."},
       {role:"ai", text:"On the breaker panel, are any switches sitting in the middle position rather than fully up or down?"},
       {role:"user", text:"One is in the middle actually."}],
     ai:{title:"Bedroom socket circuit tripped after space heater",
       summary:"Both bedroom socket circuits went dead immediately after a space heater was plugged in, and one breaker is sitting mid-position. Likely a tripped RCBO or breaker with a possible overloaded or faulty circuit behind it. Customer is home all day and has asked not to touch the panel.",
       keyFacts:[{label:"Affected", value:"Sockets in 2 bedrooms"},{label:"Lights", value:"Unaffected"},{label:"Trigger", value:"Space heater plugged in"},{label:"Panel", value:"One breaker mid-position"},{label:"Access", value:"Home all day, buzzer 3B"}],
       openQuestions:["Age and type of the consumer unit", "Whether the heater trips the circuit again on reset"]},
     quote:null},
    {id:uid(), ref:"QR-2414", createdAt:now-1000*60*60*28, status:"quoted", category:"painting", urgency:"month",
     customer:{name:"Elena Rojas", phone:"(415) 555-0114", email:"elena.r@example.com", pref:"Email"},
     property:{type:"House", age:"10–30 years", access:"Weekdays 9–3, street parking", address:"7 Marlow Gardens, Portsmouth"},
     description:"Hallway wall was repaired after a damp problem. The plaster patch is dry now and the whole hallway needs repainting so it matches.",
     photos:[], transcript:[
       {role:"ai", text:"Roughly how long is the hallway, and does it include the stairwell?"},
       {role:"user", text:"About 5 meters, no stairwell."},
       {role:"ai", text:"Is the ceiling being painted too, or walls only?"},
       {role:"user", text:"Walls only, ceiling is fine."}],
     ai:{title:"Hallway repaint over dried plaster patch",
       summary:"Repaint of a 5m hallway, walls only, following a damp repair. The new plaster patch will need a mist coat before the finish coats. No stairwell involved, so access is straightforward.",
       keyFacts:[{label:"Area", value:"5m hallway, walls only"},{label:"Surface", value:"New plaster patch, dry"},{label:"Prep", value:"Mist coat required"},{label:"Access", value:"Weekdays 9–3"}],
       openQuestions:["Colour match or full colour change"]},
     quote:{items:[{desc:"Prep, mist coat over new plaster", qty:1, price:140},{desc:"Two finish coats, hallway walls", qty:1, price:420},{desc:"Materials (paint, sundries)", qty:1, price:95}], note:"Colour match included. One day on site.", validDays:30, sentAt:now-1000*60*60*20}},
  ];
}
function loadRequests(){
  try{
    const raw = localStorage.getItem(STORE_KEY);
    if(raw) return JSON.parse(raw);
  }catch(e){}
  const seeds = seedRequests();
  try{ localStorage.setItem(STORE_KEY, JSON.stringify(seeds)); }catch(e){}
  return seeds;
}
function saveRequests(list){
  try{ localStorage.setItem(STORE_KEY, JSON.stringify(list)); }
  catch(e){
    try{ localStorage.setItem(STORE_KEY, JSON.stringify(list.map(r=>({...r, photos:[]})))); }catch(e2){}
  }
}
async function claudeJSON(system, userContent, maxTokens){
  const text = await window.claude.complete({
    system, max_tokens: maxTokens||600,
    messages:[{role:"user", content:userContent}],
  });
  const a = text.indexOf("{"), b = text.lastIndexOf("}");
  if(a<0 || b<=a) throw new Error("no json");
  return JSON.parse(text.slice(a, b+1));
}
function photoBlocks(d){
  return (d.photos||[]).slice(0,6).map(p=>({type:"image", source:{type:"base64", media_type:"image/jpeg", data:p.split(",")[1]}}));
}
function withPhotos(d, text){
  const blocks = photoBlocks(d);
  return blocks.length ? [...blocks, {type:"text", text}] : text;
}
function jobContext(d){
  return `Job categories: ${catsLabel(d)}
Customer description: "${d.description}"
Property: ${d.property.type}, age ${d.property.age||"not given"}. Access notes: ${d.property.access||"none"}.
Urgency: ${urgOf(d.urgency).label}.
Photos attached: ${d.photos.length}.`;
}
const MAX_QUESTIONS = 4;
async function nextQuestion(draft, qa, max){
  max = max || MAX_QUESTIONS;
  const hasPhotos = draft.photos.length > 0;
  const system = `You are the intake assistant on a small repairs contractor's website (${BRAND}). A customer has described a repair job${hasPhotos?" and attached photos of it":""}. Ask the FEWEST follow-up questions — one at a time — that the contractor genuinely needs to price the job accurately (dimensions, materials, age, extent of damage, what's been tried, access constraints).${hasPhotos?" LOOK at the photos first: never ask anything already visible in them, and ground your questions in what you can see (\"the corroded joint in your photo\"). On your FIRST question only, also include \"note\": one short sentence telling the customer what you spotted in their photos.":""} Plain everyday language, warm and direct, one short sentence per question. Never ask for anything already answered. Never ask for contact details. Hard limit ${max} questions total; stop as soon as you have enough. Respond ONLY with JSON, nothing else: {"done":false,${hasPhotos?'"note":"first question only, what you saw in the photos",':''}"question":"...","quickReplies":["short option", ...]} (quickReplies optional, max 4, only when a short list of likely answers exists) or {"done":true}.`;
  const user = jobContext(draft) + "\n\nFollow-up Q&A so far (" + qa.length + " of " + max + " questions asked):\n" + (qa.map(x=>`Q: ${x.q}\nA: ${x.a}`).join("\n") || "none yet");
  return claudeJSON(system, withPhotos(draft, user), 400);
}
async function batchQuestions(draft, max){
  const hasPhotos = draft.photos.length > 0;
  const system = `You are the intake assistant on a small repairs contractor's website (${BRAND}). A customer described a repair job${hasPhotos?" and attached photos of it":""}. Generate the FEWEST follow-up questions — at most ${max} — that the contractor genuinely needs to price the job accurately (dimensions, materials, age, extent of damage, what's been tried, access constraints).${hasPhotos?' LOOK at the photos: never ask anything already visible in them; ground questions in what you can see; set "source":"photos" for those; and write "observation": one short sentence telling the customer what you spotted in their photos.':''} Plain everyday language, warm and direct, one short sentence per question. Never ask for contact details or anything already given. Respond ONLY with JSON, nothing else: {${hasPhotos?'"observation":"...",':''}"questions":[{"question":"...","quickReplies":["short option", ... max 4, only when a short list of likely answers exists],"source":"photos" or "description"}]}.`;
  return claudeJSON(system, withPhotos(draft, jobContext(draft)), 900);
}
const FALLBACK_QS = [
  {question:"How long has this been an issue?", quickReplies:["Just started","A few days","Weeks or more"]},
  {question:"Roughly how big is the affected area?"},
  {question:"Has anyone attempted a repair on this before?", quickReplies:["No","Yes, recently","Yes, a while ago"]},
];
async function buildSummary(draft, qa, unanswered){
  const system = `You summarize a repair request for the contractor who will quote it. Be concrete and useful — a contractor should understand the job in 15 seconds.${draft.photos.length?" Photos of the job are attached: fold what they show into the summary and key facts (visible damage, materials, scale).":""} Plain language, no jargon, no hype. Respond ONLY with JSON, nothing else: {"title":"max 8 words, specific","summary":"2-3 sentences a contractor would want, including anything that affects price","keyFacts":[{"label":"2-3 words","value":"short"} — 4 to 6 items, most price-relevant first],"openQuestions":["things still unknown that could change the price" — 0 to 3 items]}.`;
  let user = jobContext(draft) + "\n\nFollow-up Q&A:\n" + (qa.map(x=>`Q: ${x.q}\nA: ${x.a}`).join("\n") || "none");
  if(unanswered && unanswered.length) user += "\n\nQuestions the customer couldn't answer (fold into openQuestions):\n" + unanswered.join("\n");
  return claudeJSON(system, withPhotos(draft, user), 700);
}
function fallbackSummary(draft, qa){
  return {
    title: catOf(draft.category).label + " repair request",
    summary: draft.description,
    keyFacts: [
      {label:"Category", value:catOf(draft.category).label},
      {label:"Urgency", value:urgOf(draft.urgency).label},
      {label:"Property", value:draft.property.type},
      draft.property.access ? {label:"Access", value:draft.property.access} : null,
    ].filter(Boolean),
    openQuestions: qa.length ? [] : ["No follow-up answers collected — confirm details by phone"],
  };
}
async function classifyCategory(description){
  try{
    const res = await claudeJSON(`Classify a repair request into 1-2 category ids from: ${CATEGORIES.map(c=>c.id+" ("+c.label+")").join(", ")}. Pick 2 only when the job clearly spans two trades. Respond ONLY with JSON: {"categories":["id"]}.`, `Customer description: "${description}"`, 100);
    const valid = (res.categories||[]).filter(id=>CATEGORIES.some(c=>c.id===id)).slice(0,2);
    if(valid.length) return valid;
  }catch(e){}
  const d = description.toLowerCase();
  const kw = [["plumbing",/leak|pipe|sink|drip|tap|faucet|toilet|drain|water/],["electrical",/socket|outlet|breaker|wiring|light|electric|fuse/],["roofing",/roof|gutter|shingle|tile|chimney/],["hvac",/heat|boiler|furnace|radiator|ac\b|air con|cooling|thermostat/],["painting",/paint|drywall|plaster|wall\b|ceiling/],["carpentry",/door|window|cabinet|wood|floor|deck|shelf/],["masonry",/brick|concrete|mortar|patio|driveway|crack/]];
  const hits = kw.filter(([id, re])=>re.test(d)).map(([id])=>id).slice(0,2);
  return hits.length ? hits : ["handyman"];
}
async function suggestQuote(req){
  const system = `You draft quote line items for a small repairs contractor (${BRAND}). Propose 2-5 realistic line items (labor, materials, disposal, etc.) with USD prices a small contractor would charge for this job. Round prices to sensible figures. Respond ONLY with JSON, nothing else: {"items":[{"desc":"short line item","qty":1,"price":140}],"basis":"one short sentence framing this as learned pricing, e.g. 'Drawn from 14 similar sink-leak jobs, typically quoted $180–$420.'"}`;
  const user = `Job: ${req.ai.title}\nSummary: ${req.ai.summary}\nKey facts: ${req.ai.keyFacts.map(f=>f.label+": "+f.value).join("; ")}\nCategory: ${catsLabel(req)}. Urgency: ${urgOf(req.urgency).label}. Property: ${req.property.type}, ${req.property.age||"age unknown"}.`;
  return claudeJSON(system, user, 500);
}
function resizePhoto(file){
  return new Promise((resolve, reject)=>{
    const img = new Image();
    img.onload = ()=>{
      const max = 900, s = Math.min(1, max/Math.max(img.width, img.height));
      const c = document.createElement("canvas");
      c.width = Math.round(img.width*s); c.height = Math.round(img.height*s);
      c.getContext("2d").drawImage(img, 0, 0, c.width, c.height);
      URL.revokeObjectURL(img.src);
      resolve(c.toDataURL("image/jpeg", 0.72));
    };
    img.onerror = reject;
    img.src = URL.createObjectURL(file);
  });
}
function money(n){ return "$" + Number(n||0).toLocaleString("en-US", {minimumFractionDigits:0, maximumFractionDigits:2}); }
function BrandMark({size=28, dark}){
  const c = dark ? "#fff" : "var(--blue-500)";
  return <svg width={size} height={size} viewBox="0 0 28 28" aria-hidden="true"><path d="M2 2h24v9h-9v15H2V2z" transform="rotate(180 14 14)" fill={c}></path></svg>;
}
function KeyFacts({facts}){
  return <div style={{display:"grid", gridTemplateColumns:"repeat(auto-fill,minmax(150px,1fr))", gap:"var(--s-3)"}}>
    {facts.map((f,i)=><div key={i} style={{background:"var(--bg-2)", borderRadius:"var(--r-md)", padding:"10px 12px"}}>
      <div className="t-micro" style={{marginBottom:3}}>{f.label}</div>
      <div style={{fontSize:14, fontWeight:500, letterSpacing:"-.005em"}}>{f.value}</div>
    </div>)}
  </div>;
}
Object.assign(window, {useState, useEffect, useRef, useMemo, BRAND, CATEGORIES, URGENCIES, Icon, catOf, catsList, catsLabel, urgOf, uid, timeAgo, loadRequests, saveRequests, nextQuestion, batchQuestions, buildSummary, fallbackSummary, FALLBACK_QS, MAX_QUESTIONS, resizePhoto, money, BrandMark, KeyFacts, classifyCategory, suggestQuote});
