import { NextResponse } from 'next/server'
import { db } from '@/lib/db'
import { visible } from '@/lib/scope'
import { appleOwnerId } from '@/lib/owner'
import { checkAgentAuth } from '@/lib/agent-auth'

export const runtime = 'nodejs'

const BASE = process.env.NEXT_PUBLIC_APP_URL || 'https://steward.etpics.com'

// Fetch the chores app's kid roster (names only) so the router can resolve
// spoken names/nicknames. Returns [] on any failure — never throws.
async function fetchChoresKids(): Promise<string[]> {
  const url = process.env.CHORES_MEMBERS_API_URL
  const secret = process.env.CHORES_API_SECRET
  if (!url || !secret) return []
  try {
    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${secret}` },
      signal: AbortSignal.timeout(3000),
    })
    if (!res.ok) return []
    const data = await res.json()
    const members = Array.isArray(data?.members) ? data.members : []
    return members.map((m: { name?: string }) => m?.name).filter((n: unknown): n is string => typeof n === 'string' && !!n.trim())
  } catch {
    return []
  }
}

// Fetch the chores app's CHORE tag names so the router can tag a new chore
// with an existing category. Returns [] on any failure — never throws.
async function fetchChoresTags(): Promise<string[]> {
  const url = process.env.CHORES_TAGS_API_URL
  const secret = process.env.CHORES_API_SECRET
  if (!url || !secret) return []
  try {
    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${secret}` },
      signal: AbortSignal.timeout(3000),
    })
    if (!res.ok) return []
    const data = await res.json()
    const tags = Array.isArray(data?.tags) ? data.tags : []
    return tags.map((t: { name?: string }) => t?.name).filter((n: unknown): n is string => typeof n === 'string' && !!n.trim())
  } catch {
    return []
  }
}

// Fetch the FlockBase/quailmanager tenant's expense-category names so the router
// can file a farm expense under an EXISTING category. Returns [] on any failure.
async function fetchExpenseCategories(): Promise<string[]> {
  const url = process.env.QUAIL_EXPENSE_CATEGORIES_API_URL
  const secret = process.env.QUAIL_EXPENSE_API_SECRET || process.env.QUAIL_SALES_API_SECRET
  if (!url || !secret) return []
  try {
    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${secret}` },
      signal: AbortSignal.timeout(3000),
    })
    if (!res.ok) return []
    const data = await res.json()
    const categories = Array.isArray(data?.categories) ? data.categories : []
    return categories.map((c: { name?: string }) => c?.name).filter((n: unknown): n is string => typeof n === 'string' && !!n.trim())
  } catch {
    return []
  }
}

// Fetch the supplications app's family roster so the router can attach a
// spoken name to a prayer. Returns [] on any failure — never throws.
//
// A SEPARATE call from the chores roster, not a reuse of it: the same children
// are "Sam Tran" and "Jeremiah Tran" to the chores app and "Samuel" and
// "Jeremiah" to the prayer app, and the parents are in one and not the other.
// Sending the wrong list would make every name miss.
async function fetchPrayerMembers(): Promise<string[]> {
  const url = process.env.SUPPLICATIONS_MEMBERS_API_URL
  const secret = process.env.SUPPLICATIONS_API_SECRET
  if (!url || !secret) return []
  try {
    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${secret}` },
      signal: AbortSignal.timeout(3000),
    })
    if (!res.ok) return []
    const data = await res.json()
    const members = Array.isArray(data?.members) ? data.members : []
    return members.map((m: { name?: string }) => m?.name).filter((n: unknown): n is string => typeof n === 'string' && !!n.trim())
  } catch {
    return []
  }
}

// GET /api/agent/capture/:id — the Mac agent fetches the work item. Returns
// the capture plus absolute, bearer-authed media URLs it can curl.
export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
  if (!checkAgentAuth(req)) return NextResponse.json({ error: 'unauthorized' }, { status: 401 })

  const { id } = await params
  const capture = await db.capture.findUnique({
    where: { id },
    include: {
      images: { select: { id: true } },
      reminders: { select: { externalId: true } },
      events: { select: { externalId: true } },
      owner: { select: { capabilities: true } },
    },
  })
  if (!capture) return NextResponse.json({ error: 'not found' }, { status: 404 })

  // Claim it: PENDING/FAILED → PROCESSING so a re-trigger can't double-run.
  if (capture.status === 'PENDING' || capture.status === 'FAILED') {
    await db.capture.update({ where: { id }, data: { status: 'PROCESSING', error: null } })
  }

  // Apple ids of items previously created from this capture. On a re-process
  // the agent deletes these before creating fresh ones, so we never duplicate.
  const staleExternalIds = [
    ...capture.reminders.map(r => r.externalId),
    ...capture.events.map(e => e.externalId),
  ].filter((x): x is string => !!x)

  // Refine target: the agent synthesizes this capture into the thought below
  // instead of routing.
  const refineThought = capture.refineThoughtId
    ? await db.thought.findFirst({
        where: { id: capture.refineThoughtId, ...visible.thought(capture.ownerId) },
        select: { id: true, body: true, tags: true },
      })
    : null

  // Active special lists, so the router can match "add X to the camping list" —
  // scoped to what the person who recorded this can actually see, so one
  // account's private list names never reach another's routing prompt.
  const checklists = await db.checklist.findMany({
    where: { dismissed: false, ...visible.checklist(capture.ownerId) },
    select: { name: true },
  })

  // Kids' roster from the chores app, so the router can map a spoken name or
  // nickname ("Samuel", "Jerry") onto the exact registered kid before awarding
  // points / creating chores. Best-effort: a chores hiccup must not block routing.
  // EventKit runs under ONE iCloud account, so only that account's captures
  // may see — or act on — the Apple reminder list. Everyone else's prompt gets
  // nothing, which also stops the router offering to complete or reschedule an
  // item that is not theirs.
  const isAppleOwner = capture.ownerId === (await appleOwnerId())
  const caps = capture.owner.capabilities
  const canChores = caps.includes('chores') || caps.includes('points') || caps.includes('timers')
  const [choresKids, choresTags, expenseCategories, prayerMembers] = await Promise.all([
    canChores ? fetchChoresKids() : Promise.resolve([]),
    caps.includes('chores') ? fetchChoresTags() : Promise.resolve([]),
    caps.includes('expenses') ? fetchExpenseCategories() : Promise.resolve([]),
    caps.includes('prayers') ? fetchPrayerMembers() : Promise.resolve([]),
  ])

  return NextResponse.json({
    refineThought,
    capabilities: caps,
    isAppleOwner,
    checklistNames: checklists.map(c => c.name),
    choresKids,
    choresTags,
    expenseCategories,
    prayerMembers,
    captureId: capture.id,
    status: 'PROCESSING',
    textNote: capture.textNote,
    transcript: capture.transcript, // present on re-process (may be user-edited)
    audioUrl: capture.audioPath ? `${BASE}/api/agent/audio/${capture.id}` : null,
    imageUrls: capture.images.map(i => `${BASE}/api/agent/image/${i.id}`),
    staleExternalIds,
    createdAt: capture.createdAt,
  })
}
