/**
 * HTTP route handlers for Validation Operations.
 *
 * Routes:
 * - POST /api/validate/path - Validate file/directory path
 * - POST /api/validate/mentions - Batch validate path mentions
 * - POST /api/session/scroll-to-line - Deep link scroll handler
 */

import { createLogger } from '@shared/utils/logger';
import * as fsp from 'fs/promises';
import * as path from 'path';

import type { FastifyInstance } from 'fastify';

const logger = createLogger('HTTP:validation');

/**
 * Checks if a path is contained within a base directory.
 * Prevents path traversal attacks.
 */
function isPathContained(fullPath: string, basePath: string): boolean {
  const normalizedFull = path.normalize(fullPath);
  const normalizedBase = path.normalize(basePath);
  return normalizedFull === normalizedBase || normalizedFull.startsWith(normalizedBase + path.sep);
}

export function registerValidationRoutes(app: FastifyInstance): void {
  // Validate path
  app.post<{ Body: { relativePath: string; projectPath: string } }>(
    '/api/validate/path',
    async (request) => {
      try {
        const { relativePath, projectPath } = request.body;
        const fullPath = path.join(projectPath, relativePath);

        if (!isPathContained(fullPath, projectPath)) {
          logger.warn('validate-path blocked path traversal attempt:', relativePath);
          return { exists: false };
        }

        // Single async stat — no TOCTOU, doesn't block the main thread
        const stats = await fsp.stat(fullPath);
        return { exists: true, isDirectory: stats.isDirectory() };
      } catch {
        return { exists: false };
      }
    }
  );

  // Validate mentions
  app.post<{ Body: { mentions: { type: 'path'; value: string }[]; projectPath: string } }>(
    '/api/validate/mentions',
    async (request) => {
      const { mentions, projectPath } = request.body;

      // Validate all mentions in parallel with async I/O
      const entries = await Promise.all(
        mentions.map(async (mention) => {
          const fullPath = path.join(projectPath, mention.value);
          if (!isPathContained(fullPath, projectPath)) {
            return [`@${mention.value}`, false] as const;
          }
          try {
            await fsp.access(fullPath);
            return [`@${mention.value}`, true] as const;
          } catch {
            return [`@${mention.value}`, false] as const;
          }
        })
      );

      return Object.fromEntries(entries);
    }
  );

  // Scroll to line
  app.post<{ Body: { sessionId: string; lineNumber: number } }>(
    '/api/session/scroll-to-line',
    async (request) => {
      try {
        const { sessionId, lineNumber } = request.body;

        if (!sessionId) {
          logger.error('scroll-to-line called with empty sessionId');
          return { success: false, sessionId: '', lineNumber: 0 };
        }

        if (typeof lineNumber !== 'number' || lineNumber < 0) {
          logger.error('scroll-to-line called with invalid lineNumber');
          return { success: false, sessionId, lineNumber: 0 };
        }

        return { success: true, sessionId, lineNumber };
      } catch (error) {
        logger.error('Error in POST /api/session/scroll-to-line:', error);
        return { success: false, sessionId: '', lineNumber: 0 };
      }
    }
  );
}
