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'
import { buildAgentHistory } from '@/lib/domain/chat-history'
import { syncTripToHha } from '@/lib/integrations/hha-trip-sync'
import { canSeePermitCost, mentionsMoney, redactCostsForPilot } from '@/lib/domain/permit-cost'
import { logTripAiEvent, tripAiRequestId } from '@/lib/observability/trip-ai-log'

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.enum(['en', 'es', 'ru', 'ro']).optional().default('en'),
  // 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 requestId = tripAiRequestId(req)
  const startedAt = Date.now()
  const { id: tripId } = await ctx.params
  await logTripAiEvent('chat_history_received', { request_id: requestId, trip_id: tripId })
  const guard = await requireParticipant(tripId)
  if (!guard.ok) {
    await logTripAiEvent('chat_history_denied', {
      request_id: requestId,
      trip_id: tripId,
      status: 'denied',
      http_status: guard.response.status,
    })
    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) {
    await logTripAiEvent('chat_history_failed', {
      request_id: requestId,
      trip_id: tripId,
      status: 'error',
    })
    return NextResponse.json({ error: 'Could not load messages.' }, { status: 500 })
  }
  await logTripAiEvent('chat_history_completed', {
    request_id: requestId,
    trip_id: tripId,
    message_count: messages.length,
    duration_ms: Date.now() - startedAt,
  })
  return NextResponse.json({ messages })
}

/**
 * Shared trip chat room. Persists the participant's message; when ask_ai is
 * set, synchronizes THIS trip to GPT, asks the existing Flask AI using the
 * immutable Workspace UUID, and persists the answer so permitted participants
 * see the same conversation.
 */
export async function POST(req: NextRequest, ctx: { params: Promise<{ id: string }> }) {
  const requestId = tripAiRequestId(req)
  const startedAt = Date.now()
  const { id: tripId } = await ctx.params
  await logTripAiEvent('text_chat_received', { request_id: requestId, trip_id: tripId })
  const guard = await requireParticipant(tripId)
  if (!guard.ok) {
    await logTripAiEvent('text_chat_denied', {
      request_id: requestId,
      trip_id: tripId,
      status: 'denied',
      http_status: guard.response.status,
    })
    return guard.response
  }
  const { user, participant } = guard

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

  const admin = createAdminClient()
  const label = participant.name || user.email || 'participant'
  const priorChat = await loadVisibleChat(tripId, user.id, { limit: 200 })
  const history = priorChat.failed ? [] : buildAgentHistory(priorChat.messages)

  // 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
  await logTripAiEvent('text_chat_validated', {
    request_id: requestId,
    trip_id: tripId,
    user_id: user.id,
    language,
    ask_ai,
    is_private: isPrivate,
    question_chars: question.length,
    history_turns: history.length,
    history_load_failed: priorChat.failed,
  })
  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: null,
      permit_id: null,
      ...privacy,
    })
    .select()
    .single()
  if (insertError) {
    await logTripAiEvent('text_user_message_save_failed', {
      request_id: requestId,
      trip_id: tripId,
      status: 'error',
      error_code: insertError.code,
    })
    return NextResponse.json({ error: 'Could not save the message.' }, { status: 500 })
  }
  await logTripAiEvent('text_user_message_saved', {
    request_id: requestId,
    trip_id: tripId,
    message_id: userMessage.id,
  })

  if (!ask_ai) {
    await logTripAiEvent('text_chat_completed', {
      request_id: requestId,
      trip_id: tripId,
      agent_called: false,
      duration_ms: Date.now() - startedAt,
    })
    return NextResponse.json({ ok: true, messages: [userMessage] })
  }

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

  if (!isFlaskConfigured()) {
    await logTripAiEvent('gpt_not_configured', {
      request_id: requestId,
      trip_id: tripId,
      status: 'error',
    })
    answer =
      'The AI service is not connected yet (HHA_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 sync = await syncTripToHha(tripId, requestId)
    await logTripAiEvent('pre_chat_sync_completed', {
      request_id: requestId,
      trip_id: tripId,
      status: sync.ok ? 'ok' : 'error',
      matched_all_orders: sync.matchedAllOrders,
      error_type: sync.ok ? undefined : sync.error,
    })

    const result = await askTripAi({
      question,
      workspace_trip_id: tripId,
      history,
      language,
      request_id: requestId,
      viewer_role: participant.role,
      permit_cost_visible: canSeePermitCost(participant.role),
    })

    if (result.ok && result.data) {
      answer = result.data.answer
      confidence = result.data.confidence ?? null
      sources = result.data.sources ?? null
      await logTripAiEvent('gpt_answer_received', {
        request_id: requestId,
        trip_id: tripId,
        confidence,
        source_count: sources?.length ?? 0,
        answer_chars: answer.length,
      })
    } else {
      await logTripAiEvent('gpt_answer_failed', {
        request_id: requestId,
        trip_id: tripId,
        status: 'error',
        error_type: result.error,
      })
      answer = `The AI service could not be reached (${result.error}). Your question was saved — try again in a moment.`
    }
  }

  // §14: a pilot never receives a permit fee. The backend is told not to send
  // one; this is the guarantee that it cannot reach them if it does anyway.
  if (!canSeePermitCost(participant.role) && mentionsMoney(answer)) {
    await logTripAiEvent('gpt_answer_received', {
      request_id: requestId,
      trip_id: tripId,
      status: 'redacted',
      error_type: 'permit_cost_redacted_for_pilot',
    })
    answer = redactCostsForPilot(answer)
  }

  const { data: aiMessage, error: aiInsertError } = 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: null,
      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()
  await logTripAiEvent(aiInsertError ? 'text_ai_message_save_failed' : 'text_ai_message_saved', {
    request_id: requestId,
    trip_id: tripId,
    status: aiInsertError ? 'error' : 'ok',
    message_id: aiMessage?.id,
    error_code: aiInsertError?.code,
  })

  // 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',
      })
      await logTripAiEvent('review_ticket_requested', {
        request_id: requestId,
        trip_id: tripId,
        trigger: repeated ? 'low_confidence_repeat' : 'no_source',
      })
    }
  }

  await logTripAiEvent('text_chat_completed', {
    request_id: requestId,
    trip_id: tripId,
    ai_message_saved: Boolean(aiMessage),
    duration_ms: Date.now() - startedAt,
  })
  return NextResponse.json({ ok: true, messages: aiMessage ? [userMessage, aiMessage] : [userMessage] })
}
