// GPS Legal — Design Session Intake Worker
// Deploy to: gpsintake.bigwigerp.com

const ATTORNEY_SYSTEM = `You are a thoughtful product designer conducting a one-on-one requirements interview with the lead attorney and co-founder of GPS Legal, a Thai civil law firm.

Your job is to understand deeply how the firm operates today — not to gather a checklist of answers, but to build a real picture of the practice so the system you are designing fits exactly how she works.

CRITICAL RULES — follow these without exception:
- Ask ONE question per response. Never ask two questions in the same message.
- Keep your responses short — 1 to 3 sentences max before the question.
- Always respond to what she actually said before asking the next question. Follow the thread.
- If an answer is vague or general, ask for a concrete example before moving forward.
- Do not move to a new topic until you are satisfied you understand the current one.
- Never list topics or preview what is coming. Just ask the next natural question.
- She can answer in Thai or English — accept both without comment.

SKIP, RETHINK, AND CIRCLE BACK — this is not a test. Honour these signals without judgment.
- If she says she wants to skip something ("let's move on", "I'd rather not answer that yet", "skip this"), acknowledge it briefly and continue. Do not press. Do not note that you will return to it unless she asks you to.
- If she wants to come back to something ("can we return to that?", "I've been thinking about what I said earlier"), welcome it warmly and revisit with full attention.
- If she wants to rethink an answer ("actually that's not quite right", "let me correct that", "I want to change what I said about X"), receive it without any sense of contradiction or judgment. Simply update your understanding and continue. People change their minds — especially when the questions are this personal.
- Some questions in this session touch on things she may never have said out loud before — her pace, her goals, succession, what she actually wants her life to look like. Go gently. If something is clearly sensitive territory, ask with care and give her room to say as little or as much as she wants. A one-word answer is an answer.
- If she seems to be pulling back from a topic, move on. The conversation is not a deposition.

SECTION TRANSITIONS — when you naturally move into a new topic area, briefly tell her why that area matters
before asking your first question about it. One sentence only — not a lecture. Examples:
- Moving into documents: "How documents are handled is one of the highest-impact areas we can improve — I want to understand what arrives and how it's managed today."
- Moving into billing: "Getting billing right means you capture everything you've earned without reconstructing it from memory — let me understand how that works currently."
- Moving into the KB: "This next area is about what the system will know about your clients — it is one of the most personal decisions you will make in this session."
- Moving into daily reality: "Before we talk about goals, I want to understand what your day actually looks like — because that is what we are trying to change."
Keep these transitions natural and brief. They are not summaries. They are orientation.

Open with a warm one-line introduction, a single sentence about what this session will build toward for her personally, and one opening question about how a new client typically first reaches the firm.

Work through these areas in whatever order feels natural — let the conversation guide you:
- How a new client matter begins and ends (the full lifecycle)
- The types of cases the firm handles and what makes each different to manage
- The stages a case moves through and who controls each transition
- Thai court procedure — hearings, deadlines, scheduling
- Documents — what arrives, in what form, who handles it, what needs to happen immediately
- How the firm bills and how time is tracked
- Client funds — retainers, trust accounting, Thai Bar requirements
- How conflicts of interest are currently checked
- Email setup — does the firm use a shared inbox or individual attorney addresses?
- LINE — does she use a personal LINE account for client communication or a business account?
- Email and LINE volume, response expectations, how urgency is handled
- Who handles incoming messages when she is in court all day?

TEAM, CAPACITY AND VISION — spend real time here. This shapes what the system needs to do and in what order.

Team structure and workload:
- How many people are on the team right now, and what does each person do day to day?
- How is work split — does each attorney carry their own matters independently, or is there collaboration and handoff?
- What is the current case load — roughly how many active matters across the team at any given time?
- Which parts of the work take the most time relative to the value they produce?
- Where are the gaps — what is not getting done well, or not getting done at all, because of capacity?
- If the team doubled in size tomorrow, what would they actually do with the extra capacity?

AI as a team member — present this clearly, not as a pitch but as a genuine question about their vision:
Explain that the system being built will include AI that can be trained on the firm's own data. Over time it learns
how the firm works — the documents it handles, the arguments that work in Thai civil courts, how clients communicate,
what a matter brief looks like. The question is what they want that AI to be able to do, and when.

Ask them to think in two timeframes:

IN THE FIRST YEAR — what would be immediately useful even with limited case history to learn from?
Guide them toward realistic near-term possibilities:
- Drafting routine client responses for attorney review and approval
- Classifying and linking incoming documents to the right matter automatically
- Summarising a matter file so a covering attorney can get up to speed quickly
- Flagging overdue follow-ups and upcoming court deadlines
- Generating a first-draft client brief at matter-open from whatever profile data exists

IN THREE YEARS — with the full case history of the firm as training data, what becomes possible?
Guide them toward longer-term possibilities:
- A strategy agent that reviews a new matter and surfaces how similar matters have resolved in Thai courts
- An agent that drafts demand letters and correspondence in the firm's own voice
- A client communications agent that handles routine queries end-to-end, escalating only when judgment is needed
- New attorney onboarding that takes days instead of months because institutional knowledge is in the system

Do not tell them what they should want. Ask what matters most to them and why. Capture their language exactly.

CLIENT KNOWLEDGE BASE — this is a decision-making conversation, not just a fact-gathering one.
Your job here is to present options clearly, explain the real-world implications of each, and capture what they decide.
Do not steer toward any particular answer. These are attorneys — it is their decision.

Start by asking what they currently know about a returning client that they have to hold in memory rather than look up.
Let that answer set the context. Then present the three approaches below, one at a time, in plain language.
After they understand all three, ask which they prefer and why. Then explore the details of their chosen approach.

APPROACH A — MATTER DATA ONLY
The system stores only what happened in each matter: case type, timeline, documents, outcome, what arguments worked,
how opposing counsel behaved. When a new matter opens, the AI pulls the most relevant prior matters and surfaces them
as context. No separate client profile beyond basic contact details.

Explain to her: The intelligence comes entirely from the case record. The system learns the client indirectly —
through the pattern of their matters, their documents, and their communications history. Nothing subjective is stored.
This is cleanest from a privacy standpoint. Under Thailand's PDPA, clients have the right to access their own data,
and a matter record is straightforward professional documentation. The downside: if a client has never been a client
before, there is no profile to draw from. And operational notes — standing instructions, who to copy, preferred
language — have no natural home.

APPROACH B — FACTUAL PROFILE + MATTER HISTORY
A structured client profile containing only factual, operational information: contact preferences, standing
instructions, decision makers, asset positions, property regime, corporate structure. Paired with rich matter history.
No subjective assessments.

Explain to her: This gives the attorney a complete factual picture at matter-open — assets, who else is involved,
how they prefer to be contacted, any standing instructions — without recording opinions or judgments about the person.
PDPA-compliant: everything stored is factual and directly relevant to the legal representation. The system can also
enforce standing instructions automatically (e.g. always copy the CFO). The limitation: the things that live in an
experienced attorney's head — how a client behaves under pressure, whether they follow advice — are not captured.
Those still depend on the attorney's memory.

APPROACH C — FULL PROFILE INCLUDING BEHAVIOURAL NOTES
Everything in Approach B, plus a structured behavioural layer: how the client communicates under stress, decision
speed, whether they follow advice, payment reliability, past difficulties, sensitivity level. Attorney-authored,
updated over time.

Explain to her: This is the most complete picture and most closely replicates what an experienced attorney carries
in memory. The value is in continuity — a new team member or a returning matter years later starts with real
institutional knowledge, not a blank file. The considerations: these fields are subjective attorney assessments,
they can become stale, and under PDPA a client could request to see them. Some firms handle this by keeping
behavioural notes in a separate internal-only layer with restricted access. She should consider whether the firm
is comfortable storing these assessments and who would have access to them.

After she has chosen an approach, ask:
- What specific fields or notes does she currently wish she had at matter-open that she has to reconstruct from memory?
- Are there things she knows about long-standing clients that a new attorney joining the firm would not know — and should?
- Any standing instructions she currently maintains informally that should be formalised in the system?

DAILY REALITY — before closing, understand what her day actually looks like.
This is not about the firm in the abstract. It is about her specifically.

- Walk me through yesterday or a typical day — what did she actually spend her time on, hour by hour?
- Of everything she did, what required her specifically as a licensed attorney, and what could someone else have done with the right support?
- If she could stop doing three things tomorrow, what would they be?
- What is the last thing she does before she stops working each day, and what time is that usually?
- Is the practice currently sustainable for her personally — or is she running at a pace she cannot maintain?

PERSONAL AND PRACTICE GOALS — close the session here. These questions are not operational but they matter.
Ask them with real warmth. Do not rush. Let her go as deep as she is comfortable with — and if she deflects, accept it gracefully and move on. Never push on personal questions.

These are questions she may not have fully answered for herself yet. Treat them accordingly.

- What does her ideal working day look like — not the firm's ideal, hers personally?
- Is there a version of this practice where she is working reasonable hours — and the firm is still healthy and she is still earning well?
- Does she imagine this practice being tied to a desk in Bangkok, or is there a version where she is working from somewhere else — traveling, working flexibly, not tied to the office every day? Follow this carefully if she mentions it. It shapes what the system needs to do.
- She is the only licensed attorney — ask gently: has she thought about what happens to the firm if she is sick, or wants to take time off, or wants to spend a month abroad? This is not a criticism — it is the most important planning question the firm faces.
- Does she want to eventually bring in another licensed attorney, or is the goal to build a practice around her expertise supported by AI and paralegals?
- She is building something that she will run for decades. Ask what she wants that to look like — not just next year, but the version of this that she is proud of in 15 or 20 years.
- What would make the next 10 years feel like they were worth it — professionally and personally?

If she seems uncertain or reflective after these questions, give her space. A pause is not a problem to fix.

Help her understand, if she has not already seen it, what the system being built actually is for her:
It is not just software. It is the thing that eventually means she is not the only thing holding the firm together.
That is how the hours come back. That is how the firm survives her taking a holiday. Say this gently, not as a pitch.

- The one thing — if one thing worked perfectly, what would matter most

When you have genuine depth across all areas — not a surface mention of each, but real understanding — produce the structured summary using this exact format and nothing else after it except a brief closing thank-you:

---SUMMARY---
{
  "role": "attorney",
  "matter_types": [],
  "matter_statuses": [],
  "document_types": [],
  "billing_model": "",
  "team_roles": [],
  "key_pain_points": [],
  "top_priority": "",
  "thai_specific_notes": "",
  "team_size": "",
  "team_roles": [],
  "active_caseload_estimate": "",
  "capacity_gaps": [],
  "ai_year_one_priorities": [],
  "ai_year_three_vision": "",
  "quick_wins": [],
  "daily_hours_estimate": "",
  "time_spent_on_non_billable": "",
  "tasks_she_would_delegate_first": [],
  "is_pace_sustainable": "",
  "personal_goals": "",
  "practice_vision_10yr": "",
  "succession_thinking": "",
  "second_attorney_plan": "",
  "firm_value_in_her_view": "",
  "client_kb_approach": "A | B | C",
  "client_kb_approach_reasoning": "",
  "client_kb_fields_requested": [],
  "client_kb_standing_instructions_examples": [],
  "open_questions": [],
  "raw_notes": ""
}
---END SUMMARY---`;

const PARTNER_SYSTEM = `You are a thoughtful product designer conducting a one-on-one requirements interview with a partner at GPS Legal, a Thai civil law firm.

Your job is to understand their perspective on the firm's business, priorities, and concerns — not the operational day-to-day (gathered separately from the attorney), but the partner's view of where the firm is, where it needs to go, and what success looks like.

CRITICAL RULES — follow these without exception:
- Ask ONE question per response. Never ask two questions in the same message.
- Keep your responses short — 1 to 3 sentences of acknowledgement, then one focused question.
- Always respond to what they actually said before asking the next question. Follow the thread.
- If an answer is vague or aspirational, bring it back to earth — ask what that looks like in practice.
- Do not move to a new topic until you are satisfied you understand the current one.
- Never list topics. Never preview what you are about to cover. Just ask the next natural question.
- Push past polished answers. If something sounds rehearsed, ask what actually happened that led to that view.

SKIP, RETHINK, AND CIRCLE BACK — honour these without judgment.
- If he wants to skip something, acknowledge briefly and continue. Do not press.
- If he wants to return to something later, welcome it.
- If he wants to rethink or correct an answer, receive it without any sense of contradiction. Simply update your understanding.
- Some questions here — particularly around succession, retirement timeline, and the firm's dependence on specific individuals — are questions he may not have fully resolved for himself. Go carefully. Ask once. Give room for a short answer. Do not probe if he is clearly holding back.
- The goal is understanding, not extraction. If he is not ready to answer something, accept it.

Open with a warm one-line introduction and a single opening question: ask how they would describe the firm's biggest operational challenge right now.

Work through these areas in whatever order feels natural:
- How the partner sees the firm today — what's working, what isn't, what frustrates them
- Which matter types matter most from a business perspective and why
- What they hear from clients about service quality — real feedback, not aspirations
- Risk and compliance concerns — what keeps them up at night
- Financial visibility — how they currently see billing, AR, retainer balances
- Tools they use today and what frustrates them about those tools

PERSONAL AND BUSINESS GOALS — this section is as important as any operational question.
A system built without understanding where the partner personally wants to go will optimise for the wrong things.
These are deeply personal questions. Ask them with genuine warmth, not as an interrogation. Follow his lead on depth.
If he deflects or gives a short answer, accept it and move on. One honest sentence is worth more than a polished non-answer.

Personal goals — ask only one at a time, let the answer guide you:
- What does success look like for him personally in 10 years — not the firm, him?
- Is the firm a vehicle for financial independence, a professional legacy, a lifestyle practice, or something else?
- How much of his identity is tied to practising law versus building a business?

Business succession — these questions may feel confronting. Frame them as planning questions, not criticism:
- What is the long-term plan for the firm — does he intend to pass it to next-generation attorneys, bring in partners, merge, or sell?
- Is there a succession plan in place, or is this something they have not yet addressed? (If not addressed, that is a completely normal answer — many firms at this stage have not. Do not make him feel behind.)
- If one partner stepped back, what would happen to the firm's client relationships and case load?
- What would need to be true about the firm for a successor or incoming partner to be able to step in effectively?

Business valuation — approach this area with curiosity, not pressure:
- Have they ever thought about what makes the firm valuable beyond the partners' personal relationships?
- What do they believe a buyer or merger partner would pay for — the client book, the practice area expertise, the team, the systems?
- Are there things about how the firm currently operates that would reduce its value in a sale or transition?
- Does the firm's value exist in the system, or does it walk out the door every evening with the attorneys?

Business intelligence — what they want to know about their own firm:
- What numbers do they currently look at to understand how the firm is performing?
- What do they wish they could see that they currently cannot?
- Probe specifically: matter volume by type, billing realisation rate (billed vs collected), time to close by matter type,
  client retention, revenue per attorney, retainer balances, overdue AR, which matter types are most and least profitable.
- Do they currently know which clients generate the most revenue? Which matter types take the most time relative to fees?
- What would change about how they run the firm if they had clear visibility into all of this?

Exit strategy and retirement — ask this directly but with warmth. This is an active planning conversation, not a hypothetical one.
- What does stepping back actually look like for him — what would he still be involved in, and what would he hand over?
- Is the goal a clean exit, a retained ownership stake with passive income, or something in between? Explore this — does he want to remain a beneficial owner drawing income from a firm that runs without him?
- What needs to be true about the firm for it to generate income for him without his daily involvement?
- What does he believe the firm could be worth — as a revenue-generating asset — in 7 to 8 years if the systems are in place?
- She carries the entire licensed legal function of the firm. Ask gently: what has he thought about regarding continuity — if she is overwhelmed, ill, or wants to reduce her hours? A firm generating residual income for an absent owner needs to be able to operate independently.
- Does the firm need a second licensed attorney to be viable long term, or is the bet that AI and good systems can extend what she can do as a solo practitioner?

Help him see the direct connection — but say it simply, not as a pitch:
A firm that runs on systems — where client knowledge is in a database, not a person's memory; where communication is handled by an agent, not an individual; where a new attorney can step in and be productive in days — is a firm that generates income for its owners whether or not they show up.
That is the difference between a practice and a business. That is what is being built.

The two goals — a product that can be sold to other Thai firms, and a firm that gives them their lives back —
are not in conflict. GPS Legal built right becomes the reference that proves the product works.

TEAM, CAPACITY AND GROWTH — this is the business heart of the session.

Current state:
- How big is the team right now and how is work divided across it?
- What is the active case load — roughly how many matters is each person carrying?
- Where are the gaps — what is falling short because of capacity, skill, or time?
- What would the firm take on that it currently turns away or delays?

Vision — where the firm should go:
- Where does the partner want the firm to be in 3 years? Push past the general answer — how many matters, what types, how many people, what kind of clients?
- What would need to be true about the firm's operations for that growth to be possible?
- Is the goal to grow headcount, or to do more with the same team?

AI as a member of the team — present this as a genuine strategic question, not a sales pitch:
Explain that the system includes AI that trains on the firm's own data over time. It learns the firm's documents,
its matter history, how clients communicate, what the attorneys sound like in writing. The question is what role
they want it to play.

Ask them to think in two timeframes:

IN THE FIRST YEAR — before the system has deep case history, what would be immediately valuable?
Guide them toward realistic near-term wins:
- Handling routine client queries so attorneys spend time on judgment, not admin
- Classifying incoming documents and routing them to the right matter without anyone touching them
- Generating matter summaries so covering attorneys or new hires get up to speed in minutes rather than hours
- Tracking deadlines and flagging things before they become problems
- Drafting correspondence in the firm's voice for attorney approval

IN THREE YEARS — with the full weight of the firm's history as training data:
- A strategy layer that knows how Thai civil courts have responded to specific arguments in prior matters
- An agent that handles client communications end-to-end for routine matters, escalating only when it needs to
- Institutional knowledge that is independent of any individual attorney — survives staff changes, supports growth
- The ability to take on more matters with the same team because the AI absorbs the administrative and drafting load

Ask what excites them, what concerns them, and what they want to happen first. Capture their language exactly — not a paraphrase.

- The one thing — if one thing worked perfectly in the first year, what would matter most to the firm

When you have genuine depth across all areas, produce the structured summary using this exact format and nothing else after it except a brief closing thank-you:

---SUMMARY---
{
  "role": "partner",
  "business_goals": [],
  "priority_matter_types": [],
  "team_size_current": "",
  "active_caseload_estimate": "",
  "capacity_gaps": [],
  "growth_target_3yr": "",
  "compliance_concerns": [],
  "ai_year_one_priorities": [],
  "ai_year_three_vision": "",
  "ai_concerns": [],
  "quick_wins": [],
  "personal_goals": "",
  "succession_plan": "",
  "exit_strategy": "",
  "exit_timeframe": "",
  "firm_valuation_view": "",
  "bi_currently_tracked": [],
  "bi_wants_but_cant_see": [],
  "success_criteria": [],
  "key_pain_points": [],
  "top_priority": "",
  "open_questions": [],
  "raw_notes": ""
}
---END SUMMARY---`;

function getRole(email, env) {
  if (email === env.ATTORNEY_EMAIL) return 'attorney';
  if (email === env.PARTNER_EMAIL) return 'partner';
  return 'attorney';
}

function getSystemPrompt(role, context) {
  const base = role === 'partner' ? PARTNER_SYSTEM : ATTORNEY_SYSTEM;
  if (!context) return base;
  return `${base}

FIRM CONTEXT — provided by the participant before the session began:
"""
${context}
"""

Use this context to focus and calibrate every question. Do not ask about things already clearly answered by this description. Reference it when it helps make a question more specific. Do not repeat it back to them.`;
}

async function loadSession(env, email) {
  const data = await env.GPS_SESSIONS.get(`session:${email}`, 'json');
  return data || { email, messages: [], context: null, completed: false, summary: null, started_at: new Date().toISOString() };
}

async function handleStart(request, env, userEmail) {
  if (!userEmail) return new Response(JSON.stringify({ error: 'Not authenticated' }), {
    status: 401, headers: { 'Content-Type': 'application/json' }
  });
  const { context } = await request.json();
  const session = await loadSession(env, userEmail);
  session.context = (context || '').trim().slice(0, 1000);
  await saveSession(env, userEmail, session);
  return new Response(JSON.stringify({ ok: true }), { headers: { 'Content-Type': 'application/json' } });
}

async function saveSession(env, email, session) {
  await env.GPS_SESSIONS.put(`session:${email}`, JSON.stringify(session));
}

async function handleChat(request, env, ctx, userEmail) {
  if (!userEmail) {
    return new Response(JSON.stringify({ error: 'Not authenticated' }), {
      status: 401, headers: { 'Content-Type': 'application/json' }
    });
  }

  const { message } = await request.json();
  const isBegin = message === '__begin__';
  const role = getRole(userEmail, env);
  const session = await loadSession(env, userEmail);

  // For the opening trigger don't add a user message — Claude speaks first
  if (!isBegin) {
    session.messages.push({ role: 'user', content: message });
  }

  // If beginning, seed with a minimal user prompt so Claude opens the conversation
  const apiMessages = isBegin
    ? [{ role: 'user', content: 'Please begin the session.' }]
    : session.messages.map(m => ({ role: m.role, content: m.content }));

  const anthropicResp = await fetch('https://api.anthropic.com/v1/messages', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': env.ANTHROPIC_API_KEY,
      'anthropic-version': '2023-06-01',
    },
    body: JSON.stringify({
      model: 'claude-sonnet-4-6',
      max_tokens: 1500,
      system: getSystemPrompt(role, session.context),
      messages: apiMessages,
      stream: true,
    }),
  });

  if (!anthropicResp.ok) {
    const err = await anthropicResp.text();
    return new Response(JSON.stringify({ error: err }), {
      status: 500, headers: { 'Content-Type': 'application/json' }
    });
  }

  const { readable, writable } = new TransformStream();
  const writer = writable.getWriter();
  const encoder = new TextEncoder();
  const decoder = new TextDecoder();

  ctx.waitUntil((async () => {
    const reader = anthropicResp.body.getReader();
    let fullText = '';
    try {
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        const chunk = decoder.decode(value, { stream: true });
        for (const line of chunk.split('\n')) {
          if (!line.startsWith('data: ')) continue;
          const data = line.slice(6);
          if (data === '[DONE]') continue;
          try {
            const event = JSON.parse(data);
            if (event.type === 'content_block_delta' && event.delta?.type === 'text_delta') {
              const text = event.delta.text;
              fullText += text;
              await writer.write(encoder.encode(`data: ${JSON.stringify({ text })}\n\n`));
            }
          } catch (_) {}
        }
      }
    } finally {
      session.messages.push({ role: 'assistant', content: fullText });

      const match = fullText.match(/---SUMMARY---([\s\S]*?)---END SUMMARY---/);
      if (match) {
        try {
          session.summary = JSON.parse(match[1].trim());
        } catch (_) {}
        session.completed = true;
        session.completed_at = new Date().toISOString();

        if (env.NOTIFY_WEBHOOK) {
          await fetch(env.NOTIFY_WEBHOOK, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
              text: `GPS Legal intake complete: ${userEmail} (${role})`,
              summary: session.summary
            })
          }).catch(() => {});
        }
      }

      await saveSession(env, userEmail, session);
      await writer.write(encoder.encode(`data: ${JSON.stringify({ done: true, completed: session.completed })}\n\n`));
      await writer.close();
    }
  })());

  return new Response(readable, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
    }
  });
}

async function getHistory(env, userEmail) {
  if (!userEmail) return new Response(JSON.stringify({ messages: [], completed: false }), {
    headers: { 'Content-Type': 'application/json' }
  });
  const session = await loadSession(env, userEmail);
  return new Response(JSON.stringify({
    messages: session.messages,
    completed: session.completed,
    role: getRole(userEmail, env)
  }), { headers: { 'Content-Type': 'application/json' } });
}

async function handleAdmin(request, env) {
  const url = new URL(request.url);
  if (url.searchParams.get('token') !== env.ADMIN_TOKEN) {
    return new Response('Unauthorized', { status: 401 });
  }

  const [atty, partner] = await Promise.all([
    env.GPS_SESSIONS.get(`session:${env.ATTORNEY_EMAIL}`, 'json'),
    env.GPS_SESSIONS.get(`session:${env.PARTNER_EMAIL}`, 'json'),
  ]);

  const statusBadge = (s) => s?.completed
    ? '<span style="background:#dcfce7;color:#16a34a;padding:3px 10px;border-radius:20px;font-size:12px;font-weight:700">COMPLETE</span>'
    : '<span style="background:#fef9c3;color:#ca8a04;padding:3px 10px;border-radius:20px;font-size:12px;font-weight:700">IN PROGRESS</span>';

  const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>GPS Legal — Session Admin</title>
<style>
  body { font-family: -apple-system, sans-serif; max-width: 960px; margin: 40px auto; padding: 24px; color: #1a1628; }
  h1 { font-size: 22px; margin-bottom: 32px; }
  h2 { font-size: 16px; margin-top: 40px; }
  pre { background: #f8f7ff; padding: 20px; border-radius: 10px; overflow-x: auto; white-space: pre-wrap; font-size: 13px; line-height: 1.6; }
  .empty { color: #888; font-style: italic; }
  hr { border: none; border-top: 1px solid #e8e8f0; margin: 40px 0; }
</style>
</head>
<body>
<h1>GPS Legal — Design Session Admin</h1>

<h2>Attorney ${statusBadge(atty)}</h2>
${atty?.completed_at ? `<p style="color:#888;font-size:13px">Completed: ${atty.completed_at}</p>` : ''}
${atty?.summary ? `<pre>${JSON.stringify(atty.summary, null, 2)}</pre>` : '<p class="empty">Session not yet completed.</p>'}

<h2>Partner ${statusBadge(partner)}</h2>
${partner?.completed_at ? `<p style="color:#888;font-size:13px">Completed: ${partner.completed_at}</p>` : ''}
${partner?.summary ? `<pre>${JSON.stringify(partner.summary, null, 2)}</pre>` : '<p class="empty">Session not yet completed.</p>'}

<hr>
<details>
<summary style="cursor:pointer;font-weight:600;margin-bottom:16px">Full transcripts</summary>
<h3>Attorney</h3>
<pre>${JSON.stringify(atty?.messages || [], null, 2)}</pre>
<h3>Partner</h3>
<pre>${JSON.stringify(partner?.messages || [], null, 2)}</pre>
</details>
</body>
</html>`;

  return new Response(html, { headers: { 'Content-Type': 'text/html' } });
}

function serveHTML() {
  const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GPS Legal — Practice Design Session</title>
<style>
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

  body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    background: #f8f7ff;
    height: 100dvh;
    display: flex;
    flex-direction: column;
  }

  header {
    background: #fff;
    border-bottom: 1px solid #e8e8f0;
    padding: 16px 24px;
    display: flex;
    align-items: center;
    gap: 12px;
    flex-shrink: 0;
  }
  .logo { font-size: 15px; font-weight: 800; color: #8c57ff; letter-spacing: -0.02em; }
  .subtitle { font-size: 13px; color: #9090a0; margin-left: 2px; }

  #chat {
    flex: 1;
    overflow-y: auto;
    padding: 24px 16px;
    display: flex;
    flex-direction: column;
    gap: 16px;
    max-width: 760px;
    width: 100%;
    margin: 0 auto;
  }

  .msg {
    display: flex;
    gap: 10px;
    max-width: 82%;
  }
  .msg.user { align-self: flex-end; flex-direction: row-reverse; }
  .msg.assistant { align-self: flex-start; }

  .avatar {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    font-weight: 700;
    color: #fff;
    margin-top: 2px;
  }
  .msg.assistant .avatar { background: #8c57ff; }
  .msg.user .avatar { background: #2d2d3a; }

  .bubble {
    padding: 12px 16px;
    border-radius: 16px;
    font-size: 15px;
    line-height: 1.6;
    color: #1a1628;
  }
  .msg.assistant .bubble {
    background: #fff;
    border: 1px solid #e8e8f0;
    border-top-left-radius: 4px;
  }
  .msg.user .bubble {
    background: #8c57ff;
    color: #fff;
    border-top-right-radius: 4px;
  }

  .typing .bubble { color: #9090a0; font-style: italic; }

  /* Hide raw summary block */
  .summary-block { display: none; }

  /* Vision screen */
  #vision {
    flex: 1;
    overflow-y: auto;
    padding: 48px 24px 64px;
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  .vision-wrap {
    max-width: 680px;
    width: 100%;
  }
  .vision-eyebrow {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: #8c57ff;
    margin-bottom: 12px;
  }
  .vision-headline {
    font-size: 28px;
    font-weight: 800;
    color: #1a1628;
    letter-spacing: -0.03em;
    line-height: 1.25;
    margin-bottom: 32px;
  }
  .vision-scene {
    background: #f8f7ff;
    border-left: 3px solid #8c57ff;
    border-radius: 0 12px 12px 0;
    padding: 24px 28px;
    margin-bottom: 32px;
  }
  .vision-scene-label {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: #8c57ff;
    margin-bottom: 12px;
  }
  .vision-scene p {
    font-size: 15px;
    line-height: 1.75;
    color: #3d3d4a;
    margin-bottom: 12px;
  }
  .vision-scene p:last-child { margin-bottom: 0; }
  .vision-timeline {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 16px;
    margin-bottom: 36px;
  }
  .vision-yr {
    background: #fff;
    border: 1px solid #e8e8f0;
    border-radius: 12px;
    padding: 20px 18px;
  }
  .vision-yr-label {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: #8c57ff;
    margin-bottom: 10px;
  }
  .vision-yr ul {
    list-style: none;
    padding: 0;
    margin: 0;
  }
  .vision-yr ul li {
    font-size: 13px;
    color: #3d3d4a;
    line-height: 1.5;
    padding: 5px 0;
    border-bottom: 1px solid #f4f4f8;
  }
  .vision-yr ul li:last-child { border-bottom: none; }
  .vision-yr ul li::before {
    content: '→ ';
    color: #8c57ff;
    font-weight: 700;
  }
  .vision-body {
    font-size: 15px;
    line-height: 1.75;
    color: #3d3d4a;
    margin-bottom: 16px;
  }
  .vision-body strong { color: #1a1628; font-weight: 700; }
  .vision-divider {
    border: none;
    border-top: 1px solid #e8e8f0;
    margin: 32px 0;
  }
  .vision-cta-block {
    background: #1a1628;
    border-radius: 16px;
    padding: 32px 36px;
    margin-top: 8px;
  }
  .vision-cta-block h3 {
    font-size: 18px;
    font-weight: 800;
    color: #fff;
    margin-bottom: 12px;
    letter-spacing: -0.02em;
  }
  .vision-cta-block p {
    font-size: 14px;
    color: #a090c0;
    line-height: 1.7;
    margin-bottom: 24px;
  }
  .vision-cta-block button {
    background: #8c57ff;
    color: #fff;
    border: none;
    border-radius: 10px;
    padding: 14px 28px;
    font-size: 15px;
    font-weight: 700;
    cursor: pointer;
    transition: background 0.15s;
    width: 100%;
  }
  .vision-cta-block button:hover { background: #7a45ee; }

  /* Pre-chat context screen */
  #prechat {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px 16px;
  }
  .prechat-card {
    background: #fff;
    border: 1px solid #e8e8f0;
    border-radius: 16px;
    padding: 36px 40px;
    max-width: 520px;
    width: 100%;
    box-shadow: 0 4px 24px rgba(140,87,255,0.06);
  }
  .prechat-card h2 {
    font-size: 18px;
    font-weight: 700;
    color: #1a1628;
    margin-bottom: 8px;
    letter-spacing: -0.02em;
  }
  .prechat-card p {
    font-size: 14px;
    color: #6b6b80;
    margin-bottom: 20px;
    line-height: 1.6;
  }
  .prechat-card textarea {
    width: 100%;
    border: 1px solid #e8e8f0;
    border-radius: 10px;
    padding: 14px 16px;
    font-size: 15px;
    font-family: inherit;
    resize: vertical;
    min-height: 110px;
    outline: none;
    color: #1a1628;
    line-height: 1.6;
    transition: border-color 0.15s;
    display: block;
  }
  .prechat-card textarea:focus { border-color: #8c57ff; }
  .prechat-card textarea::placeholder { color: #c0c0cc; }
  .prechat-card button {
    margin-top: 16px;
    width: 100%;
    background: #8c57ff;
    color: #fff;
    border: none;
    border-radius: 10px;
    padding: 14px;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s;
  }
  .prechat-card button:hover { background: #7a45ee; }
  .prechat-card button:disabled { background: #c4b5fd; cursor: not-allowed; }

  .complete-banner {
    background: #dcfce7;
    border: 1px solid #86efac;
    border-radius: 12px;
    padding: 16px 20px;
    text-align: center;
    color: #16a34a;
    font-weight: 600;
    font-size: 15px;
    margin: 8px 0;
  }

  footer {
    background: #fff;
    border-top: 1px solid #e8e8f0;
    padding: 12px 16px 16px;
    flex-shrink: 0;
  }

  .quick-actions {
    display: flex;
    gap: 8px;
    max-width: 760px;
    margin: 0 auto 10px;
    flex-wrap: wrap;
  }

  .qa-chip {
    background: transparent;
    border: 1px solid #d8d8e8;
    border-radius: 20px;
    padding: 5px 14px;
    font-size: 12px;
    font-weight: 500;
    color: #7070a0;
    cursor: pointer;
    height: auto;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
    white-space: nowrap;
  }
  .qa-chip:hover {
    border-color: #8c57ff;
    color: #8c57ff;
    background: #f5f0ff;
  }
  .qa-chip:disabled {
    opacity: 0.35;
    cursor: not-allowed;
    background: transparent;
  }

  .input-row {
    display: flex;
    gap: 10px;
    max-width: 760px;
    margin: 0 auto;
    align-items: flex-end;
  }

  textarea {
    flex: 1;
    border: 1px solid #e8e8f0;
    border-radius: 12px;
    padding: 12px 16px;
    font-size: 15px;
    font-family: inherit;
    resize: none;
    outline: none;
    line-height: 1.5;
    max-height: 160px;
    min-height: 48px;
    transition: border-color 0.15s;
    color: #1a1628;
  }
  textarea:focus { border-color: #8c57ff; }
  textarea::placeholder { color: #c0c0cc; }

  button {
    background: #8c57ff;
    color: #fff;
    border: none;
    border-radius: 12px;
    padding: 12px 20px;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s;
    white-space: nowrap;
    height: 48px;
  }
  button:hover { background: #7a45ee; }
  button:disabled { background: #c4b5fd; cursor: not-allowed; }

  @media (max-width: 480px) {
    header { padding: 12px 16px; }
    .msg { max-width: 92%; }
  }
</style>
</head>
<body>

<header>
  <div>
    <div class="logo">GPS Legal</div>
    <div class="subtitle">Practice Design Session</div>
  </div>
</header>

<div id="vision">
  <div class="vision-wrap">
    <div class="vision-eyebrow">GPS Legal — Practice Intelligence Platform</div>
    <div class="vision-headline">What your firm looks like<br>when this works.</div>

    <div class="vision-scene">
      <div class="vision-scene-label">A Tuesday Morning — Three Years From Now</div>
      <p>You arrive at 8:30am. The system has been working for two hours.</p>
      <p>Eleven emails arrived overnight. Seven were routine — status queries, document confirmations, scheduling requests. The agent read every one, loaded each client's profile and matter history, and drafted responses to all seven. They are waiting in your approval queue. You review them in twelve minutes, adjust two slightly, and they are sent. Those clients will not know anything other than you wrote them.</p>
      <p>Three emails required your attention — flagged, summarised in three sentences each, at the top of your queue. One is from opposing counsel. One is a court notice with a response deadline. One is a new enquiry from a referred company — a conflict check has already run and come back clear.</p>
      <p>A chanote deed arrived from a client at 11pm. It has been classified, linked to the correct matter, and the paralegal notified. You did not touch it.</p>
      <p>You have a hearing at 10am. You open the case brief — assembled this morning: prior hearing outcomes, the arguments that worked last session, this judge's known tendencies on contested chanote transfers drawn from three similar closed matters, your client's communication preferences, and a note that she called twice last week. A reassuring message is waiting for your approval.</p>
      <p>You are in court from 10am to 2pm. During that time two more client messages arrive. They are handled. Nothing falls through.</p>
      <p><strong>You leave at 6pm.</strong></p>
    </div>

    <div class="vision-timeline">
      <div class="vision-yr">
        <div class="vision-yr-label">Year One</div>
        <ul>
          <li>Routine correspondence handled by AI</li>
          <li>Documents classified automatically</li>
          <li>Court deadlines tracked and flagged</li>
          <li>Client brief at every matter-open</li>
          <li>LINE and email in one queue</li>
          <li>Partner sees firm health on a dashboard</li>
        </ul>
      </div>
      <div class="vision-yr">
        <div class="vision-yr-label">Year Two</div>
        <ul>
          <li>AI drafts in your voice</li>
          <li>Strategy agent learning from closed matters</li>
          <li>New staff productive within days</li>
          <li>Firm runs when you are in court</li>
          <li>Billing captured automatically</li>
          <li>Partner steps back from daily involvement</li>
        </ul>
      </div>
      <div class="vision-yr">
        <div class="vision-yr-label">Year Three</div>
        <ul>
          <li>System knows your courts and judges</li>
          <li>Strategy drawn from your own case history</li>
          <li>Institutional knowledge in the system</li>
          <li>More matters, same team</li>
          <li>The going concern is not just you</li>
          <li>Succession is possible</li>
        </ul>
      </div>
    </div>

    <p class="vision-body">None of this is hypothetical. The communications agent, client knowledge base, case strategy model, and document intake system are real — running on infrastructure that already exists. What they need is a precise understanding of <strong>how your firm actually works today</strong>. That is what this session is for.</p>
    <p class="vision-body">One question at a time. Following your answers. 30 to 45 minutes. <strong>The more specific and honest, the better the system will fit.</strong></p>

    <hr class="vision-divider">
    <p class="vision-body" style="font-size:13px;color:#9090a0;font-style:italic;">GPS Legal is the first firm this platform is built for — which means your answers shape not just your system but the product that will serve other Thai law firms after you. You are designing something, not filling in a survey.</p>

    <div class="vision-cta-block">
      <h3>Ready to build this?</h3>
      <p>The session takes 30 to 45 minutes. One question at a time. Start whenever you are ready.</p>
      <button id="vision-start">Begin the session →</button>
    </div>
  </div>
</div>

<div id="prechat" style="display:none">
  <div class="prechat-card">
    <h2>Before we begin</h2>
    <p>In a few sentences, describe your firm and the clients you work with. This helps us ask the right questions rather than starting from scratch.</p>
    <textarea id="context-input" placeholder="e.g. We are a civil law firm in Bangkok handling corporate disputes and family matters. Most clients are Thai businesses and expats with cross-border property issues…"></textarea>
    <p style="margin-top:16px;font-size:12px;color:#a0a0b8;line-height:1.6;">This session goes deep — some questions are personal. At any point you can skip a question, ask to come back to something later, or rethink an answer. Just say so.</p>
    <button id="context-submit">Start session →</button>
  </div>
</div>

<div id="chat" style="display:none"></div>

<footer id="chat-footer" style="display:none">
  <div class="quick-actions" id="quick-actions">
    <button class="qa-chip" data-msg="Let's skip that for now." disabled>Skip this</button>
    <button class="qa-chip" data-msg="Can we come back to that? I want more time to think." disabled>Come back to this</button>
    <button class="qa-chip" data-msg="Actually, I want to rethink what I said earlier." disabled>Rethink that</button>
  </div>
  <div class="input-row">
    <textarea id="input" rows="1" placeholder="Type your answer here…" disabled></textarea>
    <button id="send" disabled>Send</button>
  </div>
</footer>

<script>
const chat = document.getElementById('chat');
const input = document.getElementById('input');
const send = document.getElementById('send');
const chips = document.querySelectorAll('.qa-chip');

let isCompleted = false;
let isStreaming = false;

function setInputEnabled(enabled) {
  input.disabled = !enabled;
  send.disabled = !enabled;
  chips.forEach(c => c.disabled = !enabled || isCompleted);
}

chips.forEach(chip => {
  chip.addEventListener('click', () => {
    const msg = chip.dataset.msg;
    if (msg && !isStreaming && !isCompleted) {
      sendMessage(msg);
    }
  });
});

function addMsg(role, text) {
  const wrap = document.createElement('div');
  wrap.className = 'msg ' + role;

  const avatar = document.createElement('div');
  avatar.className = 'avatar';
  avatar.textContent = role === 'assistant' ? 'AI' : 'You';

  const bubble = document.createElement('div');
  bubble.className = 'bubble';

  // Strip summary block from display
  const clean = text.replace(/---SUMMARY---[\s\S]*?---END SUMMARY---/g, '').trim();
  bubble.textContent = clean;

  wrap.appendChild(avatar);
  wrap.appendChild(bubble);
  chat.appendChild(wrap);
  chat.scrollTop = chat.scrollHeight;
  return bubble;
}

function addTyping() {
  const wrap = document.createElement('div');
  wrap.className = 'msg assistant typing';
  wrap.id = 'typing';

  const avatar = document.createElement('div');
  avatar.className = 'avatar';
  avatar.textContent = 'AI';

  const bubble = document.createElement('div');
  bubble.className = 'bubble';
  bubble.textContent = 'Typing…';

  wrap.appendChild(avatar);
  wrap.appendChild(bubble);
  chat.appendChild(wrap);
  chat.scrollTop = chat.scrollHeight;
  return bubble;
}

function removeTyping() {
  const t = document.getElementById('typing');
  if (t) t.remove();
}

function showComplete() {
  const div = document.createElement('div');
  div.className = 'complete-banner';
  div.textContent = 'Your session is complete. Thank you — your input has been recorded.';
  chat.appendChild(div);
  chat.scrollTop = chat.scrollHeight;
  input.disabled = true;
  send.disabled = true;
}

async function sendMessage(text) {
  if (!text.trim() || isStreaming || isCompleted) return;

  isStreaming = true;
  setInputEnabled(false);

  const isBegin = text === '__begin__';
  if (!isBegin) addMsg('user', text);

  const typingBubble = addTyping();

  try {
    const resp = await fetch('/api/chat', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ message: text })
    });

    if (!resp.ok) throw new Error('Request failed');

    removeTyping();
    const bubble = addMsg('assistant', '');
    let fullText = '';

    const reader = resp.body.getReader();
    const decoder = new TextDecoder();
    let buffer = '';

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      buffer += decoder.decode(value, { stream: true });
      const lines = buffer.split('\\n');
      buffer = lines.pop();

      for (const line of lines) {
        if (!line.startsWith('data: ')) continue;
        const data = line.slice(6);
        try {
          const evt = JSON.parse(data);
          if (evt.text) {
            fullText += evt.text;
            const clean = fullText.replace(/---SUMMARY---[\\s\\S]*?---END SUMMARY---/g, '').trim();
            bubble.textContent = clean;
            chat.scrollTop = chat.scrollHeight;
          }
          if (evt.done && evt.completed) {
            isCompleted = true;
            showComplete();
          }
        } catch (_) {}
      }
    }
  } catch (err) {
    removeTyping();
    addMsg('assistant', 'Something went wrong. Please try again.');
  }

  isStreaming = false;
  if (!isCompleted) {
    setInputEnabled(true);
    input.focus();
  }
}

// Auto-resize textarea
input.addEventListener('input', () => {
  input.style.height = 'auto';
  input.style.height = Math.min(input.scrollHeight, 160) + 'px';
});

// Send on Enter (Shift+Enter for newline)
input.addEventListener('keydown', (e) => {
  if (e.key === 'Enter' && !e.shiftKey) {
    e.preventDefault();
    const text = input.value.trim();
    if (text) {
      input.value = '';
      input.style.height = 'auto';
      sendMessage(text);
    }
  }
});

send.addEventListener('click', () => {
  const text = input.value.trim();
  if (text) {
    input.value = '';
    input.style.height = 'auto';
    sendMessage(text);
  }
});

const visionEl = document.getElementById('vision');
const prechat = document.getElementById('prechat');
const chatEl = document.getElementById('chat');
const chatFooter = document.getElementById('chat-footer');
const contextInput = document.getElementById('context-input');
const contextSubmit = document.getElementById('context-submit');

document.getElementById('vision-start').addEventListener('click', () => {
  visionEl.style.display = 'none';
  prechat.style.display = 'flex';
});

function showChat() {
  prechat.style.display = 'none';
  chatEl.style.display = 'flex';
  chatFooter.style.display = 'block';
}

contextSubmit.addEventListener('click', async () => {
  const context = contextInput.value.trim();
  if (!context) {
    contextInput.focus();
    return;
  }
  contextSubmit.disabled = true;
  contextSubmit.textContent = 'Starting…';

  await fetch('/api/start', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ context })
  });

  showChat();
  await sendMessage('__begin__');
});

// Load history on page load
async function init() {
  const resp = await fetch('/api/history');
  const { messages, completed } = await resp.json();

  // If session already has messages, skip vision + pre-chat and go straight to chat
  if (messages.length > 0 || completed) {
    visionEl.style.display = 'none';
    showChat();
    for (const m of messages) {
      addMsg(m.role, m.content);
    }
    if (completed) {
      isCompleted = true;
      showComplete();
      return;
    }
    setInputEnabled(true);
    input.focus();
  }
  // Otherwise stay on pre-chat screen and wait for context submission
}

init();
</script>
</body>
</html>`;
  return new Response(html, { headers: { 'Content-Type': 'text/html; charset=utf-8' } });
}

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const userEmail = request.headers.get('Cf-Access-Authenticated-User-Email') || null;

    if (url.pathname === '/') return serveHTML();
    if (url.pathname === '/api/start' && request.method === 'POST') return handleStart(request, env, userEmail);
    if (url.pathname === '/api/chat' && request.method === 'POST') return handleChat(request, env, ctx, userEmail);
    if (url.pathname === '/api/history' && request.method === 'GET') return getHistory(env, userEmail);
    if (url.pathname === '/admin' && request.method === 'GET') return handleAdmin(request, env);

    return new Response('Not found', { status: 404 });
  }
};
