import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { jwtVerify } from 'jose'

const SESSION_SECRET = new TextEncoder().encode(
  process.env.SESSION_SECRET || 'reminder-fallback-secret-change-me-in-production',
)

const PUBLIC_PATHS = ['/login']
const TOKEN_AUTH_PATHS = [
  '/api/sync',
  '/api/organize',
  '/api/trades-ingest',
  '/api/trades-health',
  '/api/monitor/ingest',
  // The mail-digest timer files the morning summary here. Agent-token auth, same
  // as the monitor ingest beside it. /api/digest/read is deliberately NOT listed:
  // marking something read is a logged-in human's action and stays session-gated.
  '/api/digest/ingest',
  // The mini polls for questions Eric asked of a digest and reports back what it
  // did. Agent token, like the ingests above. /api/digest/reply (asking) and
  // /api/digest/read stay session-gated: those are the human's side.
  '/api/digest/pending',
  '/api/digest/reply/result',
  '/api/hosts-health',
  // A Mac-side watcher POSTs a one-off push here (door left open, etc.). Agent
  // token, same as the ingests above; the route does its own checkAgentAuth.
  '/api/notify',
]
const BYPASS_AUTH = process.env.BYPASS_AUTH === 'true'

export async function proxy(request: NextRequest) {
  const { pathname } = request.nextUrl

  if (BYPASS_AUTH) {
    return NextResponse.next()
  }

  if (PUBLIC_PATHS.some(p => pathname === p || pathname.startsWith(p + '/'))) {
    return NextResponse.next()
  }

  // These endpoints do their own auth (bearer token and/or session), so don't
  // gate them on the session cookie here. Note this is the *write* half of the
  // monitor only: /api/monitor itself stays session-gated, since the dashboard
  // read is for a logged-in human while the ingest is for a host agent that has
  // no cookie to present.
  // /api/ingest/* is the personal-capture-token surface (iOS Shortcut, watch,
  // bots). Matched as a PREFIX, not an exact path, because the status route
  // carries a capture id — exact matching would 307 it to /login. Each route
  // there resolves its own owner from the token and scopes to that account.
  if (
    TOKEN_AUTH_PATHS.includes(pathname) ||
    pathname.startsWith('/api/agent') ||
    // The spoken question's audio, fetched by the mini to transcribe it. A
    // PREFIX because the path carries a reply id, and deliberately narrower
    // than '/api/digest/reply' — ASKING must stay session-gated, so only the
    // /audio/ child is opened to the agent token, never the parent.
    pathname.startsWith('/api/digest/reply/audio/') ||
    pathname.startsWith('/api/ingest/')
  ) {
    return NextResponse.next()
  }

  if (
    pathname.startsWith('/_next') ||
    pathname.startsWith('/favicon') ||
    pathname.startsWith('/manifest') ||
    pathname.startsWith('/icons') ||
    pathname === '/sw.js' ||
    pathname.includes('.')
  ) {
    return NextResponse.next()
  }

  const token = request.cookies.get('rem_session')?.value
  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  try {
    await jwtVerify(token, SESSION_SECRET)
    return NextResponse.next()
  } catch {
    const response = NextResponse.redirect(new URL('/login', request.url))
    response.cookies.delete('rem_session')
    return response
  }
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
}
