import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { createAdminClient } from '@/lib/supabase/admin'
import { requireParticipant } from '@/lib/api-guard'
import { askTripAi, isFlaskConfigured } from '@/lib/adapters/flask'
import { loadVisibleChat } from '@/lib/data/chat'
import { openReviewTicket } from '@/lib/data/moderation'
import { questionSimilarity, REPEAT_SIMILARITY } from '@/lib/domain/moderation'

const schema = z.object({
  question: z.string().trim().min(1).max(2000),
  state_code: z.string().trim().max(2).optional().or(z.literal('')),
  // permit-scoped question ("ask about THIS permit" — disambiguates several
  // permits for the same state)
  permit_id: z.string().uuid().optional(),
  language: z.string().trim().max(20).optional(),
  // false = plain room message between participants; true = also ask the AI
  ask_ai: z.boolean().optional().default(true),
})

/**
 * Poll the shared trip conversation. `?after=<ISO timestamp>` returns only
 * messages newer than that moment, so clients can poll cheaply.
 */
export async function GET(req: NextRequest, ctx: { params: Promise<{ id: string }> }) {
  const { id: tripId } = await ctx.params
  const guard = await requireParticipant(tripId)
  if (!guard.ok) return guard.response

  const after = req.nextUrl.searchParams.get('after')
  // Another participant's private questions never leave the server.
  const { messages, failed } = await loadVisibleChat(tripId, guard.user.id, {
    after,
    limit: 200,
  })
  if (failed) return NextResponse.json({ error: 'Could not load messages.' }, { status: 500 })
  return NextResponse.json({ messages })
}

/**
 * Shared trip chat room. Persists the participant's message; when ask_ai is
 * set, assembles THIS trip's context from Supabase (one trip, one context),
 * asks the existing Flask AI, and persists the answer so every participant
 * sees the same conversation.
 */
export async function POST(req: NextRequest, ctx: { params: Promise<{ id: string }> }) {
  const { id: tripId } = await ctx.params
  const guard = await requireParticipant(tripId)
  if (!guard.ok) return guard.response
  const { user, participant } = guard

  const parsed = schema.safeParse(await req.json().catch(() => ({})))
  if (!parsed.success) return NextResponse.json({ error: 'Invalid message.' }, { status: 400 })
  const { question, state_code, permit_id, language, ask_ai } = parsed.data

  const admin = createAdminClient()
  const label = participant.name || user.email || 'participant'

  // The asker's per-trip sharing choice. `share_chat` is undefined before
  // migration 0010, which reads as "sharing" — the behaviour that has always
  // applied. The AI answer inherits the same flag: Nash asked for "no others
  // will see the questions AND answers".
  const isPrivate = participant.share_chat === false
  const privacy = isPrivate ? { is_private: true, private_for_user_id: user.id } : {}

  const { data: userMessage, error: insertError } = await admin
    .from('chat_messages')
    .insert({
      trip_id: tripId,
      user_id: user.id,
      author_label: label,
      author_role: participant.role,
      is_ai: false,
      content: question,
      state_code: state_code || null,
      permit_id: permit_id || null,
      ...privacy,
    })
    .select()
    .single()
  if (insertError) {
    return NextResponse.json({ error: 'Could not save the message.' }, { status: 500 })
  }

  if (!ask_ai) {
    return NextResponse.json({ ok: true, messages: [userMessage] })
  }

  let answer: string
  let confidence: string | null = null
  let sources: string[] | null = null

  if (!isFlaskConfigured()) {
    answer =
      'The AI service is not connected yet (FLASK_API_BASE_URL is not configured). Your question was saved and is visible to all trip participants; answers will work once the backend connection is set up.'
  } else {
    const { data: trip } = await admin.from('trips').select('*').eq('id', tripId).single()
    const { data: permits } = await admin
      .from('permits')
      .select('state_code, permit_number, effective_date, expiration_date, extraction')
      .eq('trip_id', tripId)

    const result = await askTripAi({
      question,
      state_code: state_code || null,
      language,
      trip: {
        ref_code: trip?.ref_code ?? '',
        origin: trip?.origin ?? '',
        destination: trip?.destination ?? '',
        commodity: trip?.commodity ?? '',
        load_dims: {
          length_in: trip?.load_length_in ?? null,
          width_in: trip?.load_width_in ?? null,
          height_in: trip?.load_height_in ?? null,
          weight_lbs: trip?.load_weight_lbs ?? null,
        },
      },
      permits: permits ?? [],
    })

    if (result.ok && result.data) {
      answer = result.data.answer
      confidence = result.data.confidence ?? null
      sources = result.data.sources ?? null
    } else {
      answer = `The AI service could not be reached (${result.error}). Your question was saved — try again in a moment.`
    }
  }

  const { data: aiMessage } = await admin
    .from('chat_messages')
    .insert({
      trip_id: tripId,
      user_id: null,
      author_label: 'HeavyHaul Agent',
      author_role: 'ai',
      is_ai: true,
      content: answer,
      state_code: state_code || null,
      permit_id: permit_id || null,
      confidence,
      sources,
      // Explicit link back to the question, so "hide my earlier questions and
      // answers" can never pick up another participant's answer.
      answer_to_message_id: userMessage.id,
      ...privacy,
    })
    .select()
    .single()

  // AI-side review triggers (moderator dashboard §5) — only for real answers
  // from the connected AI, never for the "not connected" placeholder:
  //  - the AI could not cite a strong source;
  //  - low confidence AND this user is asking the same question again.
  if (aiMessage && isFlaskConfigured()) {
    const noSource = !sources || sources.length === 0
    let repeated = false
    if (confidence === 'low') {
      const { data: earlier } = await admin
        .from('chat_messages')
        .select('content')
        .eq('trip_id', tripId)
        .eq('user_id', user.id)
        .eq('is_ai', false)
        .neq('id', userMessage.id)
        .gte('created_at', new Date(Date.now() - 24 * 3_600_000).toISOString())
        .order('created_at', { ascending: false })
        .limit(10)
      repeated = (earlier ?? []).some(
        (m) => questionSimilarity(m.content, question) >= REPEAT_SIMILARITY,
      )
    }
    if (repeated || noSource) {
      await openReviewTicket({
        tripId,
        answer: aiMessage,
        trigger: repeated ? 'low_confidence_repeat' : 'no_source',
        userId: user.id,
        userRole: participant.role,
        actorLabel: 'system',
      })
    }
  }

  return NextResponse.json({ ok: true, messages: aiMessage ? [userMessage, aiMessage] : [userMessage] })
}
