import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';
import { getCurrentUser } from '@/lib/auth';
import { prisma } from '@/lib/prisma';
import {
  PROGRAM_OUTPUT_SHAPE,
} from '@/lib/ai/programSchema';
import {
  buildHistorySummary,
  formatHistoryContext,
} from '@/lib/ai/historyContext';
import { buildBaseSystemPrompt } from '@/lib/ai/systemPromptBase';
import { kickoffGeneration } from '@/lib/ai/generationRunner';

/**
 * POST /api/ai/generate
 *
 * Body: { templateId?: string, userInput: string, includeHistory?: boolean }
 *
 * v1.1.0:4: this endpoint now KICKS OFF a background runner and returns
 * the new generation id immediately. The caller subscribes to live
 * deltas via GET /api/ai/generations/[id]/stream (SSE) or polls via
 * GET /api/ai/generations/[id]. Navigating away no longer cancels the
 * generation — the runner keeps writing to the row in the background.
 *
 * Response:
 *   201 { id: "...generationId..." }
 *   400 { error: "..." }
 */

const bodySchema = z.object({
  templateId: z.string().optional().nullable(),
  userInput: z.string().min(1),
  includeHistory: z.boolean().optional().default(false),
});

export const dynamic = 'force-dynamic';

export async function POST(request: NextRequest) {
  const user = await getCurrentUser();
  if (!user) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }

  const body = await request.json().catch(() => ({}));
  const parsed = bodySchema.safeParse(body);
  if (!parsed.success) {
    return NextResponse.json(
      { error: 'Invalid body', details: parsed.error.errors },
      { status: 400 },
    );
  }

  const prefs = await prisma.userPreferences.findUnique({
    where: { userId: user.id },
  });
  if (!prefs?.aiProvider || !prefs?.aiModel) {
    return NextResponse.json(
      {
        error:
          'AI is not configured. Open Settings → AI integration and pick a provider + model.',
      },
      { status: 400 },
    );
  }

  // Load the template if provided.
  let template:
    | { id: string; name: string; systemPrompt: string; userPromptTemplate: string }
    | null = null;
  if (parsed.data.templateId) {
    const t = await prisma.aIPromptTemplate.findFirst({
      where: {
        id: parsed.data.templateId,
        OR: [{ userId: user.id }, { userId: null }],
      },
      select: {
        id: true,
        name: true,
        systemPrompt: true,
        userPromptTemplate: true,
      },
    });
    if (!t) {
      return NextResponse.json({ error: 'Template not found.' }, { status: 404 });
    }
    template = t;
  }

  // Library for the prompt.
  const exercises = await prisma.exercise.findMany({
    where: { userId: user.id },
    select: { id: true, name: true, type: true, muscleGroups: true },
  });
  const libraryJson = JSON.stringify(
    exercises.map((e) => ({
      id: e.id,
      name: e.name,
      type: e.type,
      muscleGroups: (() => {
        try {
          return JSON.parse(e.muscleGroups);
        } catch {
          return [];
        }
      })(),
    })),
  );

  // History context if requested.
  let historyBlock = '';
  if (parsed.data.includeHistory) {
    const summary = await buildHistorySummary(prisma, user.id);
    historyBlock = formatHistoryContext(summary);
  }

  // v1.1.0:4 base prompt with output contract + weight rules. Stitched
  // BEFORE the template's coaching philosophy so output rules win when
  // they conflict.
  const weightUnit = (prefs.defaultWeightUnit as 'lbs' | 'kg') || 'lbs';
  const isLocalModel = prefs.aiProvider === 'ollama';
  const basePrompt = buildBaseSystemPrompt({
    weightUnit,
    hasHistoryContext: parsed.data.includeHistory,
    isLocalModel,
  });
  const templatePrompt = template?.systemPrompt ?? DEFAULT_TEMPLATE_PROMPT;

  const systemPrompt = `${basePrompt}

# COACHING PHILOSOPHY (template-specific)

${templatePrompt}

# OUTPUT SHAPE

${PROGRAM_OUTPUT_SHAPE}

# LIBRARY (use these exerciseIds; do not invent ids)

${libraryJson}${historyBlock}`;

  const userPromptBody =
    template?.userPromptTemplate.replace(/{{userInput}}/g, parsed.data.userInput) ??
    parsed.data.userInput;

  const id = await kickoffGeneration({
    prisma,
    userId: user.id,
    templateId: template?.id ?? null,
    templateName: template?.name ?? null,
    userInput: parsed.data.userInput,
    systemPrompt,
    userPrompt: userPromptBody,
    provider: prefs.aiProvider,
    model: prefs.aiModel,
    apiKey: prefs.aiApiKey,
    baseUrl: prefs.aiBaseUrl,
  });

  return NextResponse.json({ id }, { status: 201 });
}

const DEFAULT_TEMPLATE_PROMPT = `You are a strength and conditioning coach. The user will describe what they want; design a program that matches their goal, experience, equipment, and time budget. Pick exercises from the LIBRARY and stay close to evidence-based programming for the requested goal (hypertrophy / strength / power / conditioning / general fitness).`;
