// 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(),
};

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

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

describe('Task 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.getDocument.mockReset();
    mockCouchdbService.findUserById.mockReset();
  });

  describe('Schema Validation', () => {
    it('should create a valid task', async () => {
      const streetData = {
        streetId: 'street_123',
        name: 'Test Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        }
      };

      const taskData = {
        street: streetData,
        description: 'Clean up litter on street',
        status: 'pending',
      };

      const mockCreated = {
        _id: 'task_123',
        _rev: '1-abc',
        type: 'task',
        ...taskData,
        pointsAwarded: 10,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

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

      const task = await Task.create(taskData);

      expect(task._id).toBeDefined();
      expect(task.description).toBe(taskData.description);
      expect(task.status).toBe(taskData.status);
      expect(task.street.streetId).toBe(streetData.streetId);
      expect(task.street.name).toBe(streetData.name);
    });

    it('should require street field', async () => {
      const taskData = {
        description: 'Task without street',
      };

      expect(() => new Task(taskData)).toThrow();
    });

    it('should require description field', async () => {
      const streetData = {
        streetId: 'street_123',
        name: 'Test Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        }
      };

      const taskData = {
        street: streetData,
      };

      expect(() => new Task(taskData)).toThrow();
    });
  });

  describe('Task Status', () => {
    it('should default status to pending', async () => {
      const streetData = {
        streetId: 'street_123',
        name: 'Test Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        }
      };

      const taskData = {
        street: streetData,
        description: 'Default status task',
      };

      const mockCreated = {
        _id: 'task_123',
        _rev: '1-abc',
        type: 'task',
        ...taskData,
        status: 'pending',
        pointsAwarded: 10,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

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

      const task = await Task.create(taskData);

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

    const validStatuses = ['pending', 'completed'];

    validStatuses.forEach(status => {
      it(`should accept "${status}" as valid status`, async () => {
        const streetData = {
          streetId: 'street_123',
          name: 'Test Street',
          location: {
            type: 'Point',
            coordinates: [-73.935242, 40.730610],
          }
        };

        const taskData = {
          street: streetData,
          description: `Task with ${status} status`,
          status,
        };

        const mockCreated = {
          _id: 'task_123',
          _rev: '1-abc',
          type: 'task',
          ...taskData,
          pointsAwarded: 10,
          createdAt: '2023-01-01T00:00:00.000Z',
          updatedAt: '2023-01-01T00:00:00.000Z'
        };

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

        const task = await Task.create(taskData);

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

  describe('Task Completion', () => {
    it('should allow completing a task', async () => {
      const streetData = {
        streetId: 'street_123',
        name: 'Test Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        }
      };

      const taskData = {
        street: streetData,
        description: 'Task to complete',
        status: 'pending',
      };

      const mockTask = {
        _id: 'task_123',
        _rev: '1-abc',
        type: 'task',
        ...taskData,
        pointsAwarded: 10,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

      mockCouchdbService.getDocument.mockResolvedValue(mockTask);
      mockCouchdbService.findUserById.mockResolvedValue({
        _id: 'user_123',
        name: 'Test User',
        email: 'test@example.com',
        password: 'password123'
      });
      mockCouchdbService.updateDocument.mockResolvedValue({
        ...mockTask,
        status: 'completed',
        completedBy: {
          userId: 'user_123',
          name: 'Test User',
          profilePicture: ''
        },
        completedAt: '2023-01-01T01:00:00.000Z',
        _rev: '2-def'
      });

      const task = await Task.findById('task_123');
      task.completedBy = {
        userId: 'user_123',
        name: 'Test User',
        profilePicture: ''
      };
      task.status = 'completed';
      task.completedAt = '2023-01-01T01:00:00.000Z';
      await task.save();

      expect(task.status).toBe('completed');
      expect(task.completedBy.userId).toBe('user_123');
      expect(task.completedAt).toBeDefined();
    });
  });

  describe('Points Awarded', () => {
    it('should default pointsAwarded to 10', async () => {
      const streetData = {
        streetId: 'street_123',
        name: 'Test Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        }
      };

      const taskData = {
        street: streetData,
        description: 'Default points task',
      };

      const mockCreated = {
        _id: 'task_123',
        _rev: '1-abc',
        type: 'task',
        ...taskData,
        status: 'pending',
        pointsAwarded: 10,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

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

      const task = await Task.create(taskData);

      expect(task.pointsAwarded).toBe(10);
    });

    it('should allow custom pointsAwarded', async () => {
      const streetData = {
        streetId: 'street_123',
        name: 'Test Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        }
      };

      const taskData = {
        street: streetData,
        description: 'Custom points task',
        pointsAwarded: 25,
      };

      const mockCreated = {
        _id: 'task_123',
        _rev: '1-abc',
        type: 'task',
        ...taskData,
        status: 'pending',
        pointsAwarded: 25,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

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

      const task = await Task.create(taskData);

      expect(task.pointsAwarded).toBe(25);
    });
  });

  describe('Timestamps', () => {
    it('should automatically set createdAt and updatedAt', async () => {
      const streetData = {
        streetId: 'street_123',
        name: 'Test Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        }
      };

      const taskData = {
        street: streetData,
        description: 'Timestamp task',
      };

      const mockCreated = {
        _id: 'task_123',
        _rev: '1-abc',
        type: 'task',
        ...taskData,
        status: 'pending',
        pointsAwarded: 10,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

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

      const task = await Task.create(taskData);

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

    it('should update updatedAt on modification', async () => {
      const streetData = {
        streetId: 'street_123',
        name: 'Test Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        }
      };

      const taskData = {
        street: streetData,
        description: 'Update test task',
      };

      const mockTask = {
        _id: 'task_123',
        _rev: '1-abc',
        type: 'task',
        ...taskData,
        status: 'pending',
        pointsAwarded: 10,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

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

      const task = await Task.findById('task_123');
      const originalUpdatedAt = task.updatedAt;

      task.status = 'completed';
      await task.save();

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

  describe('Relationships', () => {
    it('should reference Street model', async () => {
      const streetData = {
        streetId: 'street_123',
        name: 'Test Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        }
      };

      const taskData = {
        street: streetData,
        description: 'Street relationship task',
      };

      const mockCreated = {
        _id: 'task_123',
        _rev: '1-abc',
        type: 'task',
        ...taskData,
        status: 'pending',
        pointsAwarded: 10,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

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

      const task = await Task.create(taskData);

      expect(task.street).toBeDefined();
      expect(task.street.name).toBe('Test Street');
    });

    it('should reference User model for completedBy', async () => {
      const streetData = {
        streetId: 'street_123',
        name: 'Test Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        }
      };

      const userData = {
        userId: 'user_123',
        name: 'Test User',
        profilePicture: ''
      };

      const taskData = {
        street: streetData,
        description: 'Completed relationship task',
        completedBy: userData,
        status: 'completed',
      };

      const mockCreated = {
        _id: 'task_123',
        _rev: '1-abc',
        type: 'task',
        ...taskData,
        pointsAwarded: 10,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

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

      const task = await Task.create(taskData);

      expect(task.completedBy).toBeDefined();
      expect(task.completedBy.name).toBe('Test User');
    });
  });

  describe('Description Length', () => {
    it('should allow long descriptions', async () => {
      const streetData = {
        streetId: 'street_123',
        name: 'Test Street',
        location: {
          type: 'Point',
          coordinates: [-73.935242, 40.730610],
        }
      };

      const longDescription = 'a'.repeat(1001); // Long description

      const taskData = {
        street: streetData,
        description: longDescription,
      };

      const mockCreated = {
        _id: 'task_123',
        _rev: '1-abc',
        type: 'task',
        ...taskData,
        status: 'pending',
        pointsAwarded: 10,
        createdAt: '2023-01-01T00:00:00.000Z',
        updatedAt: '2023-01-01T00:00:00.000Z'
      };

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

      const task = await Task.create(taskData);

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