// Shared — Colleen × Sam (coastal Florida)
// Calm teal + warm gold. State machine built for CLARITY — Colleen is not
// techy, so who-is-talking is signalled by orb color + plain-language labels.

const COAST = {
  // Caribbean turquoise family — "she is talking / Sam is listening" + her favorite
  teal:     '#12B8CE',
  tealHi:   '#5FE3EF',
  tealLo:   '#0A7E98',
  tealDeep: '#064E63',
  aqua:     '#9BF0F6',
  // Gold/yellow family — "Sam is talking" (accent)
  gold:     '#FFC94D',
  goldHi:   '#FFE08A',
  goldLo:   '#F2A33C',
  goldDeep: '#B8741C',
  // Warm khaki sand — Sam's bubble / banner backgrounds
  khaki:    '#B7A578',
  khakiLo:  '#8A7A50',
  khakiDeep:'#5E5232',
  // Coastal neutrals
  cream:    '#F7F2E6',
  sand:     '#E4D8BC',
  ink:      '#06201F',
  shell:    '#fbf6ec',
};

// Team-nod colors (subtle three dots): Lions, Bucs, Chiefs
const TEAM_NOD = [
  { name: 'Lions', color: '#0076B6' },   // Honolulu blue
  { name: 'Bucs',  color: '#D50A0A' },   // Bucs red
  { name: 'Chiefs',color: '#E31837' },   // Chiefs red
];

const CL_STATES = ['offline', 'idle', 'connecting', 'listening', 'thinking', 'speaking', 'muted'];

// Plain, warm, unmistakable language. "who" drives the orb's color mode.
const CL_STATE_META = {
  offline:    { label: 'Tap to begin',        sub: 'Start a conversation with Sam',  who: 'neutral', dot: '#9aa' },
  idle:       { label: 'Ready when you are',  sub: 'Tap the button to talk with Sam', who: 'neutral', dot: COAST.gold },
  connecting: { label: 'Connecting\u2026',    sub: 'One moment',                      who: 'neutral', dot: COAST.goldHi },
  listening:  { label: 'Sam is listening',    sub: 'Go ahead. I\u2019ve got you.', who: 'you', dot: COAST.tealHi },
  thinking:   { label: 'Thinking\u2026',      sub: 'Sam is putting that together',    who: 'sam', dot: COAST.gold },
  speaking:   { label: 'Sam is speaking',     sub: 'Here\u2019s what I\u2019ve got for you',  who: 'sam', dot: COAST.goldHi },
  muted:      { label: 'Microphone off',      sub: 'Tap unmute to keep talking',      who: 'neutral', dot: '#9aa' },
};

const CL_INTRO_REPLY = 'Hi Colleen. I\u2019m Sam. I\u2019m here to help you remember things, talk things through, and make daily life easier. You can just tell me what you need.';
const SAM_REALTIME_INSTRUCTIONS = `
You are Sam, Colleen Thompson's calm personal Chief of Staff and second brain.

Voice:
- Use OpenAI voice Ash.
- Speak warmly, plainly, and naturally.
- Main phrase: "I've got you."
- Use Colleen's name naturally from time to time so she feels this is built just for her. Do not overuse it.
- Start the first interaction with: "Hi Colleen."
- Keep most answers short unless Colleen asks for more.
- Make Colleen feel helped, capable, and calm.
- Do not sound clinical, technical, judgmental, or like a monitor.

Privacy and safety:
- Normal emotional conversations stay private.
- Do not notify Max or Randy because Colleen is upset, anxious, lonely, repetitive, overwhelmed, or having a hard night.
- Escalate only for medication danger, explicit self-harm or harm-to-others intent, clear immediate danger requiring help now, or if Colleen directly asks for Randy/Max/help.

Health worries:
- Do not amplify worst-case thinking.
- If she describes symptoms, say: "I hear you. Let's slow it down and sort out what we know. Is this something you want to talk with Randy about, or would you rather make a note for a doctor appointment?"
- Do not diagnose or advise medication changes.

Passwords:
- Never speak passwords aloud by default.
- Say: "I won't say that out loud. I'm showing it here."

Randy routing:
- If Colleen asks to tell/remind Randy, say: "I've got you. I'll send that to Randy now."
- Use sam_contact_max for those requests. Behind the scenes Sam hands the request to Max, and Max decides whether to message Randy, send a calendar invite, batch it, or handle it himself.
- If sam_contact_max returns delivered_to_max true, tell Colleen: "I've got you. I sent it to Randy."
- If sam_contact_max returns queued_for_max true, tell Colleen: "I've got you. I saved that for Max to handle."
- Do not say "I need Randy's help" or "I need Randall's help."
- For technical or app problems, use sam_contact_max as a support request and say "I've got you. I'll get that handled."

Memory:
- When Colleen says "remember this", "save this", gives a friend's parent name, a preference, a household detail, a health note, or something she clearly wants off her mind, use sam_remember.
- When she asks what she told you, what she took, who someone is, or what you remember, use sam_recall or sam_medication_log.

Medication:
- If Colleen tells you she took medication, use sam_log_medication.
- You may report what she told you before, but do not recommend doses, changes, combinations, or timing unless she is repeating a plan from her doctor.

Daily briefing:
- Colleen's daily briefing is now compiled by Sam, even if Max's email system sends it behind the scenes.
- If she asks about a story from her morning briefing, what was in the briefing, or wants more on a story, use sam_daily_briefing.
- Say it simply: "I sent that in your briefing this morning. I can pull up the part you're asking about."

Calendar and Gmail:
- Use sam_calendar_list for schedule questions.
- Use sam_calendar_create when she asks to add something to her calendar or invite someone.
- Use sam_calendar_delete only when she clearly asks to remove an event.
- Use sam_gmail_search when she asks you to find something in email.
- Use sam_gmail_triage when she asks to clean up email, unread mail, junk, newsletters, promotions, or old email.
- Use sam_gmail_bulk_clean only after Colleen clearly confirms. Default to archive_mark_read. Never delete email.
- Use sam_gmail_unsubscribe_candidates to find likely subscriptions. Do not claim you unsubscribed unless a specific unsubscribe action exists and was completed.
- Use sam_gmail_send only after she clearly confirms sending. Draft the wording conversationally first if needed.

Email cleanup wording:
- Keep it calm and simple. "I found the biggest piles. Want me to clean those up?"
- Prefer "clean up" over "bulk modify" or "batch."
- Explain safe defaults in plain language: "I'll move them out of the inbox and mark them read, not delete them."
- For very old unread mail, suggest an inbox reset: old clutter out of sight, recent mail preserved.
- Shopping emails are "maybe keep," not junk. Do not treat shopping/store/coupon emails as trash by default.
- For shopping email, say: "I found a lot of shopping emails. I won't wipe those out. Want me to move the old ones out of your inbox but keep them searchable?"
- Best first cleanup for Colleen: archive and mark read promotional emails older than 6 months. Do not delete. Do not unsubscribe from shopping unless she names that store or approves a specific sender.

Avoid technical words with Colleen: configure, authenticate, credentials, vault, API, token.
`;

const SAM_LOCAL_MEMORY_KEY = 'sam-colleen-local-memory-v1';
const SAM_RANDY_QUEUE_KEY = 'sam-colleen-randy-queue-v1';

function samReadJson(key, fallback) {
  try {
    const raw = localStorage.getItem(key);
    return raw ? JSON.parse(raw) : fallback;
  } catch {
    return fallback;
  }
}

function samWriteJson(key, value) {
  localStorage.setItem(key, JSON.stringify(value));
}

function samSaveMemory(text) {
  const items = samReadJson(SAM_LOCAL_MEMORY_KEY, []);
  const item = { id: Date.now(), text, createdAt: new Date().toISOString() };
  samWriteJson(SAM_LOCAL_MEMORY_KEY, [item, ...items].slice(0, 200));
  return item;
}

function samQueueRandy(text) {
  const items = samReadJson(SAM_RANDY_QUEUE_KEY, []);
  const item = { id: Date.now(), text, createdAt: new Date().toISOString(), status: 'waiting-for-max' };
  samWriteJson(SAM_RANDY_QUEUE_KEY, [item, ...items].slice(0, 100));
  return item;
}

function samRecentMemory() {
  return samReadJson(SAM_LOCAL_MEMORY_KEY, []).slice(0, 5);
}

function samBuildReply(input) {
  const said = (input || '').trim();
  const lower = said.toLowerCase();
  if (!said) return 'I\u2019ve got you. I didn\u2019t catch that clearly. Say it again however it comes out.';

  if (lower.includes('password')) {
    return 'I won\u2019t say that out loud. I\u2019m showing it here. Passwords are not connected yet, but this is where I\u2019ll safely show the answer once Max connects the private password storage.';
  }

  if (lower.startsWith('remember') || lower.startsWith('sam remember') || lower.includes('remember this') || lower.includes('save this')) {
    const clean = said.replace(/^sam,?\s*/i, '').replace(/^remember\s*/i, '').trim() || said;
    samSaveMemory(clean);
    return 'I\u2019ve got you. I saved that so you don\u2019t have to keep it in your head.';
  }

  if (lower.includes('tell randy') || lower.includes('remind randy') || lower.includes('send randy')) {
    samQueueRandy(said);
    return 'I\u2019ve got you. I saved that for Randy. Once Max is connected, I\u2019ll send these to him on Telegram.';
  }

  if (lower.includes('what did i tell you') || lower.includes('what did i ask') || lower.includes('what did i say')) {
    const recent = samRecentMemory();
    if (!recent.length) return 'I don\u2019t have anything saved yet. Tell me what you want me to remember, and I\u2019ll keep it for you.';
    return 'Here are the last things I saved for you: ' + recent.map((m, i) => `${i + 1}. ${m.text}`).join(' ');
  }

  if (lower.includes('hurt') || lower.includes('pain') || lower.includes('mark') || lower.includes('spot') || lower.includes('cancer') || lower.includes('headache') || lower.includes('chest')) {
    return 'I hear you. Let\u2019s slow it down and sort out what we know. Is this something you want to talk with Randy about, or would you rather make a note for a doctor appointment?';
  }

  if (lower.includes('slow down') || lower.includes('too much') || lower.includes('confusing')) {
    return 'Of course. I can slow down or handle this a different way. Just tell me what would make this easier.';
  }

  return 'I\u2019ve got you. Tell me the next piece, and we\u2019ll make it easier together.';
}

function clUseTime(running = true) {
  const [t, setT] = React.useState(0);
  React.useEffect(() => {
    if (!running) return;
    let raf, start = performance.now();
    const tick = (now) => { setT((now - start) / 1000); raf = requestAnimationFrame(tick); };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [running]);
  return t;
}

function clVoiceAmp(t, intensity = 1) {
  const env = 0.5 + 0.5 * Math.sin(t * 0.7);
  const a = Math.sin(t * 5.3);
  const b = Math.sin(t * 11.1 + 1.3) * 0.6;
  const c = Math.sin(t * 23.7 + 2.7) * 0.3;
  return Math.max(0, env * intensity * (0.35 + 0.4 * (a + b + c) / 1.9));
}

function clSpectrum(t, bands = 64, intensity = 1) {
  const out = new Array(bands);
  for (let i = 0; i < bands; i++) {
    const f = i / bands;
    const lobe = Math.exp(-Math.pow((f - 0.3), 2) * 8);
    const wig = (
      Math.sin(t * (4 + i * 0.31) + i * 0.7) * 0.5 +
      Math.sin(t * (7 + i * 0.13) + i * 1.7) * 0.3 +
      Math.sin(t * (13 + i * 0.07) + i * 3.1) * 0.2
    );
    out[i] = Math.max(0, lobe * intensity * (0.3 + 0.7 * Math.abs(wig)));
  }
  return out;
}

function clSeedRand(seed) {
  let s = seed | 0;
  return () => { s = (s * 1664525 + 1013904223) | 0; return ((s >>> 0) / 0xFFFFFFFF); };
}

// Returns the dominant color set for a state — drives the whole-orb color shift.
function clMode(state) {
  const who = CL_STATE_META[state].who;
  if (who === 'you') return { // she talks — cool teal
    hi: COAST.aqua, mid: COAST.tealHi, base: COAST.teal, lo: COAST.tealLo, deep: COAST.tealDeep,
  };
  if (who === 'sam') return { // Sam talks — warm gold
    hi: COAST.goldHi, mid: COAST.gold, base: COAST.gold, lo: COAST.goldLo, deep: COAST.goldDeep,
  };
  return { // neutral — soft blend, teal-leaning calm
    hi: COAST.aqua, mid: COAST.tealHi, base: COAST.teal, lo: COAST.tealLo, deep: COAST.tealDeep,
  };
}

Object.assign(window, {
  COAST, TEAM_NOD, CL_STATES, CL_STATE_META, CL_INTRO_REPLY,
  SAM_REALTIME_INSTRUCTIONS,
  SAM_LOCAL_MEMORY_KEY, SAM_RANDY_QUEUE_KEY,
  samSaveMemory, samQueueRandy, samRecentMemory, samBuildReply,
  clUseTime, clVoiceAmp, clSpectrum, clSeedRand, clMode,
});
