/**
 * @ai-summary Controller for OCR API endpoints
 */
import { FastifyReply, FastifyRequest } from 'fastify';
import { logger } from '../../../core/logging/logger';
import { ocrService } from '../domain/ocr.service';
import type { ExtractQuery, JobIdParams, JobSubmitBody } from './ocr.validation';

/** Supported MIME types for OCR */
const SUPPORTED_TYPES = new Set([
  'image/jpeg',
  'image/png',
  'image/heic',
  'image/heif',
  'application/pdf',
]);

export class OcrController {
  /**
   * POST /api/ocr/extract
   * Extract text from an uploaded image using synchronous OCR.
   */
  async extract(
    request: FastifyRequest<{ Querystring: ExtractQuery }>,
    reply: FastifyReply
  ) {
    const userId = (request as any).user?.sub as string;
    const preprocess = request.query.preprocess !== false;

    logger.info('OCR extract requested', {
      operation: 'ocr.controller.extract',
      userId,
      preprocess,
    });

    // Get uploaded file
    const file = await (request as any).file({ limits: { files: 1 } });
    if (!file) {
      logger.warn('No file provided for OCR', {
        operation: 'ocr.controller.extract.no_file',
        userId,
      });
      return reply.code(400).send({
        error: 'Bad Request',
        message: 'No file provided',
      });
    }

    // Validate content type
    const contentType = file.mimetype as string;
    if (!SUPPORTED_TYPES.has(contentType)) {
      logger.warn('Unsupported file type for OCR', {
        operation: 'ocr.controller.extract.unsupported_type',
        userId,
        contentType,
        fileName: file.filename,
      });
      return reply.code(415).send({
        error: 'Unsupported Media Type',
        message: `Unsupported file type: ${contentType}. Supported: JPEG, PNG, HEIC, PDF`,
      });
    }

    // Read file content
    const chunks: Buffer[] = [];
    for await (const chunk of file.file) {
      chunks.push(chunk);
    }
    const fileBuffer = Buffer.concat(chunks);

    if (fileBuffer.length === 0) {
      logger.warn('Empty file provided for OCR', {
        operation: 'ocr.controller.extract.empty_file',
        userId,
        fileName: file.filename,
      });
      return reply.code(400).send({
        error: 'Bad Request',
        message: 'Empty file provided',
      });
    }

    try {
      const result = await ocrService.extract(userId, {
        fileBuffer,
        contentType,
        preprocess,
      });

      logger.info('OCR extract completed', {
        operation: 'ocr.controller.extract.success',
        userId,
        success: result.success,
        documentType: result.documentType,
        processingTimeMs: result.processingTimeMs,
      });

      return reply.code(200).send(result);
    } catch (error: any) {
      if (error.statusCode === 413) {
        return reply.code(413).send({
          error: 'Payload Too Large',
          message: error.message,
        });
      }
      if (error.statusCode === 415) {
        return reply.code(415).send({
          error: 'Unsupported Media Type',
          message: error.message,
        });
      }

      logger.error('OCR extract failed', {
        operation: 'ocr.controller.extract.error',
        userId,
        error: error.message,
      });

      return reply.code(500).send({
        error: 'Internal Server Error',
        message: 'OCR processing failed',
      });
    }
  }

  /**
   * POST /api/ocr/jobs
   * Submit an async OCR job for large files.
   */
  async submitJob(
    request: FastifyRequest<{ Body: JobSubmitBody }>,
    reply: FastifyReply
  ) {
    const userId = (request as any).user?.sub as string;

    logger.info('OCR job submit requested', {
      operation: 'ocr.controller.submitJob',
      userId,
    });

    // Get uploaded file
    const file = await (request as any).file({ limits: { files: 1 } });
    if (!file) {
      logger.warn('No file provided for OCR job', {
        operation: 'ocr.controller.submitJob.no_file',
        userId,
      });
      return reply.code(400).send({
        error: 'Bad Request',
        message: 'No file provided',
      });
    }

    // Validate content type
    const contentType = file.mimetype as string;
    if (!SUPPORTED_TYPES.has(contentType)) {
      logger.warn('Unsupported file type for OCR job', {
        operation: 'ocr.controller.submitJob.unsupported_type',
        userId,
        contentType,
        fileName: file.filename,
      });
      return reply.code(415).send({
        error: 'Unsupported Media Type',
        message: `Unsupported file type: ${contentType}. Supported: JPEG, PNG, HEIC, PDF`,
      });
    }

    // Read file content
    const chunks: Buffer[] = [];
    for await (const chunk of file.file) {
      chunks.push(chunk);
    }
    const fileBuffer = Buffer.concat(chunks);

    if (fileBuffer.length === 0) {
      logger.warn('Empty file provided for OCR job', {
        operation: 'ocr.controller.submitJob.empty_file',
        userId,
        fileName: file.filename,
      });
      return reply.code(400).send({
        error: 'Bad Request',
        message: 'Empty file provided',
      });
    }

    // Get callback URL from form data (if present)
    const callbackUrl = file.fields?.callbackUrl?.value as string | undefined;

    try {
      const result = await ocrService.submitJob(userId, {
        fileBuffer,
        contentType,
        callbackUrl,
      });

      logger.info('OCR job submitted', {
        operation: 'ocr.controller.submitJob.success',
        userId,
        jobId: result.jobId,
        status: result.status,
      });

      return reply.code(202).send(result);
    } catch (error: any) {
      if (error.statusCode === 413) {
        return reply.code(413).send({
          error: 'Payload Too Large',
          message: error.message,
        });
      }
      if (error.statusCode === 415) {
        return reply.code(415).send({
          error: 'Unsupported Media Type',
          message: error.message,
        });
      }

      logger.error('OCR job submit failed', {
        operation: 'ocr.controller.submitJob.error',
        userId,
        error: error.message,
      });

      return reply.code(500).send({
        error: 'Internal Server Error',
        message: 'Job submission failed',
      });
    }
  }

  /**
   * GET /api/ocr/jobs/:jobId
   * Get the status of an async OCR job.
   */
  async getJobStatus(
    request: FastifyRequest<{ Params: JobIdParams }>,
    reply: FastifyReply
  ) {
    const userId = (request as any).user?.sub as string;
    const { jobId } = request.params;

    logger.debug('OCR job status requested', {
      operation: 'ocr.controller.getJobStatus',
      userId,
      jobId,
    });

    try {
      const result = await ocrService.getJobStatus(userId, jobId);

      return reply.code(200).send(result);
    } catch (error: any) {
      if (error.statusCode === 404) {
        return reply.code(404).send({
          error: 'Not Found',
          message: error.message,
        });
      }

      logger.error('OCR job status failed', {
        operation: 'ocr.controller.getJobStatus.error',
        userId,
        jobId,
        error: error.message,
      });

      return reply.code(500).send({
        error: 'Internal Server Error',
        message: 'Failed to retrieve job status',
      });
    }
  }
}
