import { useState, useEffect, useRef } from "react";

const MCP = "https://mcp.notion.com/mcp";
const DB = "https://www.notion.so/34340e02c2c680d5926ff95582480703";

const fmt = d => d.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' });
const iso = d => `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;

async function claudeCall(system, user, tokens = 4000, noMcp = false) {
  const body = {
    model: "claude-sonnet-4-20250514",
    max_tokens: tokens,
    system,
    messages: [{ role: "user", content: user }],
  };
  if (!noMcp) body.mcp_servers = [{ type: "url", url: MCP, name: "notion" }];
  const r = await fetch("https://api.anthropic.com/v1/messages", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body)
  });
  if (!r.ok) throw new Error(`HTTP ${r.status}`);
  return r.json();
}

const AUTO_FIELDS = ["good_day", "rough_day", "watch_for", "community_prompt", "notification_text", "art_recs", "speaker_guide_line"];

async function generateMissing(p) {
  const missing = AUTO_FIELDS.filter(k => !p[k] || !p[k].trim());
  if (missing.length === 0) return {};

  const systemPrompt = `You are a content writer for Our Daily Quilt (ODQ). Given a quote and author, generate content for missing fields. Return ONLY a JSON object with the requested fields.

Field specs:
- good_day: Short declarative push, has edge, specific enough to act on. Sometimes a command. No questions, no filler. "Today" only when it earns it.
- rough_day: Reframes without naming emotions or assuming how someone feels. No demands. Strips to essential permission or redirect. Can be as short as three words. Never diagnoses.
- watch_for: A standalone sentence fragment naming a specific observable behavior. UI prepends "Watch for the moment today when..." so the value continues from that. No adverbs doing interpretive work.
- community_prompt: Single question inviting users to share something from their experience useful to others. Transferable. Plain language. Does not mention quote or author. Ends with ?
- notification_text: Format "[Full Name] on [what the quote is about]". The on... part intriguing and human. No period. One line.
- art_recs: 5 recommendations across music, film, painting, literature, and one wildcard. Format: Title, Artist — one sentence why it connects. All 5 in one field.
- speaker_guide_line: 1 sentence about who this person was and why their perspective matters. Start with a verb, omit name at start. Grounded in what they actually lived. No reverence.`;

  const res = await claudeCall(
    systemPrompt,
    `Quote: "${p.Quote}" — ${p.author}\n\nGenerate ONLY these missing fields as JSON: ${missing.join(", ")}`,
    2000,
    true
  );

  for (const b of (res.content || [])) {
    if (b.type === "text") {
      const m = b.text.replace(/```json|```/g, "").match(/\{[\s\S]*\}/);
      if (m) { try { return JSON.parse(m[0]); } catch(e) {} }
    }
  }
  return {};
}

function parseJSON(data) {
  for (const b of (data.content || [])) {
    if (b.type === "text") {
      const m = b.text.replace(/```json|```/g, '').match(/\{[\s\S]*\}/);
      if (m) { try { return JSON.parse(m[0]); } catch(e) {} }
    }
  }
  return null;
}

const WARM = {
  bg: "#F5F0E8",
  surface: "#FDFAF4",
  surface2: "#EDE8DC",
  border: "rgba(120,100,60,0.15)",
  borderStrong: "rgba(120,100,60,0.28)",
  text: "#2C2416",
  text2: "#7A6A50",
  text3: "#B0A080",
  accent: "#3D2F1A",
  accentFg: "#FDFAF4",
  blue: "#5B7FA6",
  green: "#4A8C6A",
  amber: "#A07030",
  red: "#A04040",
  sectionBg: "rgba(120,100,60,0.06)",
};

const inputStyle = {
  width: "100%",
  background: "none",
  border: "none",
  outline: "none",
  fontSize: 14,
  color: WARM.text,
  fontFamily: "Inter, system-ui, -apple-system, sans-serif",
  lineHeight: 1.7,
  resize: "none",
  whiteSpace: "pre-wrap",
  wordBreak: "break-word",
  overflowWrap: "break-word",
};

function Field({ label, children, changed, fullWidth, mono }) {
  return (
    <div style={{
      background: WARM.surface,
      border: `1px solid ${changed ? WARM.blue : WARM.border}`,
      borderRadius: 8,
      padding: "10px 14px",
      gridColumn: fullWidth ? "1/-1" : undefined,
      transition: "border-color 0.15s",
      boxShadow: "0 1px 3px rgba(80,60,20,0.06)",
    }}>
      <div style={{ display: "flex", alignItems: "center", marginBottom: 6 }}>
        <span style={{
          fontSize: 9,
          fontWeight: 600,
          color: WARM.text3,
          textTransform: "uppercase",
          letterSpacing: "0.1em",
          fontFamily: "'DM Mono', monospace",
        }}>{label}</span>
        {changed && <div style={{ width: 5, height: 5, borderRadius: "50%", background: WARM.blue, marginLeft: "auto", flexShrink: 0 }} />}
      </div>
      {children}
    </div>
  );
}

function SectionHeader({ title }) {
  return (
    <div style={{
      display: "flex",
      alignItems: "center",
      gap: 10,
      margin: "24px 0 12px",
    }}>
      <span style={{
        fontSize: 9,
        fontWeight: 700,
        color: WARM.text3,
        textTransform: "uppercase",
        letterSpacing: "0.14em",
        fontFamily: "'DM Mono', monospace",
        flexShrink: 0,
      }}>{title}</span>
      <div style={{ flex: 1, height: "0.5px", background: WARM.borderStrong, opacity: 0.5 }} />
    </div>
  );
}

function ImagePreview({ url }) {
  const [state, setState] = useState("idle");
  const prevUrl = useRef(null);

  useEffect(() => {
    if (url !== prevUrl.current) {
      prevUrl.current = url;
      setState(url ? "loading" : "idle");
    }
  }, [url]);

  if (!url) return null;

  return (
    <div style={{ marginTop: 10, borderRadius: 6, overflow: "hidden", border: `1px solid ${WARM.border}`, background: WARM.surface2 }}>
      {state === "error" ? (
        <div style={{ padding: "10px 14px", fontSize: 13, color: WARM.text2, display: "flex", alignItems: "center", gap: 8 }}>
          <span style={{ color: WARM.text3 }}>Can't embed image —</span>
          <a href={url} target="_blank" rel="noreferrer" style={{ color: WARM.blue, textDecoration: "none" }}>Open image ↗</a>
        </div>
      ) : (
        <>
          <img
            src={url}
            alt="Speaker portrait"
            onLoad={() => setState("loaded")}
            onError={() => setState("error")}
            style={{ width: "100%", maxHeight: 180, objectFit: "cover", objectPosition: "top", display: state === "loaded" ? "block" : "none" }}
          />
          {state === "loading" && (
            <div style={{ padding: "10px 14px", fontSize: 13, color: WARM.text3 }}>Loading image…</div>
          )}
        </>
      )}
    </div>
  );
}

export default function App() {
  const tomorrow = () => { const d = new Date(); d.setDate(d.getDate() + 1); return d; };
  const [date, setDate] = useState(tomorrow());
  const [status, setStatus] = useState({ state: "idle", msg: "Ready" });
  const [data, setData] = useState(null);
  const [orig, setOrig] = useState(null);
  const [changed, setChanged] = useState({});
  const [saving, setSaving] = useState(false);

  const pageId = useRef(null);
  const pageUrl = useRef(null);

  function shiftDate(d) {
    setDate(prev => { const n = new Date(prev); n.setDate(n.getDate() + d); return n; });
    setChanged({});
  }

  useEffect(() => { load(); }, [date]);

  async function load() {
    setStatus({ state: "loading", msg: "Fetching…" });
    setData(null); setOrig(null); setChanged({});
    try {
      const res = await claudeCall(
        `Query ODQ QUOTES DATABASE (${DB}). Find page where date_scheduled="${iso(date)}". Return ONLY valid JSON with these exact keys: page_id, page_url, Quote, author, speaker_dates, keyword, notification_text, good_day, rough_day, community_prompt, first_response, speaker_image_url, image_attribution, speaker_guide_line, watch_for, art_recs, art_recs_type, approved, reviewed. For all rich text / text properties return the full complete string with no truncation. Empty string for missing text fields, null for missing select fields. If page not found return {"error":"not_found"}.`,
        `Get the full content of the ODQ quote page scheduled for ${iso(date)}.`
      );
      const p = parseJSON(res);
      if (!p || p.error) { setStatus({ state: "", msg: "" }); setData("empty"); return; }
      pageId.current = p.page_id;
      pageUrl.current = p.page_url;

      // Auto-generate missing fields
      const hasMissing = AUTO_FIELDS.some(k => !p[k] || !p[k].trim());
      if (hasMissing) {
        setOrig(p); setData({ ...p });
        setStatus({ state: "loading", msg: "Generating missing fields…" });
        try {
          const generated = await generateMissing(p);
          const merged = { ...p, ...generated };
          const autoChanged = {};
          for (const k of Object.keys(generated)) autoChanged[k] = true;
          setOrig(p);
          setData(merged);
          setChanged(autoChanged);
          setStatus({ state: "success", msg: `Loaded · ${Object.keys(generated).length} fields generated` });
        } catch(e) {
          setStatus({ state: "success", msg: "Loaded (generation failed)" });
        }
      } else {
        setOrig(p); setData({ ...p });
        setStatus({ state: "success", msg: "Loaded" });
      }
    } catch(e) {
      setStatus({ state: "error", msg: e.message });
      setData("error");
    }
  }

  function update(key, val) {
    setData(d => ({ ...d, [key]: val }));
    setChanged(c => ({ ...c, [key]: val !== (orig?.[key] || '') }));
  }

  const changedKeys = Object.keys(changed).filter(k => changed[k]);
  const hasChanges = changedKeys.length > 0;

  function reset() { setData({ ...orig }); setChanged({}); }

  async function save() {
    if (!pageId.current || !hasChanges) return;
    setSaving(true); setStatus({ state: "loading", msg: "Saving…" });
    const updates = {};
    for (const k of changedKeys) updates[k] = data[k] || null;
    try {
      await claudeCall(
        `Update Notion page "${pageId.current}" with these properties: ${JSON.stringify(updates)}. Property types: keyword/notification_text/good_day/rough_day/community_prompt/first_response/speaker_image_url/image_attribution/speaker_guide_line/speaker_dates/watch_for/art_recs=text, art_recs_type/approved=select, "reviewed?"=checkbox(true if value is "__YES__"). Return {"success":true}.`,
        "Update the page.", 1000
      );
      setOrig({ ...data });
      setChanged({});
      setStatus({ state: "success", msg: "Saved" });
    } catch(e) {
      setStatus({ state: "error", msg: "Save failed" });
    }
    setSaving(false);
  }

  const dotColor = { loading: WARM.amber, success: WARM.green, error: WARM.red }[status.state] || WARM.text3;

  const ta = (k, rows = 3) => (
    <textarea
      rows={rows}
      value={data[k] || ""}
      onChange={e => update(k, e.target.value)}
      style={{ ...inputStyle, minHeight: rows * 24 }}
    />
  );

  const inp = (k) => (
    <input
      type="text"
      value={data[k] || ""}
      onChange={e => update(k, e.target.value)}
      style={{ ...inputStyle, display: "block" }}
    />
  );

  return (
    <div style={{ background: WARM.bg, minHeight: "100vh", fontFamily: "Inter, system-ui, sans-serif" }}>
      {/* Header */}
      <div style={{
        position: "sticky", top: 0, zIndex: 10,
        background: WARM.bg,
        borderBottom: `1px solid ${WARM.border}`,
        padding: "0 24px",
        height: 50,
        display: "flex", alignItems: "center", gap: 12,
      }}>
        <span style={{ fontSize: 13, fontWeight: 600, color: WARM.text2, flex: 1, fontFamily: "'DM Mono', monospace", letterSpacing: "0.06em" }}>ODQ · EDITOR</span>
        <div style={{ display: "flex", alignItems: "center", gap: 4 }}>
          {["‹","›"].map((ch, i) => (
            <button key={ch} onClick={() => shiftDate(i === 0 ? -1 : 1)} style={{ width: 26, height: 26, display: "flex", alignItems: "center", justifyContent: "center", border: `1px solid ${WARM.border}`, borderRadius: 6, background: WARM.surface, color: WARM.text2, cursor: "pointer", fontSize: 16, lineHeight: 1 }}>{ch}</button>
          ))}
          <span style={{ fontSize: 13, fontWeight: 500, color: WARM.text, background: WARM.surface, border: `1px solid ${WARM.borderStrong}`, borderRadius: 6, padding: "3px 10px", minWidth: 110, textAlign: "center", fontFamily: "'DM Mono', monospace" }}>{fmt(date)}</span>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 5, fontSize: 11, color: WARM.text3, fontFamily: "'DM Mono', monospace" }}>
          <div style={{ width: 6, height: 6, borderRadius: "50%", background: dotColor, animation: status.state === "loading" ? "pulse 1s infinite" : "none", flexShrink: 0 }} />
          {status.msg}
        </div>
      </div>

      <div style={{ maxWidth: 720, margin: "0 auto", padding: "0 24px 80px" }}>

        {(!data || data === "empty" || data === "error") && (
          <div style={{ textAlign: "center", padding: "5rem 1rem", color: WARM.text3, fontSize: 14, lineHeight: 2 }}>
            {!data ? "Loading…" : data === "empty" ? `No quote scheduled for ${fmt(date)}.` : `Couldn't load. ${status.msg}`}
          </div>
        )}

        {data && data !== "empty" && data !== "error" && (<>

          {/* Quote hero — sticky */}
          <div style={{ position: "sticky", top: 50, zIndex: 9, background: WARM.bg, paddingTop: 16, paddingBottom: 8 }}>
            <div style={{ background: WARM.surface, border: `1px solid ${WARM.border}`, borderRadius: 10, padding: "16px 20px", boxShadow: "0 2px 8px rgba(80,60,20,0.08)", display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 16 }}>
              <div>
                <div style={{ fontSize: 16, fontStyle: "italic", lineHeight: 1.6, color: WARM.text, marginBottom: 6 }}>"{data.Quote}"</div>
                <div style={{ fontSize: 12, color: WARM.text2, fontFamily: "'DM Mono', monospace" }}>— {data.author}{data.speaker_dates ? ` · ${data.speaker_dates}` : ""}</div>
              </div>
              {pageUrl.current && (
                <a href={pageUrl.current} target="_blank" rel="noreferrer" style={{ flexShrink: 0, width: 28, height: 28, display: "flex", alignItems: "center", justifyContent: "center", border: `1px solid ${WARM.border}`, borderRadius: 6, color: WARM.text3, textDecoration: "none", fontSize: 14, background: WARM.surface2 }}>↗</a>
              )}
            </div>
          </div>

          {/* GETTING STARTED */}
          <SectionHeader title="Getting Started" />
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(260px, 1fr))", gap: 8 }}>
            <Field label="Keyword" changed={changed.keyword}>{inp("keyword")}</Field>
            <Field label="Notification text" changed={changed.notification_text} fullWidth>{ta("notification_text", 2)}</Field>
            <Field label="Good day" changed={changed.good_day}>{ta("good_day", 2)}</Field>
            <Field label="Rough day" changed={changed.rough_day}>{ta("rough_day", 2)}</Field>
          </div>

          {/* COMMUNITY REFLECTION */}
          <SectionHeader title="Community Reflection" />
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(260px, 1fr))", gap: 8 }}>
            <Field label="Community prompt" changed={changed.community_prompt} fullWidth>{ta("community_prompt", 2)}</Field>
            <Field label="First response" changed={changed.first_response} fullWidth>{ta("first_response", 3)}</Field>
          </div>

          {/* SPEAKER CARD */}
          <SectionHeader title="Speaker Card" />
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(260px, 1fr))", gap: 8 }}>
            <Field label="Speaker image URL" changed={changed.speaker_image_url} fullWidth>
              {inp("speaker_image_url")}
              {data.speaker_image_url && (
                <a href={data.speaker_image_url} target="_blank" rel="noreferrer" style={{ display: "inline-block", marginTop: 8, fontSize: 12, color: WARM.blue, textDecoration: "none", border: `1px solid ${WARM.blue}`, borderRadius: 5, padding: "4px 10px" }}>View image ↗</a>
              )}

            </Field>
            <Field label="Attribution" changed={changed.image_attribution}>{inp("image_attribution")}</Field>
            <Field label="Speaker dates" changed={changed.speaker_dates}>{inp("speaker_dates")}</Field>
            <Field label="Speaker guide line" changed={changed.speaker_guide_line} fullWidth>{ta("speaker_guide_line", 2)}</Field>
          </div>

          {/* BEFORE YOU GO */}
          <SectionHeader title="Before You Go" />
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(260px, 1fr))", gap: 8 }}>
            <Field label="Watch for" changed={changed.watch_for} fullWidth>{ta("watch_for", 2)}</Field>
            <Field label="Art recs" changed={changed.art_recs} fullWidth>{ta("art_recs", 3)}</Field>
            <Field label="Art type" changed={changed.art_recs_type}>
              <select value={data.art_recs_type || ""} onChange={e => update("art_recs_type", e.target.value)} style={{ ...inputStyle, cursor: "pointer", display: "block" }}>
                {["","music","book","movie","art","other"].map(v => <option key={v} value={v}>{v || "—"}</option>)}
              </select>
            </Field>
          </div>

          {/* Review */}
          <SectionHeader title="Review" />
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(260px, 1fr))", gap: 8 }}>
            <Field label="Approved" changed={changed.approved}>
              <select value={data.approved || ""} onChange={e => update("approved", e.target.value)} style={{ ...inputStyle, cursor: "pointer", display: "block" }}>
                {["","TRUE","FALSE"].map(v => <option key={v} value={v}>{v || "—"}</option>)}
              </select>
            </Field>
            <Field label="Reviewed" changed={changed.reviewed}>
              <div style={{ display: "flex", alignItems: "center", gap: 8, paddingTop: 2 }}>
                <input type="checkbox" id="reviewed" checked={data.reviewed === "__YES__" || data.reviewed === true} onChange={e => update("reviewed", e.target.checked ? "__YES__" : "")} style={{ width: 15, height: 15, cursor: "pointer", accentColor: WARM.accent }} />
                <label htmlFor="reviewed" style={{ fontSize: 14, color: WARM.text2, cursor: "pointer", fontFamily: "Inter, system-ui, sans-serif" }}>Yes</label>
              </div>
            </Field>
          </div>

        </>)}
      </div>

      {/* Sticky action bar */}
      {data && data !== "empty" && data !== "error" && (
        <div style={{ position: "fixed", bottom: 0, left: 0, right: 0, background: WARM.bg, borderTop: `1px solid ${WARM.border}`, padding: "12px 24px", display: "flex", alignItems: "center", gap: 10 }}>
          <button onClick={save} disabled={saving || !hasChanges} style={{ background: hasChanges ? WARM.accent : WARM.text3, color: WARM.accentFg, border: "none", borderRadius: 6, padding: "8px 18px", fontSize: 14, fontWeight: 500, cursor: hasChanges ? "pointer" : "not-allowed", opacity: hasChanges ? 1 : 0.5, fontFamily: "Inter, system-ui, sans-serif", transition: "opacity 0.15s" }}>
            ✓ Save to Notion
          </button>
          <button onClick={reset} style={{ background: "none", border: `1px solid ${WARM.border}`, borderRadius: 6, padding: "7px 14px", fontSize: 14, color: WARM.text2, cursor: "pointer", fontFamily: "Inter, system-ui, sans-serif" }}>Reset</button>
          {hasChanges && <span style={{ fontSize: 11, color: WARM.text3, marginLeft: "auto", fontFamily: "'DM Mono', monospace" }}>{changedKeys.length} unsaved</span>}
        </div>
      )}

      <style>{`@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.3} } * { box-sizing: border-box; } textarea { overflow: hidden; } textarea:focus, input:focus, select:focus { outline: none; }`}</style>
    </div>
  );
}