import { documentRepository, DocumentFilters } from '@/lib/repositories/documentRepository';
import { storageAdapter } from '@/lib/adapters/storage';
import { Document } from '@/types/document';
import { Prisma } from '@/generated/prisma/client';
import { logger } from '@/lib/logger';

/**
 * Service para lógica de negocio de Documentos
 * 
 * Este service NO depende de Next.js, permitiendo migrar a backend separado.
 */
export class DocumentService {
  private static readonly MODULE = 'DocumentService';

  /**
   * Obtener documentos con filtros
   */
  async getDocuments(filters: DocumentFilters): Promise<Document[]> {
    logger.debug(DocumentService.MODULE, 'Obteniendo documentos', { filters });
    
    const documents = await documentRepository.findMany(filters);

    logger.info(DocumentService.MODULE, `Documentos obtenidos: ${documents.length}`, {
      count: documents.length,
      userId: filters.userId,
      role: filters.role,
    });

    // Transformar URLs a formato local para el frontend
    return documents.map((doc) => ({
      ...doc,
      fileUrl: this.buildLocalUrl(doc.fileUrl),
    }));
  }

  /**
   * Obtener un documento por ID
   */
  async getDocumentById(id: string): Promise<Document | null> {
    logger.debug(DocumentService.MODULE, 'Buscando documento por ID', { id });
    
    const document = await documentRepository.findById(id);

    if (!document) {
      logger.warn(DocumentService.MODULE, 'Documento no encontrado', { id });
      return null;
    }

    logger.info(DocumentService.MODULE, 'Documento encontrado', { id });

    return {
      ...document,
      fileUrl: this.buildLocalUrl(document.fileUrl),
    };
  }

  /**
   * Obtener todos los documentos (solo admin)
   */
  async getAllDocuments(): Promise<Document[]> {
    logger.info(DocumentService.MODULE, 'Obteniendo todos los documentos');
    
    const documents = await documentRepository.findAll();

    logger.info(DocumentService.MODULE, `Total documentos: ${documents.length}`);

    return documents.map((doc) => ({
      ...doc,
      fileUrl: this.buildLocalUrl(doc.fileUrl),
    }));
  }

  /**
   * Crear un documento con archivo
   */
  async createDocument(data: {
    fileName: string;
    fileData: string; // Base64
    mimeType: string;
    extractedData: Record<string, unknown>;
    ownerId: string;
    categoryId?: string;
  }): Promise<{ id: string }> {
    logger.info(DocumentService.MODULE, 'Creando documento', {
      fileName: data.fileName,
      ownerId: data.ownerId,
      categoryId: data.categoryId,
    });

    try {
      // 1. Guardar archivo en storage
      const buffer = Buffer.from(data.fileData, 'base64');
      const storedFile = await storageAdapter.save(buffer, data.fileName);

      logger.debug(DocumentService.MODULE, 'Archivo guardado en storage', {
        fileName: storedFile.name,
      });

      // 2. Guardar metadata en BD
      const document = await documentRepository.create({
        fileName: data.fileName,
        fileUrl: storedFile.name, // Guardamos solo el nombre único
        extractedData: data.extractedData as Prisma.InputJsonValue,
        owner: {
          connect: { id: data.ownerId },
        },
        category: data.categoryId ? {
          connect: { id: data.categoryId },
        } : undefined,
      });

      logger.info(DocumentService.MODULE, 'Documento creado exitosamente', {
        id: document.id,
        fileName: data.fileName,
      });

      return { id: document.id };
    } catch (error) {
      logger.error(
        DocumentService.MODULE,
        'Error al crear documento',
        error instanceof Error ? error : undefined,
        { fileName: data.fileName }
      );
      throw error;
    }
  }

  /**
   * Actualizar datos extraídos de un documento
   */
  async updateDocument(
    id: string,
    extractedData: Record<string, unknown>,
    userId: string,
    userRole: 'USER' | 'ADMIN'
  ): Promise<void> {
    logger.debug(DocumentService.MODULE, 'Actualizando documento', {
      id,
      userId,
      role: userRole,
    });

    const document = await documentRepository.findById(id);

    if (!document) {
      logger.warn(DocumentService.MODULE, 'Documento no encontrado para actualizar', { id });
      throw new Error('Documento no encontrado');
    }

    // Verificar permisos
    if (document.ownerId !== userId && userRole !== 'ADMIN') {
      logger.warn(DocumentService.MODULE, 'Intento de actualización sin permisos', {
        id,
        userId,
        ownerId: document.ownerId,
      });
      throw new Error('No tienes permisos para actualizar este documento');
    }

    await documentRepository.update(id, { extractedData: extractedData as Prisma.InputJsonValue });

    logger.info(DocumentService.MODULE, 'Documento actualizado', { id });
  }

  /**
   * Eliminar un documento
   */
  async deleteDocument(
    id: string,
    userId: string,
    userRole: 'USER' | 'ADMIN'
  ): Promise<void> {
    logger.info(DocumentService.MODULE, 'Eliminando documento', { id, userId, role: userRole });

    const document = await documentRepository.findById(id);

    if (!document) {
      logger.warn(DocumentService.MODULE, 'Documento no encontrado para eliminar', { id });
      throw new Error('Documento no encontrado');
    }

    // Verificar permisos
    if (document.ownerId !== userId && userRole !== 'ADMIN') {
      logger.warn(DocumentService.MODULE, 'Intento de eliminación sin permisos', {
        id,
        userId,
        ownerId: document.ownerId,
      });
      throw new Error('No tienes permisos para eliminar este documento');
    }

    try {
      // 1. Eliminar archivo del storage
      await storageAdapter.delete(document.fileUrl);
      logger.debug(DocumentService.MODULE, 'Archivo eliminado del storage', {
        fileUrl: document.fileUrl,
      });

      // 2. Eliminar registro de BD
      await documentRepository.delete(id);

      logger.info(DocumentService.MODULE, 'Documento eliminado exitosamente', { id });
    } catch (error) {
      logger.error(
        DocumentService.MODULE,
        'Error al eliminar documento',
        error instanceof Error ? error : undefined,
        { id }
      );
      throw error;
    }
  }

  /**
   * Eliminar todos los documentos (solo desarrollo)
   */
  async deleteAllDocuments(): Promise<number> {
    if (process.env.NODE_ENV !== 'development') {
      logger.warn(DocumentService.MODULE, 'Intento de eliminar todos los documentos en producción');
      throw new Error('Esta acción solo está permitida en desarrollo');
    }

    logger.info(DocumentService.MODULE, 'Eliminando todos los documentos (desarrollo)');

    try {
      // Eliminar todos los archivos del storage
      const documents = await documentRepository.findAll();
      logger.debug(DocumentService.MODULE, `Eliminando ${documents.length} archivos del storage`);
      
      for (const doc of documents) {
        await storageAdapter.delete(doc.fileUrl).catch(() => {});
      }

      // Eliminar registros de BD
      const result = await documentRepository.deleteMany();
      
      logger.info(DocumentService.MODULE, `Documentos eliminados: ${result.count}`);
      
      return result.count;
    } catch (error) {
      logger.error(
        DocumentService.MODULE,
        'Error al eliminar todos los documentos',
        error instanceof Error ? error : undefined
      );
      throw error;
    }
  }

  /**
   * Construir URL local para el frontend
   */
  private buildLocalUrl(fileUrl: string): string {
    // Si es una ruta completa, extraer solo el nombre
    const fileName = fileUrl.includes('/') ? fileUrl.split('/').pop()! : fileUrl;
    return `/api/files/${fileName}`;
  }

  /**
   * Extraer nombre de archivo de una URL
   */
  private extractFileName(fileUrl: string): string {
    return fileUrl.includes('/') ? fileUrl.split('/').pop()! : fileUrl;
  }
}

export const documentService = new DocumentService();
