import { NextRequest, NextResponse } from 'next/server';
import { getSession } from '@/lib/auth';
import { dbHelpers } from '@/lib/db';
import { fetchEngine } from '@/lib/engine-server';
import { SOURCE_EXTENSIONS, buildAnalysisResponse, analyzeSourceFile } from '@/lib/analyze-source';
import type { AnalyzedFile } from '@/lib/analyze-source';

interface UploadFileMeta {
  path: string;
  size: number;
  sha256: string;
}

interface UploadMetadata {
  id: string;
  tenantId: string;
  createdAt: string;
  files: UploadFileMeta[];
}

const BYTES_PER_LINE = 40;
const MAX_CONTENT_BYTES = 200 * 1024; // 200 KB per file — skip larger files
const MAX_FILES_TO_FETCH = 100;

function estimateRisk(codeLines: number): 'high' | 'medium' | 'low' {
  if (codeLines > 500) return 'high';
  if (codeLines > 200) return 'medium';
  return 'low';
}

function buildSyntheticFile(file: UploadFileMeta): AnalyzedFile {
  const lines = Math.max(1, Math.round(file.size / BYTES_PER_LINE));
  const blankLines = Math.round(lines * 0.1);
  const commentLines = Math.round(lines * 0.1);
  const codeLines = lines - blankLines - commentLines;
  return {
    path: file.path,
    name: file.path.split('/').pop() || file.path,
    size: file.size,
    lines,
    blankLines,
    commentLines,
    codeLines,
    complexity: 1,
    nestingDepth: 0,
    risk: estimateRisk(codeLines),
    dependencies: [],
    deps: [],
    externalCallCount: 0,
    procedures: 0,
    sections: 0,
  };
}

async function fetchFileContent(uploadId: string, filePath: string): Promise<string | null> {
  try {
    const res = await fetchEngine(`uploads/${uploadId}/file?path=${encodeURIComponent(filePath)}`);
    if (!res.ok) return null;
    return await res.text();
  } catch {
    return null;
  }
}

export async function POST(
  request: NextRequest,
  { params }: { params: Promise<{ id: string }> },
) {
  const session = await getSession(request.cookies.get('scriba.access_token')?.value);
  if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

  const { id: projectId } = await params;
  const project = await dbHelpers.getProject(projectId);
  if (!project || project.userId !== session.user.id) {
    return NextResponse.json({ error: 'Not found' }, { status: 404 });
  }

  const config = (project.config ?? {}) as Record<string, unknown>;
  const uploadId = config.uploadId as string | undefined;
  if (!uploadId) {
    return NextResponse.json({ error: 'No upload found for this project. Upload a folder first.' }, { status: 400 });
  }

  const sourceLanguage = (project.sourceLanguage ?? '').trim().toLowerCase();
  const additionalLangs: string[] = (config.additionalSourceLanguages as string[] | undefined) ?? [];

  const engRes = await fetchEngine(`uploads/${uploadId}`);
  if (!engRes.ok) {
    return NextResponse.json({ error: 'Failed to fetch upload metadata from engine' }, { status: 502 });
  }
  const meta: UploadMetadata = await engRes.json();

  const allExts = [
    ...(SOURCE_EXTENSIONS[sourceLanguage] ?? []),
    ...additionalLangs.flatMap(l => SOURCE_EXTENSIONS[l.toLowerCase()] ?? []),
  ];
  const sourceExts = [...new Set(allExts)];

  const allFiles = meta.files ?? [];
  const sourceFiles = sourceExts.length > 0
    ? allFiles.filter(f => sourceExts.some(ext => f.path.toLowerCase().endsWith(ext)))
    : allFiles;

  // Fetch content for source files that are small enough, up to MAX_FILES_TO_FETCH.
  // Files that are too large or fail to load fall back to synthetic estimates.
  const filesToFetch = sourceFiles
    .filter(f => f.size <= MAX_CONTENT_BYTES)
    .slice(0, MAX_FILES_TO_FETCH);
  const fetchableSet = new Set(filesToFetch.map(f => f.path));

  const contentResults = await Promise.allSettled(
    filesToFetch.map(f => fetchFileContent(uploadId, f.path)),
  );
  const contentByPath = new Map<string, string>();
  filesToFetch.forEach((f, i) => {
    const r = contentResults[i];
    if (r.status === 'fulfilled' && r.value !== null) {
      contentByPath.set(f.path, r.value);
    }
  });

  const analyzedFiles: AnalyzedFile[] = sourceFiles.map(f => {
    if (fetchableSet.has(f.path)) {
      const content = contentByPath.get(f.path);
      if (content !== undefined) {
        return analyzeSourceFile(f.path, content, sourceLanguage);
      }
    }
    return buildSyntheticFile(f);
  });

  const { lineMetrics, complexity, dependencies, risk } = buildAnalysisResponse(analyzedFiles, [], sourceFiles.length);

  const totalSize = allFiles.reduce((s, f) => s + f.size, 0);
  const extCounts: Record<string, number> = {};
  for (const f of allFiles) {
    const ext = f.path.includes('.') ? '.' + f.path.split('.').pop()!.toLowerCase() : '(none)';
    extCounts[ext] = (extCounts[ext] ?? 0) + 1;
  }
  const extensions = Object.entries(extCounts)
    .map(([ext, count]) => ({ ext, count, percentage: Math.round((count / allFiles.length) * 1000) / 10 }))
    .sort((a, b) => b.count - a.count);

  const langLabel = sourceLanguage.charAt(0).toUpperCase() + sourceLanguage.slice(1);
  const languages = [{ name: langLabel, bytes: totalSize, files: sourceFiles.length, percentage: 100 }];

  const result = {
    scanResults: {
      totalFiles: allFiles.length,
      totalSourceFiles: sourceFiles.length,
      totalDirectories: 0,
      totalSize,
      ...lineMetrics,
      languages,
      extensions,
    },
    complexity,
    dependencies,
    risk,
    analyzedFileCount: analyzedFiles.length,
  };

  // Persist to project config
  try {
    await dbHelpers.updateProjectProgress(projectId, {
      config: { ...config, preAnalysis: result },
      totalFiles: sourceFiles.length,
      totalLines: lineMetrics.totalLines,
    });
  } catch {
    // Non-critical
  }

  return NextResponse.json(result);
}
