import { NextResponse } from 'next/server';
import { withErrorHandler } from '@server/middleware/withErrorHandler';
import { withAuth, requireSection, AuthedRequest } from '@server/middleware/withAuth';
import { createKbEntry } from '@server/services/knowledge-base.service';
import { db, restaurantApiKeys } from '@server/db/drizzle';
import { eq, and } from 'drizzle-orm';
import { requirePlanFeature } from '@server/utils/features';

const MAX_FILE_SIZE = 20 * 1024 * 1024;
const ALLOWED_EXTENSIONS = ['.pdf', '.txt', '.jpg', '.jpeg', '.png'];
const IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png'];

function formatFileSize(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}

async function extractTextFromPdf(buffer: Buffer): Promise<{ text: string; pages: number }> {
  const pdfParse = (await import('pdf-parse')).default;
  const data = await pdfParse(buffer);
  return { text: data.text, pages: data.numpages };
}

async function getOpenAIApiKey(restaurantId: string): Promise<string> {
  try {
    const rows = await db.select({ encryptedApiKey: restaurantApiKeys.encryptedApiKey })
      .from(restaurantApiKeys)
      .where(and(
        eq(restaurantApiKeys.restaurantId, restaurantId),
        eq(restaurantApiKeys.providerName, 'openai'),
        eq(restaurantApiKeys.isActive, true),
      ))
      .limit(1);
    const row = rows[0];
    if (row?.encryptedApiKey) {
      const key = row.encryptedApiKey;
      if (key.startsWith('sk-')) return key;
      const { decrypt } = await import('@server/utils/crypto');
      return decrypt(key);
    }
  } catch {
  }
  const envKey = process.env.OPENAI_API_KEY;
  if (!envKey) throw new Error('OpenAI API key not configured');
  return envKey;
}

async function extractTextFromImage(buffer: Buffer, mimeType: string, restaurantId: string): Promise<string> {
  const apiKey = await getOpenAIApiKey(restaurantId);
  const base64 = buffer.toString('base64');
  const dataUrl = `data:${mimeType};base64,${base64}`;

  const response = await fetch('https://api.openai.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${apiKey}`,
    },
    body: JSON.stringify({
      model: 'gpt-4o',
      messages: [
        {
          role: 'user',
          content: [
            {
              type: 'text',
              text: 'Extract all text content from this image. If this is a restaurant menu, list all items with their names, descriptions, and prices. If it contains other text, extract it faithfully. Return only the extracted text content, no commentary.',
            },
            {
              type: 'image_url',
              image_url: { url: dataUrl },
            },
          ],
        },
      ],
      max_tokens: 4096,
    }),
  });

  if (!response.ok) {
    const err = await response.text();
    throw new Error(`OpenAI Vision API error: ${response.status} ${err}`);
  }

  const data = await response.json();
  return data.choices?.[0]?.message?.content || '';
}

export const POST = withErrorHandler(
  withAuth(async (req: AuthedRequest) => {
    const { restaurantId, branchId } = req.session;
    await requireSection(req, 'knowledge_base', 'create');
    await requirePlanFeature(restaurantId!, 'knowledge_base');

    const formData = await req.formData();
    const file = formData.get('file') as File | null;
    const branchIdOverride = formData.get('branch_id') as string | null;

    if (!file || !(file instanceof File)) {
      return NextResponse.json({ error: 'No file provided' }, { status: 400 });
    }

    const ext = '.' + (file.name.split('.').pop()?.toLowerCase() || '');
    if (!ALLOWED_EXTENSIONS.includes(ext)) {
      return NextResponse.json(
        { error: `Unsupported file type. Allowed: ${ALLOWED_EXTENSIONS.join(', ')}` },
        { status: 400 }
      );
    }

    if (file.size > MAX_FILE_SIZE) {
      return NextResponse.json(
        { error: `File too large. Maximum size is ${formatFileSize(MAX_FILE_SIZE)}` },
        { status: 400 }
      );
    }

    const arrayBuffer = await file.arrayBuffer();
    const buffer = Buffer.from(arrayBuffer);

    let content: string;
    let pages: number | undefined;

    if (ext === '.pdf' || file.type === 'application/pdf') {
      const result = await extractTextFromPdf(buffer);
      content = result.text;
      pages = result.pages;
      if (!content.trim()) {
        return NextResponse.json(
          { error: 'Could not extract text from PDF. The file may be scanned or image-based.' },
          { status: 400 }
        );
      }
    } else if (IMAGE_EXTENSIONS.includes(ext)) {
      const mimeType = ext === '.png' ? 'image/png' : 'image/jpeg';
      content = await extractTextFromImage(buffer, mimeType, restaurantId!);
      if (!content.trim()) {
        return NextResponse.json(
          { error: 'Could not extract text from image. The image may not contain readable text.' },
          { status: 400 }
        );
      }
    } else {
      content = buffer.toString('utf-8');
      if (!content.trim()) {
        return NextResponse.json({ error: 'File is empty' }, { status: 400 });
      }
    }

    const entry = await createKbEntry(restaurantId!, {
      branch_id: branchIdOverride || branchId || null,
      type: 'doc',
      title: file.name,
      content,
      file_size: formatFileSize(file.size),
      file_type: ext.replace('.', '').toUpperCase(),
      pages: pages ?? null,
      status: 'active',
      is_active: true,
    });

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