import { listLogFiles, readLogFile, type StoredLogEvent } from "../log"

const typeColors: Record<string, string> = {
  webhook: "#4ade80",
  "webhook-ignored": "#facc15",
  pr: "#4ade80",
  comment: "#4ade80",
  "comment-skipped": "#facc15",
  review: "#4ade80",
  "thread-auto-created": "#60a5fa",
  "discord-relay": "#60a5fa",
  "discord-command-error": "#f87171",
  "discord-warning": "#facc15",
  "crash-log-found": "#f87171",
  startup: "#60a5fa",
  "discord-ready": "#60a5fa",
  error: "#f87171",
}

const summaryFields = ["type", "ts"] as const

function errorMessage(error: unknown): string {
  if (error instanceof Error) return error.message
  if (typeof error === "object" && error && "message" in error) return String((error as any).message)
  return String(error)
}

function errorStack(error: unknown): string | undefined {
  if (error instanceof Error) return error.stack
  if (typeof error === "object" && error && "stack" in error) return String((error as any).stack)
}

function getEventMeta(event: StoredLogEvent): Record<string, string> {
  const meta: Record<string, string> = {}
  for (const [key, value] of Object.entries(event)) {
    if (summaryFields.includes(key as any)) continue
    if (value === undefined) continue
    if (key === "error") {
      meta[key] = errorMessage(value)
      const stack = errorStack(value)
      if (stack) meta.stack = stack
      continue
    }
    meta[key] = typeof value === "string" ? value : String(value)
  }
  return meta
}

function EventRow({ event, index }: { event: StoredLogEvent; index: number }) {
  const color = typeColors[event.type] || "#9ca3af"
  const time = event.ts.slice(11, 19)

  const details: string[] = []
  if ("action" in event) details.push(event.action)
  if ("repo" in event) details.push(event.repo)
  if ("pr" in event) details.push(`#${event.pr}`)
  if ("user" in event) details.push(event.user)
  if ("detail" in event) details.push(event.detail)
  if ("command" in event) details.push(event.command)
  if ("eventType" in event) details.push(event.eventType)
  if (event.type === "error" && event.context) details.push(event.context)
  if (event.type === "error") {
    details.push(errorMessage(event.error))
  }

  const meta = getEventMeta(event)
  const hasExtra = Object.keys(meta).length > 0

  return (
    <>
      <tr
        style={`border-bottom: 1px solid #2a2a2a${hasExtra ? "; cursor: pointer" : ""}`}
        data-row={index}
        onclick={hasExtra ? `(function(e){var d=document.getElementById('detail-${index}');if(d){d.style.display=d.style.display==='none'?'table-row':'none';e.currentTarget.querySelector('.expand-icon').textContent=d.style.display==='none'?'+':'-'}})(event)` : undefined}
      >
        <td style="padding: 6px 4px 6px 12px; color: #4a4a4a; font-size: 11px; width: 16px; user-select: none">
          {hasExtra && <span class="expand-icon">+</span>}
        </td>
        <td data-ts={event.ts} style="padding: 6px 12px; color: #6b7280; white-space: nowrap; font-size: 13px">{time}</td>
        <td style={`padding: 6px 12px; color: ${color}; white-space: nowrap; font-weight: 600; font-size: 13px`}>
          {event.type}
        </td>
        <td style="padding: 6px 12px; color: #d1d5db; font-size: 13px">{details.join(" ")}</td>
      </tr>
      {hasExtra && (
        <tr id={`detail-${index}`} style="display: none; background: #141414">
          <td></td>
          <td colspan="3" style="padding: 8px 12px 12px">
            <div style="display: flex; flex-wrap: wrap; gap: 6px 20px; font-size: 12px">
              {Object.entries(meta).map(([key, value]) => (
                key === "stack" ? (
                  <div style="width: 100%">
                    <span style="color: #6b7280">{key}</span>
                    <pre style="color: #d1d5db; margin: 4px 0 0; font-size: 11px; white-space: pre-wrap; opacity: 0.8">{value}</pre>
                  </div>
                ) : (
                  <div style="display: flex; gap: 6px">
                    <span style="color: #6b7280">{key}</span>
                    <span style="color: #d1d5db; max-width: 500px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap">{value}</span>
                  </div>
                )
              ))}
            </div>
          </td>
        </tr>
      )}
    </>
  )
}

function Sidebar({ files, selectedFile }: { files: string[]; selectedFile?: string }) {
  // Group files by sha, sorted by most recent file in each group
  const grouped: Record<string, string[]> = {}
  for (const file of files) {
    const sha = file.split("_")[0] || "unknown"
    const list = grouped[sha] || (grouped[sha] = [])
    list.push(file)
  }
  const sortedGroups = Object.entries(grouped).sort(([, a], [, b]) => {
    return b[0]!.localeCompare(a[0]!)
  })

  return (
    <nav style="width: 280px; min-width: 280px; background: #141414; border-right: 1px solid #2a2a2a; overflow-y: auto; padding: 16px 0">
      <div style="padding: 0 16px 12px; color: #9ca3af; font-size: 11px; text-transform: uppercase; letter-spacing: 1px">
        Log Files
      </div>
      {sortedGroups.map(([sha, shaFiles]) => (
        <div style="margin-bottom: 8px">
          <div style="padding: 4px 16px; color: #6b7280; font-size: 11px; font-family: monospace">{sha}</div>
          {shaFiles.map((file) => {
            const isSelected = file === selectedFile
            // Parse ISO timestamp back from filename: sha_2026-03-10T02-56-59-938Z.jsonl
            const isoStr = file.replace(/^[^_]+_/, "").replace(".jsonl", "").replace(/T(\d{2})-(\d{2})-(\d{2})-(\d+)Z/, "T$1:$2:$3.$4Z")
            const bg = isSelected ? "#2a2a2a" : "transparent"
            return (
              <a
                href={`/logs?file=${file}`}
                data-ts={isoStr}
                style={`display: block; padding: 6px 16px 6px 24px; color: ${isSelected ? "#f3f4f6" : "#9ca3af"}; text-decoration: none; font-size: 12px; background: ${bg}; font-family: monospace`}
              >
                {isoStr}
              </a>
            )
          })}
        </div>
      ))}
    </nav>
  )
}

export function LogsPage(req: Request) {
  const url = new URL(req.url)
  const selectedFile = url.searchParams.get("file") || undefined
  const typeFilter = url.searchParams.get("type") || undefined
  const repoFilter = url.searchParams.get("repo") || undefined

  const files = listLogFiles()

  let events: StoredLogEvent[] = []
  if (selectedFile) {
    events = readLogFile(selectedFile)
    if (typeFilter) events = events.filter((e) => e.type === typeFilter)
    if (repoFilter) events = events.filter((e) => "repo" in e && e.repo === repoFilter)
  }

  // Collect unique types and repos for filter links
  const allEvents = selectedFile ? readLogFile(selectedFile) : []
  const types = [...new Set(allEvents.map((e) => e.type))]
  const repos = [...new Set(allEvents.filter((e): e is StoredLogEvent & { repo: string } => "repo" in e).map((e) => e.repo))]

  const html = (
    <html>
      <head>
        <title>Spike Logs</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
      </head>
      <body style="margin: 0; background: #0a0a0a; color: #f3f4f6; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; display: flex; height: 100vh">
        <Sidebar files={files} selectedFile={selectedFile} />

        <main style="flex: 1; overflow-y: auto; padding: 24px">
          {!selectedFile ? (
            <div style="color: #6b7280; padding: 40px; text-align: center; font-size: 14px">
              Select a log file from the sidebar
            </div>
          ) : (
            <div>
              {/* Filters */}
              {(types.length > 1 || repos.length > 0) && (
                <div style="margin-bottom: 16px; display: flex; gap: 8px; flex-wrap: wrap; align-items: center">
                  <a
                    href={`/logs?file=${selectedFile}`}
                    style={`padding: 4px 10px; border-radius: 4px; font-size: 12px; text-decoration: none; ${!typeFilter && !repoFilter ? "background: #3b82f6; color: white" : "background: #1f1f1f; color: #9ca3af; border: 1px solid #2a2a2a"}`}
                  >
                    all
                  </a>
                  {types.map((t) => (
                    <a
                      href={`/logs?file=${selectedFile}&type=${t}${repoFilter ? `&repo=${repoFilter}` : ""}`}
                      style={`padding: 4px 10px; border-radius: 4px; font-size: 12px; text-decoration: none; ${typeFilter === t ? "background: #3b82f6; color: white" : "background: #1f1f1f; color: #9ca3af; border: 1px solid #2a2a2a"}`}
                    >
                      {t}
                    </a>
                  ))}
                  {repos.length > 0 && (
                    <span style="color: #3a3a3a; margin: 0 4px">|</span>
                  )}
                  {repos.map((r) => (
                    <a
                      href={`/logs?file=${selectedFile}&repo=${r}${typeFilter ? `&type=${typeFilter}` : ""}`}
                      style={`padding: 4px 10px; border-radius: 4px; font-size: 12px; text-decoration: none; font-family: monospace; ${repoFilter === r ? "background: #3b82f6; color: white" : "background: #1f1f1f; color: #9ca3af; border: 1px solid #2a2a2a"}`}
                    >
                      {r}
                    </a>
                  ))}
                </div>
              )}

              {/* Events table */}
              <table style="width: 100%; border-collapse: collapse">
                <tbody>
                  {events.map((event, i) => (
                    <EventRow event={event} index={i} />
                  ))}
                </tbody>
              </table>

              {events.length === 0 && (
                <div style="color: #6b7280; padding: 40px; text-align: center; font-size: 14px">
                  No events{typeFilter ? ` of type "${typeFilter}"` : ""}{repoFilter ? ` for ${repoFilter}` : ""}
                </div>
              )}
            </div>
          )}
        </main>
        <script dangerouslySetInnerHTML={{__html: `
          document.querySelectorAll('[data-ts]').forEach(function(el) {
            var d = new Date(el.dataset.ts);
            if (isNaN(d)) return;
            var pad = function(n) { return n < 10 ? '0' + n : n; };
            el.textContent = d.getFullYear() + '-' + pad(d.getMonth()+1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds());
          });
        `}} />
      </body>
    </html>
  )

  return new Response(html.toString(), { headers: { "Content-Type": "text/html" } })
}
