// Mock CouchDB service for testing
const mockCouchdbService = {
  createDocument: jest.fn(),
  findDocumentById: jest.fn(),
  updateDocument: jest.fn(),
  findByType: jest.fn(),
  initialize: jest.fn(),
  getDocument: jest.fn(),
  findUserById: jest.fn(),
  update: jest.fn(),
};

// Mock the service module
jest.mock('../../services/couchdbService', () => mockCouchdbService);

const Report = require('../../models/Report');

describe('Report Model', () => {
  beforeEach(() => {
    // Reset all mocks to ensure clean state
    mockCouchdbService.createDocument.mockReset();
    mockCouchdbService.getDocument.mockReset();
    mockCouchdbService.updateDocument.mockReset();
    mockCouchdbService.findByType.mockReset();
  });

  describe('Schema Validation', () => {
    it('should create a valid report', async () => {
      const reportData = {
        street: 'street_123',
        reporter: 'user_123',
        type: 'pothole',
        description: 'Large pothole in the middle of the street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        }
      };

      const mockCreated = {
        _id: 'report_123',
        _rev: '1-abc',
        type: 'report',
        ...reportData,
        status: 'pending',
        imageUrl: null,
        cloudinaryPublicId: null,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

      const report = await Report.create(reportData);

      expect(report._id).toBeDefined();
      expect(report.street).toBe(reportData.street);
      expect(report.reporter).toBe(reportData.reporter);
      expect(report.type).toBe(reportData.type);
      expect(report.description).toBe(reportData.description);
      expect(report.status).toBe('pending');
    });

    it('should require street field', async () => {
      const reportData = {
        reporter: 'user_123',
        type: 'pothole',
        description: 'Report without street',
      };

      expect(() => new Report(reportData)).toThrow();
    });

    it('should require reporter field', async () => {
      const reportData = {
        street: 'street_123',
        type: 'pothole',
        description: 'Report without reporter',
      };

      expect(() => new Report(reportData)).toThrow();
    });

    it('should require type field', async () => {
      const reportData = {
        street: 'street_123',
        reporter: 'user_123',
        description: 'Report without type',
      };

      expect(() => new Report(reportData)).toThrow();
    });

    it('should require description field', async () => {
      const reportData = {
        street: 'street_123',
        reporter: 'user_123',
        type: 'pothole',
      };

      expect(() => new Report(reportData)).toThrow();
    });
  });

  describe('Report Types', () => {
    const validTypes = ['pothole', 'graffiti', 'trash', 'broken_light', 'other'];

    validTypes.forEach(type => {
      it(`should accept "${type}" as valid report type`, async () => {
        const reportData = {
          street: 'street_123',
          reporter: 'user_123',
          type,
          description: `Testing ${type} report`,
        };

        const mockCreated = {
          _id: 'report_123',
          _rev: '1-abc',
          type: 'report',
          ...reportData,
          status: 'pending',
          imageUrl: null,
          cloudinaryPublicId: null,
          createdAt: '2023-01-01T00:00:00.000Z',
          updatedAt: '2023-01-01T00:00:00.000Z'
        };

        mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

        const report = await Report.create(reportData);

        expect(report.type).toBe(type);
      });
    });

    it('should reject invalid report type', async () => {
      const reportData = {
        street: 'street_123',
        reporter: 'user_123',
        type: 'invalid_type',
        description: 'Invalid type report',
      };

      expect(() => new Report(reportData)).toThrow();
    });
  });

  describe('Status Field', () => {
    it('should default status to pending', async () => {
      const reportData = {
        street: 'street_123',
        reporter: 'user_123',
        type: 'pothole',
        description: 'Default status test',
      };

      const mockCreated = {
        _id: 'report_123',
        _rev: '1-abc',
        type: 'report',
        ...reportData,
        status: 'pending',
        imageUrl: null,
        cloudinaryPublicId: null,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

      const report = await Report.create(reportData);

      expect(report.status).toBe('pending');
    });

    const validStatuses = ['pending', 'in_progress', 'resolved', 'rejected'];

    validStatuses.forEach(status => {
      it(`should accept "${status}" as valid status`, async () => {
        const reportData = {
          street: 'street_123',
          reporter: 'user_123',
          type: 'pothole',
          description: `Testing ${status} status`,
          status,
        };

        const mockCreated = {
          _id: 'report_123',
          _rev: '1-abc',
          type: 'report',
          ...reportData,
          imageUrl: null,
          cloudinaryPublicId: null,
          createdAt: '2023-01-01T00:00:00.000Z',
          updatedAt: '2023-01-01T00:00:00.000Z'
        };

        mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

        const report = await Report.create(reportData);

        expect(report.status).toBe(status);
      });
    });
  });

  describe('Location', () => {
    it('should store location as GeoJSON Point', async () => {
      const reportData = {
        street: 'street_123',
        reporter: 'user_123',
        type: 'pothole',
        description: 'Report with location',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        }
      };

      const mockCreated = {
        _id: 'report_123',
        _rev: '1-abc',
        type: 'report',
        ...reportData,
        status: 'pending',
        imageUrl: null,
        cloudinaryPublicId: null,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

      const report = await Report.create(reportData);

      expect(report.location.type).toBe('Point');
      expect(report.location.coordinates).toEqual([-73.935242, 40.730610]);
    });

    it('should not require location field', async () => {
      const reportData = {
        street: 'street_123',
        reporter: 'user_123',
        type: 'pothole',
        description: 'Report without location',
      };

      const mockCreated = {
        _id: 'report_123',
        _rev: '1-abc',
        type: 'report',
        ...reportData,
        status: 'pending',
        imageUrl: null,
        cloudinaryPublicId: null,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

      const report = await Report.create(reportData);

      expect(report.location).toBeUndefined();
    });
  });

  describe('Image Support', () => {
    it('should allow image URL for reports', async () => {
      const reportData = {
        street: 'street_123',
        reporter: 'user_123',
        type: 'graffiti',
        description: 'Report with image',
        imageUrl: 'https://example.com/report-image.jpg',
        cloudinaryPublicId: 'report_123',
      };

      const mockCreated = {
        _id: 'report_123',
        _rev: '1-abc',
        type: 'report',
        ...reportData,
        status: 'pending',
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

      const report = await Report.create(reportData);

      expect(report.imageUrl).toBe('https://example.com/report-image.jpg');
      expect(report.cloudinaryPublicId).toBe('report_123');
    });

    it('should allow report without image', async () => {
      const reportData = {
        street: 'street_123',
        reporter: 'user_123',
        type: 'pothole',
        description: 'Report without image',
      };

      const mockCreated = {
        _id: 'report_123',
        _rev: '1-abc',
        type: 'report',
        ...reportData,
        status: 'pending',
        imageUrl: null,
        cloudinaryPublicId: null,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

      const report = await Report.create(reportData);

      expect(report.imageUrl).toBeNull();
      expect(report.cloudinaryPublicId).toBeNull();
    });
  });

  describe('Status Updates', () => {
    it('should allow updating status', async () => {
      const reportData = {
        street: 'street_123',
        reporter: 'user_123',
        type: 'pothole',
        description: 'Status update test',
      };

      const mockReport = {
        _id: 'report_123',
        _rev: '1-abc',
        type: 'report',
        ...reportData,
        status: 'pending',
        imageUrl: null,
        cloudinaryPublicId: null,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.getDocument.mockResolvedValue(mockReport);
      mockCouchdbService.updateDocument.mockResolvedValue({
        ...mockReport,
        status: 'resolved',
        _rev: '2-def'
      });

      const report = await Report.findById('report_123');
      report.status = 'resolved';
      await report.save();

      expect(report.status).toBe('resolved');
    });
  });

  describe('Timestamps', () => {
    it('should automatically set createdAt and updatedAt', async () => {
      const reportData = {
        street: 'street_123',
        reporter: 'user_123',
        type: 'pothole',
        description: 'Timestamp test',
      };

      const mockCreated = {
        _id: 'report_123',
        _rev: '1-abc',
        type: 'report',
        ...reportData,
        status: 'pending',
        imageUrl: null,
        cloudinaryPublicId: null,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

      const report = await Report.create(reportData);

      expect(report.createdAt).toBeDefined();
      expect(report.updatedAt).toBeDefined();
      expect(typeof report.createdAt).toBe('string');
      expect(typeof report.updatedAt).toBe('string');
    });

    it('should update updatedAt on modification', async () => {
      const reportData = {
        street: 'street_123',
        reporter: 'user_123',
        type: 'pothole',
        description: 'Update timestamp test',
      };

      const mockReport = {
        _id: 'report_123',
        _rev: '1-abc',
        type: 'report',
        ...reportData,
        status: 'pending',
        imageUrl: null,
        cloudinaryPublicId: null,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.getDocument.mockResolvedValue(mockReport);
      mockCouchdbService.updateDocument.mockResolvedValue({
        ...mockReport,
        status: 'in_progress',
        _rev: '2-def',
        updatedAt: '2023-01-01T00:00:01.000Z'
      });

      const report = await Report.findById('report_123');
      const originalUpdatedAt = report.updatedAt;

      report.status = 'in_progress';
      await report.save();

      expect(report.updatedAt).not.toBe(originalUpdatedAt);
    });
  });

  describe('Description Length', () => {
    it('should allow long descriptions', async () => {
      const longDescription = 'a'.repeat(1001); // Long description

      const reportData = {
        street: 'street_123',
        reporter: 'user_123',
        type: 'other',
        description: longDescription,
      };

      const mockCreated = {
        _id: 'report_123',
        _rev: '1-abc',
        type: 'report',
        ...reportData,
        status: 'pending',
        imageUrl: null,
        cloudinaryPublicId: null,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

      const report = await Report.create(reportData);

      expect(report.description).toBe(longDescription);
    });
  });

  describe('Static Methods', () => {
    it('should find report by ID', async () => {
      const mockReport = {
        _id: 'report_123',
        _rev: '1-abc',
        type: 'report',
        street: 'street_123',
        reporter: 'user_123',
        type: 'pothole',
        description: 'Test report',
        status: 'pending',
        imageUrl: null,
        cloudinaryPublicId: null,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.getDocument.mockResolvedValue(mockReport);

      const report = await Report.findById('report_123');
      expect(report).toBeDefined();
      expect(report._id).toBe('report_123');
      expect(report.type).toBe('pothole');
    });

    it('should return null when report not found', async () => {
      mockCouchdbService.getDocument.mockResolvedValue(null);

      const report = await Report.findById('nonexistent');
      expect(report).toBeNull();
    });
  });
});
