import { useState, useRef, useEffect } from 'react'
import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'

const URL_REGEX =
  /^(https?:\/\/)?([\w-]+\.)+[\w-]{2,}(\/[\w\-._~:/?#[\]@!$&'()*+,;=%]*)?$/i

const initialForm = {
  userName: '',
  userEmail: '',
  stationName: '',
  stationLocation: '',
  stationWebsite: '',
}

export default function App() {
  const [form, setForm] = useState(initialForm)
  const [errors, setErrors] = useState({})
  const [isSubmitting, setIsSubmitting] = useState(false)
  const [isThinking, setIsThinking] = useState(false)
  const [isStreaming, setIsStreaming] = useState(false)
  const [result, setResult] = useState('')
  const [status, setStatus] = useState(null)
  const [streamError, setStreamError] = useState(null)
  const resultRef = useRef(null)

  useEffect(() => {
    if (resultRef.current) {
      resultRef.current.scrollTop = resultRef.current.scrollHeight
    }
  }, [result])

  const handleChange = (e) => {
    const { name, value } = e.target
    setForm((prev) => ({ ...prev, [name]: value }))
  }

  const validate = () => {
    const next = {}
    if (!form.userName.trim()) next.userName = 'Required'
    if (!form.userEmail.trim() || !/^\S+@\S+\.\S+$/.test(form.userEmail))
      next.userEmail = 'Valid email required'
    if (!form.stationName.trim()) next.stationName = 'Required'
    if (!form.stationLocation.trim()) next.stationLocation = 'Required'
    if (!form.stationWebsite.trim() || !URL_REGEX.test(form.stationWebsite))
      next.stationWebsite = 'Valid URL required'
    setErrors(next)
    return Object.keys(next).length === 0
  }

  // Parse the NDJSON stream from /api/agent-proxy: one JSON object per
  // line. Returns a resume payload `{sessionId, lastEventId, startedAt}`
  // if the server emitted a `segment_end` (Netlify Edge caps a single
  // streaming response at ~60 s, so longer briefs span multiple
  // segments). Returns null when the stream ends cleanly (`done`,
  // `error`, or upstream EOF without a segment_end).
  const readStream = async (response) => {
    const reader = response.body.getReader()
    const decoder = new TextDecoder('utf-8')
    let buffer = ''
    let firstByte = true
    let segmentEnd = null
    let fatalError = null

    const handleEvent = (msg) => {
      switch (msg?.type) {
        case 'text':
          if (typeof msg.text === 'string' && msg.text) {
            setResult((prev) => prev + msg.text)
          }
          break
        case 'status':
          setStatus(msg)
          break
        case 'segment_end':
          if (typeof msg.sessionId === 'string' && msg.sessionId) {
            segmentEnd = {
              sessionId: msg.sessionId,
              lastEventId: msg.lastEventId,
              startedAt: msg.startedAt,
            }
          }
          break
        case 'error':
        case 'session_error':
          fatalError = msg.message || 'streaming error'
          setStreamError(fatalError)
          break
        case 'done':
        case 'heartbeat':
        default:
          break
      }
    }

    const flushLines = () => {
      let nl
      while ((nl = buffer.indexOf('\n')) !== -1) {
        const line = buffer.slice(0, nl).trim()
        buffer = buffer.slice(nl + 1)
        if (!line) continue
        try {
          handleEvent(JSON.parse(line))
        } catch {
          // Ignore malformed lines — the heartbeat / done sentinel
          // will still tell us when the stream is over.
        }
      }
    }

    while (true) {
      const { done, value } = await reader.read()
      if (done) break
      buffer += decoder.decode(value, { stream: true })
      if (firstByte) {
        // Once any bytes arrive, flip 'thinking' off and 'streaming' on.
        setIsThinking(false)
        setIsStreaming(true)
        firstByte = false
      }
      flushLines()
    }
    // Flush any trailing buffered bytes (line without final \n).
    buffer += decoder.decode()
    if (buffer.trim()) {
      try {
        handleEvent(JSON.parse(buffer.trim()))
      } catch {
        /* ignore */
      }
    }

    // Only return a resume payload if we got a clean segment_end AND
    // no fatal error fired in the same segment.
    return fatalError ? null : segmentEnd
  }

  const handleSubmit = async (e) => {
    e.preventDefault()
    if (!validate()) return

    setResult('')
    setStatus(null)
    setStreamError(null)
    setIsSubmitting(true)
    setIsThinking(true)
    setIsStreaming(false)

    try {
      let resume = null
      // Loop across segments. Each iteration is one HTTP request;
      // the server closes it at ~54 s and the next iteration picks
      // up from `lastEventId` so the user sees one continuous brief.
      // Hard cap on iterations as a belt-and-braces guard against a
      // runaway segment loop.
      for (let i = 0; i < 40; i++) {
        const body = resume
          ? {
              sessionId: resume.sessionId,
              lastEventId: resume.lastEventId,
              startedAt: resume.startedAt,
            }
          : form

        const response = await fetch('/api/agent-proxy', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(body),
        })

        if (!response.ok || !response.body) {
          throw new Error(
            `Request failed: ${response.status} ${response.statusText}`,
          )
        }

        const next = await readStream(response)
        if (!next) break
        resume = next
      }
    } catch (err) {
      console.error(err)
      setStreamError(err.message || 'Something went wrong while streaming.')
    } finally {
      setIsSubmitting(false)
      setIsThinking(false)
      setIsStreaming(false)
      setStatus(null)
    }
  }

  return (
    <div className="min-h-screen w-full font-serif">
      <div className="mx-auto max-w-3xl px-6 py-12">
        <header className="mb-10">
          <h1 className="text-4xl font-semibold tracking-tight">
            PMC Funder Discovery Tool
          </h1>
          <p className="mt-2 text-stone-600">
            Tell us about your public media station and we'll have an
            Anthropic-powered agent draft a tailored funding outlook.
          </p>
        </header>

        <form
          onSubmit={handleSubmit}
          className="space-y-5 rounded-xl border border-stone-300 bg-white/70 p-6 shadow-sm"
        >
          <Field
            label="Your Name"
            name="userName"
            value={form.userName}
            onChange={handleChange}
            error={errors.userName}
          />
          <Field
            label="Your Email"
            name="userEmail"
            type="email"
            value={form.userEmail}
            onChange={handleChange}
            error={errors.userEmail}
          />
          <Field
            label="Station Name"
            name="stationName"
            value={form.stationName}
            onChange={handleChange}
            error={errors.stationName}
          />
          <Field
            label="Station Location"
            name="stationLocation"
            value={form.stationLocation}
            onChange={handleChange}
            error={errors.stationLocation}
          />
          <Field
            label="Station Website"
            name="stationWebsite"
            type="url"
            placeholder="https://example.org"
            value={form.stationWebsite}
            onChange={handleChange}
            error={errors.stationWebsite}
          />

          <button
            type="submit"
            disabled={isSubmitting}
            className="w-full rounded-md bg-stone-900 px-4 py-3 text-white transition hover:bg-stone-700 disabled:cursor-not-allowed disabled:bg-stone-400"
          >
            {isSubmitting ? 'Working…' : 'Generate Funding Brief'}
          </button>
        </form>

        <section className="mt-10">
          {isSubmitting && !isStreaming && (
            <StatusBanner>
              {isThinking ? (
                <>
                  <Spinner /> Claude is thinking…
                </>
              ) : (
                <>
                  <Spinner /> Connecting to Agent…
                </>
              )}
            </StatusBanner>
          )}

          {isStreaming && status && (
            <StatusBanner>
              <Spinner /> {renderStatus(status)}
            </StatusBanner>
          )}

          {streamError && (
            <div className="rounded-md border border-red-300 bg-red-50 p-4 text-red-800">
              {streamError}
            </div>
          )}

          {(result || isStreaming) && (
            <article
              ref={resultRef}
              className="markdown max-h-[60vh] overflow-y-auto rounded-xl border border-stone-300 bg-white p-6 leading-relaxed shadow-sm"
            >
              <ReactMarkdown remarkPlugins={[remarkGfm]}>
                {result}
              </ReactMarkdown>
              {isStreaming && <span className="cursor-pulse" />}
            </article>
          )}
        </section>
      </div>
    </div>
  )
}

function Field({ label, name, type = 'text', value, onChange, error, placeholder }) {
  return (
    <label className="block">
      <span className="mb-1 block text-sm font-medium text-stone-800">
        {label}
      </span>
      <input
        type={type}
        name={name}
        value={value}
        onChange={onChange}
        placeholder={placeholder}
        className={`w-full rounded-md border bg-white px-3 py-2 outline-none transition focus:ring-2 focus:ring-stone-700 ${
          error ? 'border-red-400' : 'border-stone-300'
        }`}
      />
      {error && <span className="mt-1 block text-sm text-red-600">{error}</span>}
    </label>
  )
}

function renderStatus(status) {
  if (!status) return null
  switch (status.kind) {
    case 'thinking':
      return 'Thinking…'
    case 'tool_use':
      return status.label || `Using tool: ${status.name || 'tool'}`
    case 'tool_result':
      if (status.ok) return 'Tool finished — continuing…'
      return `Tool error: ${status.message || 'unknown error'}`
    case 'session_error':
      return `Session error: ${status.message || 'unknown error'}`
    default:
      return 'Working…'
  }
}

function StatusBanner({ children }) {
  return (
    <div className="mb-4 flex items-center gap-3 rounded-md border border-stone-300 bg-stone-100 px-4 py-3 text-stone-700">
      {children}
    </div>
  )
}

function Spinner() {
  return (
    <svg
      className="h-4 w-4 animate-spin text-stone-700"
      viewBox="0 0 24 24"
      fill="none"
    >
      <circle
        className="opacity-25"
        cx="12"
        cy="12"
        r="10"
        stroke="currentColor"
        strokeWidth="4"
      />
      <path
        className="opacity-75"
        fill="currentColor"
        d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"
      />
    </svg>
  )
}
