import { DocumentModel } from '../DocumentModel';
import { CreateDocumentInput } from '../types';

// Mock the database pool
jest.mock('../../config/database', () => ({
  query: jest.fn()
}));

// Mock the logger
jest.mock('../../utils/logger', () => ({
  info: jest.fn(),
  error: jest.fn(),
  warn: jest.fn()
}));

describe('DocumentModel', () => {
  let mockPool: any;

  beforeEach(() => {
    jest.clearAllMocks();
    mockPool = require('../../config/database');
  });

  describe('create', () => {
    it('should create a new document successfully', async () => {
      const documentData: CreateDocumentInput = {
        user_id: '123e4567-e89b-12d3-a456-426614174000',
        original_file_name: 'test.pdf',
        file_path: '/uploads/test.pdf',
        file_size: 1024000
      };

      const mockDocument = {
        id: '123e4567-e89b-12d3-a456-426614174001',
        ...documentData,
        uploaded_at: new Date(),
        status: 'uploaded',
        created_at: new Date(),
        updated_at: new Date()
      };

      mockPool.query.mockResolvedValueOnce({ rows: [mockDocument] });

      const result = await DocumentModel.create(documentData);

      expect(mockPool.query).toHaveBeenCalledWith(
        expect.stringContaining('INSERT INTO documents'),
        [documentData.user_id, documentData.original_file_name, documentData.file_path, documentData.file_size, 'uploaded'],
      );
      expect(result).toEqual(mockDocument);
    });

    it('should handle database errors', async () => {
      const documentData: CreateDocumentInput = {
        user_id: '123e4567-e89b-12d3-a456-426614174000',
        original_file_name: 'test.pdf',
        file_path: '/uploads/test.pdf',
        file_size: 1024000
      };

      const error = new Error('Database error');
      mockPool.query.mockRejectedValueOnce(error);

      await expect(DocumentModel.create(documentData)).rejects.toThrow('Database error');
    });
  });

  describe('findById', () => {
    it('should find document by ID successfully', async () => {
      const documentId = '123e4567-e89b-12d3-a456-426614174001';
      const mockDocument = {
        id: documentId,
        user_id: '123e4567-e89b-12d3-a456-426614174000',
        original_file_name: 'test.pdf',
        file_path: '/uploads/test.pdf',
        file_size: 1024000,
        uploaded_at: new Date(),
        status: 'uploaded',
        created_at: new Date(),
        updated_at: new Date()
      };

      mockPool.query.mockResolvedValueOnce({ rows: [mockDocument] });

      const result = await DocumentModel.findById(documentId);

      expect(mockPool.query).toHaveBeenCalledWith(
        'SELECT * FROM documents WHERE id = $1',
        [documentId]
      );
      expect(result).toEqual(mockDocument);
    });

    it('should return null when document not found', async () => {
      const documentId = '123e4567-e89b-12d3-a456-426614174001';

      mockPool.query.mockResolvedValueOnce({ rows: [] });

      const result = await DocumentModel.findById(documentId);

      expect(result).toBeNull();
    });
  });

  describe('findByUserId', () => {
    it('should find documents by user ID successfully', async () => {
      const userId = '123e4567-e89b-12d3-a456-426614174000';
      const mockDocuments = [
        {
          id: '123e4567-e89b-12d3-a456-426614174001',
          user_id: userId,
          original_file_name: 'test1.pdf',
          file_path: '/uploads/test1.pdf',
          file_size: 1024000,
          uploaded_at: new Date(),
          status: 'uploaded',
          created_at: new Date(),
          updated_at: new Date()
        },
        {
          id: '123e4567-e89b-12d3-a456-426614174002',
          user_id: userId,
          original_file_name: 'test2.pdf',
          file_path: '/uploads/test2.pdf',
          file_size: 2048000,
          uploaded_at: new Date(),
          status: 'completed',
          created_at: new Date(),
          updated_at: new Date()
        }
      ];

      mockPool.query.mockResolvedValueOnce({ rows: mockDocuments });

      const result = await DocumentModel.findByUserId(userId);

      expect(mockPool.query).toHaveBeenCalledWith(
        expect.stringContaining('SELECT * FROM documents'),
        [userId, 50, 0]
      );
      expect(result).toEqual(mockDocuments);
    });
  });

  describe('updateStatus', () => {
    it('should update document status successfully', async () => {
      const documentId = '123e4567-e89b-12d3-a456-426614174001';
      const newStatus = 'processing_llm';

      const mockUpdatedDocument = {
        id: documentId,
        user_id: '123e4567-e89b-12d3-a456-426614174000',
        original_file_name: 'test.pdf',
        file_path: '/uploads/test.pdf',
        file_size: 1024000,
        uploaded_at: new Date(),
        status: newStatus,
        processing_started_at: new Date(),
        created_at: new Date(),
        updated_at: new Date()
      };

      mockPool.query.mockResolvedValueOnce({ rows: [mockUpdatedDocument] });

      const result = await DocumentModel.updateStatus(documentId, newStatus);

      expect(mockPool.query).toHaveBeenCalledWith(
        expect.stringContaining('UPDATE documents'),
        [newStatus, documentId]
      );
      expect(result).toEqual(mockUpdatedDocument);
    });
  });

  describe('updateExtractedText', () => {
    it('should update extracted text successfully', async () => {
      const documentId = '123e4567-e89b-12d3-a456-426614174001';
      const extractedText = 'This is the extracted text from the PDF';

      const mockUpdatedDocument = {
        id: documentId,
        user_id: '123e4567-e89b-12d3-a456-426614174000',
        original_file_name: 'test.pdf',
        file_path: '/uploads/test.pdf',
        file_size: 1024000,
        uploaded_at: new Date(),
        status: 'extracting_text',
        extracted_text: extractedText,
        created_at: new Date(),
        updated_at: new Date()
      };

      mockPool.query.mockResolvedValueOnce({ rows: [mockUpdatedDocument] });

      const result = await DocumentModel.updateExtractedText(documentId, extractedText);

      expect(mockPool.query).toHaveBeenCalledWith(
        expect.stringContaining('UPDATE documents'),
        [extractedText, documentId]
      );
      expect(result).toEqual(mockUpdatedDocument);
    });
  });

  describe('updateGeneratedSummary', () => {
    it('should update generated summary successfully', async () => {
      const documentId = '123e4567-e89b-12d3-a456-426614174001';
      const summary = 'Generated summary content';
      const markdownPath = '/summaries/test.md';
      const pdfPath = '/summaries/test.pdf';

      const mockUpdatedDocument = {
        id: documentId,
        user_id: '123e4567-e89b-12d3-a456-426614174000',
        original_file_name: 'test.pdf',
        file_path: '/uploads/test.pdf',
        file_size: 1024000,
        uploaded_at: new Date(),
        status: 'completed',
        generated_summary: summary,
        summary_markdown_path: markdownPath,
        summary_pdf_path: pdfPath,
        created_at: new Date(),
        updated_at: new Date()
      };

      mockPool.query.mockResolvedValueOnce({ rows: [mockUpdatedDocument] });

      const result = await DocumentModel.updateGeneratedSummary(documentId, summary, markdownPath, pdfPath);

      expect(mockPool.query).toHaveBeenCalledWith(
        expect.stringContaining('UPDATE documents'),
        [summary, markdownPath, pdfPath, documentId]
      );
      expect(result).toEqual(mockUpdatedDocument);
    });
  });

  describe('delete', () => {
    it('should delete document successfully', async () => {
      const documentId = '123e4567-e89b-12d3-a456-426614174001';

      mockPool.query.mockResolvedValueOnce({ rows: [{ id: documentId }] });

      const result = await DocumentModel.delete(documentId);

      expect(mockPool.query).toHaveBeenCalledWith(
        'DELETE FROM documents WHERE id = $1 RETURNING id',
        [documentId]
      );
      expect(result).toBe(true);
    });

    it('should return false when document not found', async () => {
      const documentId = '123e4567-e89b-12d3-a456-426614174001';

      mockPool.query.mockResolvedValueOnce({ rows: [] });

      const result = await DocumentModel.delete(documentId);

      expect(result).toBe(false);
    });
  });

  describe('countByUser', () => {
    it('should return correct document count for user', async () => {
      const userId = '123e4567-e89b-12d3-a456-426614174000';
      const expectedCount = 5;

      mockPool.query.mockResolvedValueOnce({ rows: [{ count: expectedCount.toString() }] });

      const result = await DocumentModel.countByUser(userId);

      expect(mockPool.query).toHaveBeenCalledWith(
        'SELECT COUNT(*) FROM documents WHERE user_id = $1',
        [userId]
      );
      expect(result).toBe(expectedCount);
    });
  });

  describe('findByStatus', () => {
    it('should find documents by status successfully', async () => {
      const status = 'completed';
      const mockDocuments = [
        {
          id: '123e4567-e89b-12d3-a456-426614174001',
          user_id: '123e4567-e89b-12d3-a456-426614174000',
          original_file_name: 'test1.pdf',
          file_path: '/uploads/test1.pdf',
          file_size: 1024000,
          uploaded_at: new Date(),
          status,
          created_at: new Date(),
          updated_at: new Date()
        }
      ];

      mockPool.query.mockResolvedValueOnce({ rows: mockDocuments });

      const result = await DocumentModel.findByStatus(status);

      expect(mockPool.query).toHaveBeenCalledWith(
        expect.stringContaining('SELECT * FROM documents'),
        [status, 50, 0]
      );
      expect(result).toEqual(mockDocuments);
    });
  });

  describe('findPendingProcessing', () => {
    it('should find pending processing documents', async () => {
      const mockDocuments = [
        {
          id: '123e4567-e89b-12d3-a456-426614174001',
          user_id: '123e4567-e89b-12d3-a456-426614174000',
          original_file_name: 'test.pdf',
          file_path: '/uploads/test.pdf',
          file_size: 1024000,
          uploaded_at: new Date(),
          status: 'uploaded',
          created_at: new Date(),
          updated_at: new Date()
        }
      ];

      mockPool.query.mockResolvedValueOnce({ rows: mockDocuments });

      const result = await DocumentModel.findPendingProcessing();

      expect(mockPool.query).toHaveBeenCalledWith(
        expect.stringContaining('SELECT * FROM documents'),
        [10]
      );
      expect(result).toEqual(mockDocuments);
    });
  });
}); 