/**
 * @ai-summary Unit tests for DocumentsService
 * @ai-context Tests business logic with mocked dependencies
 */

import { DocumentsService } from '../../domain/documents.service';
import { DocumentsRepository } from '../../data/documents.repository';
import pool from '../../../../core/config/database';

// Mock dependencies
jest.mock('../../data/documents.repository');
jest.mock('../../../../core/config/database');

const mockRepository = jest.mocked(DocumentsRepository);
const mockPool = jest.mocked(pool);

describe('DocumentsService', () => {
  let service: DocumentsService;
  let repositoryInstance: jest.Mocked<DocumentsRepository>;

  beforeEach(() => {
    jest.clearAllMocks();

    repositoryInstance = {
      insert: jest.fn(),
      findById: jest.fn(),
      listByUser: jest.fn(),
      updateMetadata: jest.fn(),
      updateStorageMeta: jest.fn(),
      softDelete: jest.fn(),
    } as any;

    mockRepository.mockImplementation(() => repositoryInstance);
    service = new DocumentsService();
  });

  describe('createDocument', () => {
    const mockDocumentBody = {
      vehicle_id: 'vehicle-123',
      document_type: 'insurance' as const,
      title: 'Car Insurance Policy',
      notes: 'Annual insurance policy',
      details: { provider: 'State Farm' },
      issued_date: '2024-01-01',
      expiration_date: '2024-12-31',
    };

    const mockCreatedDocument = {
      id: 'doc-123',
      user_id: 'user-123',
      vehicle_id: 'vehicle-123',
      document_type: 'insurance' as const,
      title: 'Car Insurance Policy',
      notes: 'Annual insurance policy',
      details: { provider: 'State Farm' },
      storage_bucket: null,
      storage_key: null,
      file_name: null,
      content_type: null,
      file_size: null,
      file_hash: null,
      issued_date: '2024-01-01',
      expiration_date: '2024-12-31',
      created_at: '2024-01-01T00:00:00Z',
      updated_at: '2024-01-01T00:00:00Z',
      deleted_at: null,
    };

    it('should create a document successfully', async () => {
      // Mock vehicle ownership check
      mockPool.query.mockResolvedValue({ rows: [{ id: 'vehicle-123' }] });
      repositoryInstance.insert.mockResolvedValue(mockCreatedDocument);

      const result = await service.createDocument('user-123', mockDocumentBody);

      expect(mockPool.query).toHaveBeenCalledWith(
        'SELECT id FROM vehicles WHERE id = $1 AND user_id = $2',
        ['vehicle-123', 'user-123']
      );
      expect(repositoryInstance.insert).toHaveBeenCalledWith({
        id: expect.any(String),
        user_id: 'user-123',
        vehicle_id: 'vehicle-123',
        document_type: 'insurance',
        title: 'Car Insurance Policy',
        notes: 'Annual insurance policy',
        details: { provider: 'State Farm' },
        issued_date: '2024-01-01',
        expiration_date: '2024-12-31',
      });
      expect(result).toEqual(mockCreatedDocument);
    });

    it('should create document with minimal data', async () => {
      const minimalBody = {
        vehicle_id: 'vehicle-123',
        document_type: 'registration' as const,
        title: 'Vehicle Registration',
      };

      mockPool.query.mockResolvedValue({ rows: [{ id: 'vehicle-123' }] });
      repositoryInstance.insert.mockResolvedValue({
        ...mockCreatedDocument,
        document_type: 'registration',
        title: 'Vehicle Registration',
        notes: null,
        details: null,
        issued_date: null,
        expiration_date: null,
      });

      const result = await service.createDocument('user-123', minimalBody);

      expect(repositoryInstance.insert).toHaveBeenCalledWith({
        id: expect.any(String),
        user_id: 'user-123',
        vehicle_id: 'vehicle-123',
        document_type: 'registration',
        title: 'Vehicle Registration',
        notes: null,
        details: null,
        issued_date: null,
        expiration_date: null,
      });
    });

    it('should reject document for non-owned vehicle', async () => {
      mockPool.query.mockResolvedValue({ rows: [] });

      await expect(service.createDocument('user-123', mockDocumentBody))
        .rejects.toThrow('Vehicle not found or not owned by user');

      expect(mockPool.query).toHaveBeenCalledWith(
        'SELECT id FROM vehicles WHERE id = $1 AND user_id = $2',
        ['vehicle-123', 'user-123']
      );
      expect(repositoryInstance.insert).not.toHaveBeenCalled();
    });

    it('should generate unique IDs for documents', async () => {
      mockPool.query.mockResolvedValue({ rows: [{ id: 'vehicle-123' }] });
      repositoryInstance.insert.mockResolvedValue(mockCreatedDocument);

      await service.createDocument('user-123', mockDocumentBody);
      await service.createDocument('user-123', mockDocumentBody);

      expect(repositoryInstance.insert).toHaveBeenCalledTimes(2);
      const firstCall = repositoryInstance.insert.mock.calls[0][0];
      const secondCall = repositoryInstance.insert.mock.calls[1][0];
      expect(firstCall.id).not.toEqual(secondCall.id);
      expect(firstCall.id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);
    });
  });

  describe('getDocument', () => {
    it('should return document if found', async () => {
      const mockDocument = {
        id: 'doc-123',
        user_id: 'user-123',
        title: 'Test Document',
      };
      repositoryInstance.findById.mockResolvedValue(mockDocument as any);

      const result = await service.getDocument('user-123', 'doc-123');

      expect(repositoryInstance.findById).toHaveBeenCalledWith('doc-123', 'user-123');
      expect(result).toEqual(mockDocument);
    });

    it('should return null if document not found', async () => {
      repositoryInstance.findById.mockResolvedValue(null);

      const result = await service.getDocument('user-123', 'doc-123');

      expect(repositoryInstance.findById).toHaveBeenCalledWith('doc-123', 'user-123');
      expect(result).toBeNull();
    });
  });

  describe('listDocuments', () => {
    const mockDocuments = [
      { id: 'doc-1', title: 'Insurance', document_type: 'insurance' },
      { id: 'doc-2', title: 'Registration', document_type: 'registration' },
    ];

    it('should list all user documents without filters', async () => {
      repositoryInstance.listByUser.mockResolvedValue(mockDocuments as any);

      const result = await service.listDocuments('user-123');

      expect(repositoryInstance.listByUser).toHaveBeenCalledWith('user-123', undefined);
      expect(result).toEqual(mockDocuments);
    });

    it('should list documents with filters', async () => {
      const filters = {
        vehicleId: 'vehicle-123',
        type: 'insurance' as const,
        expiresBefore: '2024-12-31',
      };
      repositoryInstance.listByUser.mockResolvedValue([mockDocuments[0]] as any);

      const result = await service.listDocuments('user-123', filters);

      expect(repositoryInstance.listByUser).toHaveBeenCalledWith('user-123', filters);
      expect(result).toEqual([mockDocuments[0]]);
    });
  });

  describe('updateDocument', () => {
    const mockExistingDocument = {
      id: 'doc-123',
      user_id: 'user-123',
      title: 'Original Title',
    };

    it('should update document successfully', async () => {
      const updateData = { title: 'Updated Title', notes: 'Updated notes' };
      const updatedDocument = { ...mockExistingDocument, ...updateData };

      repositoryInstance.findById.mockResolvedValue(mockExistingDocument as any);
      repositoryInstance.updateMetadata.mockResolvedValue(updatedDocument as any);

      const result = await service.updateDocument('user-123', 'doc-123', updateData);

      expect(repositoryInstance.findById).toHaveBeenCalledWith('doc-123', 'user-123');
      expect(repositoryInstance.updateMetadata).toHaveBeenCalledWith('doc-123', 'user-123', updateData);
      expect(result).toEqual(updatedDocument);
    });

    it('should return null if document not found', async () => {
      repositoryInstance.findById.mockResolvedValue(null);

      const result = await service.updateDocument('user-123', 'doc-123', { title: 'New Title' });

      expect(repositoryInstance.findById).toHaveBeenCalledWith('doc-123', 'user-123');
      expect(repositoryInstance.updateMetadata).not.toHaveBeenCalled();
      expect(result).toBeNull();
    });

    it('should return existing document if no valid patch provided', async () => {
      repositoryInstance.findById.mockResolvedValue(mockExistingDocument as any);

      const result = await service.updateDocument('user-123', 'doc-123', null as any);

      expect(repositoryInstance.findById).toHaveBeenCalledWith('doc-123', 'user-123');
      expect(repositoryInstance.updateMetadata).not.toHaveBeenCalled();
      expect(result).toEqual(mockExistingDocument);
    });
  });

  describe('deleteDocument', () => {
    it('should delete document successfully', async () => {
      repositoryInstance.softDelete.mockResolvedValue(undefined);

      await service.deleteDocument('user-123', 'doc-123');

      expect(repositoryInstance.softDelete).toHaveBeenCalledWith('doc-123', 'user-123');
    });
  });
});