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

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

const Street = require('../../models/Street');
const User = require('../../models/User');

describe('Street Model', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    // Reset all mocks to ensure clean state
    mockCouchdbService.createDocument.mockReset();
    mockCouchdbService.findDocumentById.mockReset();
    mockCouchdbService.updateDocument.mockReset();
    mockCouchdbService.findByType.mockReset();
    mockCouchdbService.initialize.mockReset();
    mockCouchdbService.find.mockReset();
    mockCouchdbService.findStreetsByLocation.mockReset();
  });

  describe('Schema Validation', () => {
    it('should create a valid street', async () => {
      const streetData = {
        name: 'Main Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        },
        city: 'New York',
        state: 'NY',
      };

      const mockCreated = {
        _id: 'street_123',
        _rev: '1-abc',
        type: 'street',
        ...streetData,
        status: 'available',
        adoptedBy: null,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.initialize.mockResolvedValue(true);
      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

      const street = await Street.create(streetData);

      expect(street._id).toBeDefined();
      expect(street.name).toBe(streetData.name);
      expect(street.location.type).toBe('Point');
      expect(street.location.coordinates).toEqual(streetData.location.coordinates);
      expect(street.status).toBe('available');
    });

    it('should require name field', async () => {
      const streetData = {
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        },
        city: 'New York',
        state: 'NY',
      };

      expect(() => new Street(streetData)).toThrow();
    });

    it('should require location field', async () => {
      const streetData = {
        name: 'Main Street',
        city: 'New York',
        state: 'NY',
      };

      expect(() => new Street(streetData)).toThrow();
    });

    it('should not require adoptedBy field', async () => {
      const streetData = {
        name: 'Main Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        },
        city: 'New York',
        state: 'NY',
      };

      const mockCreated = {
        _id: 'street_123',
        _rev: '1-abc',
        type: 'street',
        ...streetData,
        status: 'available',
        adoptedBy: null,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

      const street = await Street.create(streetData);

      expect(street._id).toBeDefined();
      expect(street.adoptedBy).toBeNull();
      expect(street.status).toBe('available');
    });
  });

  describe('GeoJSON Location', () => {
    it('should store Point type correctly', async () => {
      const streetData = {
        name: 'Geo Street',
        location: {
          type: 'Point',
          coordinates: [-122.4194, 37.7749], // San Francisco
        },
        city: 'San Francisco',
        state: 'CA',
      };

      const mockCreated = {
        _id: 'street_123',
        _rev: '1-abc',
        type: 'street',
        ...streetData,
        status: 'available',
        adoptedBy: null,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

      const street = await Street.create(streetData);

      expect(street.location.type).toBe('Point');
      expect(street.location.coordinates).toEqual([-122.4194, 37.7749]);
      expect(street.location.coordinates[0]).toBe(-122.4194); // longitude
      expect(street.location.coordinates[1]).toBe(37.7749); // latitude
    });

    it('should support geospatial queries', async () => {
      const streetData = {
        name: 'NYC Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        },
        city: 'New York',
        state: 'NY',
      };

      const mockCreated = {
        _id: 'street_123',
        _rev: '1-abc',
        type: 'street',
        ...streetData,
        status: 'available',
        adoptedBy: null,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);
      mockCouchdbService.findByType.mockResolvedValue([mockCreated]);
      mockCouchdbService.findStreetsByLocation.mockResolvedValue([mockCreated]);

      // Test findNearby method
      const nearbyStreets = await Street.findNearby([-73.935242, 40.730610], 1000);
      expect(nearbyStreets.length).toBeGreaterThan(0);
      expect(nearbyStreets[0].name).toBe('NYC Street');
    });
  });

  describe('Status Field', () => {
    it('should default status to available', async () => {
      const streetData = {
        name: 'Status Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        },
        city: 'New York',
        state: 'NY',
      };

      const mockCreated = {
        _id: 'street_123',
        _rev: '1-abc',
        type: 'street',
        ...streetData,
        status: 'available',
        adoptedBy: null,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

      const street = await Street.create(streetData);

      expect(street.status).toBe('available');
    });

    it('should allow setting custom status', async () => {
      const streetData = {
        name: 'Custom Status Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        },
        city: 'New York',
        state: 'NY',
        status: 'adopted',
      };

      const mockCreated = {
        _id: 'street_123',
        _rev: '1-abc',
        type: 'street',
        ...streetData,
        adoptedBy: null,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

      const street = await Street.create(streetData);

      expect(street.status).toBe('adopted');
    });
  });

  describe('Timestamps', () => {
    it('should automatically set createdAt and updatedAt', async () => {
      const streetData = {
        name: 'Timestamp Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        },
        city: 'New York',
        state: 'NY',
      };

      const mockCreated = {
        _id: 'street_123',
        _rev: '1-abc',
        type: 'street',
        ...streetData,
        status: 'available',
        adoptedBy: null,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

      const street = await Street.create(streetData);

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

  describe('Relationships', () => {
    it('should reference User model through adoptedBy', async () => {
      const streetData = {
        name: 'Relationship Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        },
        city: 'New York',
        state: 'NY',
        adoptedBy: {
          userId: 'user_123',
          name: 'Adopter User',
          profilePicture: ''
        },
        status: 'adopted',
      };

      const mockCreated = {
        _id: 'street_123',
        _rev: '1-abc',
        type: 'street',
        ...streetData,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

      const street = await Street.create(streetData);

      expect(street.adoptedBy).toBeDefined();
      expect(street.adoptedBy.name).toBe('Adopter User');
    });
  });

  describe('Coordinates Format', () => {
    it('should accept valid longitude and latitude', async () => {
      const validCoordinates = [
        [-180, -90],  // min values
        [180, 90],    // max values
        [0, 0],       // origin
        [-74.006, 40.7128], // NYC
      ];

      for (const coords of validCoordinates) {
        const streetData = {
          name: `Street at ${coords.join(',')}`,
          location: {
            type: 'Point',
            coordinates: coords,
          },
          city: 'Test City',
          state: 'TS',
        };

        const mockCreated = {
          _id: 'street_123',
          _rev: '1-abc',
          type: 'street',
          ...streetData,
          status: 'available',
          adoptedBy: null,
          createdAt: '2023-01-01T00:00:00.000Z',
          updatedAt: '2023-01-01T00:00:00.000Z'
        };

        mockCouchdbService.createDocument.mockResolvedValue(mockCreated);

        const street = await Street.create(streetData);

        expect(street.location.coordinates).toEqual(coords);
      }
    });
  });
});
