import { NextResponse } from 'next/server';
import OpenAI from 'openai';
import { withErrorHandler } from '@server/middleware/withErrorHandler';
import { withAuth, AuthedRequest } from '@server/middleware/withAuth';
import { withRateLimit, getClientIp } from '@server/middleware/withRateLimit';

import { childLogger } from '@server/logger';
const log = childLogger('route.ai.chat_completion');

const API_KEYS: Record<string, string | undefined> = {
  OPEN_AI: process.env.OPENAI_API_KEY,
};

type CompletionParams = {
  model: string;
  messages: OpenAI.Chat.ChatCompletionMessageParam[];
  stream: boolean;
  api_key: string;
  [key: string]: unknown;
};

async function completion({ model, messages, stream, api_key, ...parameters }: CompletionParams) {
  const openai = new OpenAI({ apiKey: api_key });
  return openai.chat.completions.create({
    model,
    messages,
    stream,
    ...parameters,
  } as Parameters<typeof openai.chat.completions.create>[0]);
}

interface LlmProviderError {
  statusCode?: number;
  status?: number;
  llmProvider?: string;
}

function formatErrorResponse(error: unknown, provider?: string) {
  const err = (error ?? {}) as LlmProviderError;
  const statusCode = err.statusCode || err.status || 500;
  const providerName = err.llmProvider || provider || 'Unknown';
  return {
    error: `${providerName.toUpperCase()} API error: ${statusCode}`,
    details: error instanceof Error ? error.message : String(error),
    statusCode,
  };
}

const handler = async (request: Request) => {
  let body: any = {};

  try {
    body = await request.json();
    const { provider, model, messages, stream = false, parameters = {} } = body;

    if (!provider || !model || !messages?.length) {
      return NextResponse.json(
        { error: 'Missing required fields: provider, model, messages', details: 'Request validation failed' },
        { status: 400 }
      );
    }

    const apiKey = API_KEYS[provider];
    if (!apiKey) {
      return NextResponse.json(
        { error: `${provider.toUpperCase()} API key is not configured`, details: 'The API key for this provider is missing in environment variables' },
        { status: 400 }
      );
    }

    if (stream) {
      const response = await completion({ model, messages, stream: true, api_key: apiKey, ...parameters });
      const encoder = new TextEncoder();
      const readable = new ReadableStream({
        async start(controller) {
          try {
            controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: 'start' })}\n\n`));
            for await (const chunk of response as unknown as AsyncIterable<unknown>) {
              controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: 'chunk', chunk })}\n\n`));
            }
            controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: 'done' })}\n\n`));
            controller.close();
          } catch (error) {
            const formatted = formatErrorResponse(error, provider);
            log.error({ error: formatted.error, details: formatted.details }, 'API route error');
            controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: 'error', error: formatted.error, details: formatted.details })}\n\n`));
            controller.close();
          }
        },
      });

      return new NextResponse(readable, {
        headers: {
          'Content-Type': 'text/event-stream',
          'Cache-Control': 'no-cache',
          Connection: 'keep-alive',
        },
      });
    }

    const response = await completion({ model, messages, stream: false, api_key: apiKey, ...parameters });
    return NextResponse.json(response);
  } catch (error) {
    const formatted = formatErrorResponse(error, body?.provider);
    log.error({ error: formatted.error, details: formatted.details }, 'API route error');
    return NextResponse.json(
      { error: formatted.error, details: formatted.details },
      { status: formatted.statusCode }
    );
  }
};

export const POST = withErrorHandler(
  withAuth(
    withRateLimit(
      [
        {
          scope: 'ai-chat-completion:user-min',
          limit: 30,
          windowMs: 60_000,
          keyOf: (req) => (req as AuthedRequest).session?.userId,
        },
        {
          scope: 'ai-chat-completion:user-day',
          limit: 500,
          windowMs: 24 * 60 * 60 * 1000,
          keyOf: (req) => (req as AuthedRequest).session?.userId,
        },
        {
          scope: 'ai-chat-completion:ip-min',
          limit: 60,
          windowMs: 60_000,
          keyOf: (req) => getClientIp(req),
        },
      ],
      handler
    )
  )
);
