import type { FastifyInstance } from 'fastify';
import { eq, desc } from 'drizzle-orm';
import { db } from '../db.js';
import { recommendations, feedback } from '../db/schema.js';
import { runPipeline } from '../pipelines/recommendation.js';
import type { MediaType, SSEEvent } from '../types/agents.js';

export default async function recommendationsRoute(fastify: FastifyInstance) {
  // POST /recommendations — create record, return { id }
  fastify.post('/recommendations', async (request, reply) => {
    const body = request.body as {
      main_prompt: string;
      liked_shows?: string;
      disliked_shows?: string;
      themes?: string;
      brainstorm_count?: number;
      media_type?: string;
      use_web_search?: boolean;
    };

    const title = (body.main_prompt ?? '')
      .trim()
      .split(/\s+/)
      .slice(0, 5)
      .join(' ');

    const rawCount = Number(body.brainstorm_count ?? 100);
    const brainstorm_count = Number.isFinite(rawCount) ? Math.min(200, Math.max(50, rawCount)) : 100;
    const media_type: MediaType = body.media_type === 'movie' ? 'movie' : 'tv_show';
    const use_web_search = body.use_web_search === true;

    const [rec] = await db
      .insert(recommendations)
      .values({
        title: title || 'Untitled',
        main_prompt: body.main_prompt ?? '',
        liked_shows: body.liked_shows ?? '',
        disliked_shows: body.disliked_shows ?? '',
        themes: body.themes ?? '',
        brainstorm_count,
        media_type,
        use_web_search,
        status: 'pending',
      })
      .returning({ id: recommendations.id });

    return reply.code(201).send({ id: rec?.id });
  });

  // GET /recommendations — list all
  fastify.get('/recommendations', async (_request, reply) => {
    const rows = await db
      .select({
        id: recommendations.id,
        title: recommendations.title,
        status: recommendations.status,
        media_type: recommendations.media_type,
        created_at: recommendations.created_at,
      })
      .from(recommendations)
      .orderBy(desc(recommendations.created_at));
    return reply.send(rows);
  });

  // GET /recommendations/:id — full record
  fastify.get('/recommendations/:id', async (request, reply) => {
    const { id } = request.params as { id: string };
    const [rec] = await db
      .select()
      .from(recommendations)
      .where(eq(recommendations.id, id));

    if (!rec) return reply.code(404).send({ error: 'Not found' });
    return reply.send(rec);
  });

  // GET /recommendations/:id/stream — SSE pipeline stream
  fastify.get('/recommendations/:id/stream', async (request, reply) => {
    const { id } = request.params as { id: string };
    const [rec] = await db
      .select()
      .from(recommendations)
      .where(eq(recommendations.id, id));

    if (!rec) return reply.code(404).send({ error: 'Not found' });

    // Load all feedback to potentially inject as context
    const feedbackRows = await db.select().from(feedback);
    const mediaLabel = rec.media_type === 'movie' ? 'Movie' : 'Show';
    const feedbackContext =
      feedbackRows.length > 0
        ? feedbackRows
            .map(
              (f) =>
                `${mediaLabel}: "${f.item_name}" — Rating: ${f.stars}/3 stars${f.feedback ? ` — Comment: ${f.feedback}` : ''}`,
            )
            .join('\n')
        : undefined;

    // Set SSE headers and hijack
    reply.raw.setHeader('Content-Type', 'text/event-stream');
    reply.raw.setHeader('Cache-Control', 'no-cache');
    reply.raw.setHeader('Connection', 'keep-alive');
    reply.raw.setHeader('Access-Control-Allow-Origin', '*');
    reply.raw.flushHeaders();
    reply.hijack();

    const sseWrite = (event: SSEEvent) => {
      reply.raw.write(`data: ${JSON.stringify(event)}\n\n`);
    };

    try {
      await runPipeline(rec, sseWrite, feedbackContext);
    } finally {
      reply.raw.end();
    }
  });

  // POST /recommendations/:id/rerank — reset status so client can re-open SSE stream
  fastify.post('/recommendations/:id/rerank', async (request, reply) => {
    const { id } = request.params as { id: string };
    const [rec] = await db
      .select({ id: recommendations.id })
      .from(recommendations)
      .where(eq(recommendations.id, id));

    if (!rec) return reply.code(404).send({ error: 'Not found' });

    await db
      .update(recommendations)
      .set({ status: 'pending' })
      .where(eq(recommendations.id, id));

    return reply.send({ ok: true });
  });
}
