const couchdbService = require("../../services/couchdbService");

describe("CouchDB Service", () => {
  beforeAll(async () => {
    // Note: These tests require CouchDB to be running
    // They will be skipped if CouchDB is not available
    try {
      await couchdbService.initialize();
    } catch (error) {
      console.log("CouchDB not available, skipping tests");
    }
  });

  describe("Connection", () => {
    test("should initialize connection", async () => {
      if (!couchdbService.isReady()) {
        console.log("Skipping test - CouchDB not available");
        return;
      }
      
      expect(couchdbService.isReady()).toBe(true);
      expect(couchdbService.getDB()).toBeDefined();
    });

    test("should check connection status", () => {
      const isReady = couchdbService.isReady();
      expect(typeof isReady).toBe("boolean");
    });
  });

  describe("Document Operations", () => {
    test("should create and retrieve a document", async () => {
      if (!couchdbService.isReady()) {
        console.log("Skipping test - CouchDB not available");
        return;
      }

      const testDoc = {
        _id: "test_doc_1",
        type: "test",
        name: "Test Document",
        createdAt: new Date().toISOString()
      };

      // Create document
      const created = await couchdbService.createDocument(testDoc);
      expect(created._id).toBe(testDoc._id);
      expect(created._rev).toBeDefined();

      // Retrieve document
      const retrieved = await couchdbService.getDocument(testDoc._id);
      expect(retrieved._id).toBe(testDoc._id);
      expect(retrieved.name).toBe(testDoc.name);

      // Clean up
      await couchdbService.deleteDocument(testDoc._id, created._rev);
    });

    test("should update a document", async () => {
      if (!couchdbService.isReady()) {
        console.log("Skipping test - CouchDB not available");
        return;
      }

      const testDoc = {
        _id: "test_doc_2",
        type: "test",
        name: "Original Name",
        createdAt: new Date().toISOString()
      };

      // Create document
      const created = await couchdbService.createDocument(testDoc);

      // Update document
      created.name = "Updated Name";
      const updated = await couchdbService.updateDocument(created);
      expect(updated.name).toBe("Updated Name");
      expect(updated._rev).not.toBe(created._rev);

      // Clean up
      await couchdbService.deleteDocument(testDoc._id, updated._rev);
    });

    test("should find documents by selector", async () => {
      if (!couchdbService.isReady()) {
        console.log("Skipping test - CouchDB not available");
        return;
      }

      const testDocs = [
        {
          _id: "test_doc_3a",
          type: "test",
          category: "A",
          name: "Test A",
          createdAt: new Date().toISOString()
        },
        {
          _id: "test_doc_3b",
          type: "test",
          category: "B",
          name: "Test B",
          createdAt: new Date().toISOString()
        }
      ];

      // Create documents
      const created = await couchdbService.bulkDocs({ docs: testDocs });

      // Find by type
      const foundByType = await couchdbService.findByType("test");
      expect(foundByType.length).toBeGreaterThanOrEqual(2);

      // Find by category
      const foundByCategory = await couchdbService.find({
        selector: {
          type: "test",
          category: "A"
        }
      });
      expect(foundByCategory.length).toBe(1);
      expect(foundByCategory[0].category).toBe("A");

      // Clean up
      for (let i = 0; i < testDocs.length; i++) {
        if (created[i].ok) {
          await couchdbService.deleteDocument(testDocs[i]._id, created[i].rev);
        }
      }
    });
  });

  describe("Helper Functions", () => {
    test("should generate and extract IDs correctly", () => {
      const originalId = "1234567890abcdef";
      const type = "user";
      const prefixedId = couchdbService.generateId(type, originalId);
      
      expect(prefixedId).toBe(`${type}_${originalId}`);
      
      const extractedId = couchdbService.extractOriginalId(prefixedId);
      expect(extractedId).toBe(originalId);
    });

    test("should validate documents correctly", () => {
      const validDoc = {
        type: "user",
        name: "John Doe",
        email: "john@example.com"
      };

      const invalidDoc = {
        name: "John Doe"
        // Missing type
      };

      const validErrors = couchdbService.validateDocument(validDoc, ["email"]);
      expect(validErrors).toHaveLength(0);

      const invalidErrors = couchdbService.validateDocument(invalidDoc, ["email"]);
      expect(invalidErrors.length).toBeGreaterThan(0);
      expect(invalidErrors.some(e => e.includes("type"))).toBe(true);
    });
  });

  describe("User-specific Operations", () => {
    test("should find user by email", async () => {
      if (!couchdbService.isReady()) {
        console.log("Skipping test - CouchDB not available");
        return;
      }

      const testUser = {
        _id: "user_test_1",
        type: "user",
        name: "Test User",
        email: "test@example.com",
        password: "hashedpassword",
        points: 100,
        adoptedStreets: [],
        completedTasks: [],
        posts: [],
        events: [],
        earnedBadges: [],
        stats: {
          streetsAdopted: 0,
          tasksCompleted: 0,
          postsCreated: 0,
          eventsParticipated: 0,
          badgesEarned: 0
        },
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString()
      };

      // Create user
      const created = await couchdbService.createDocument(testUser);

      // Find by email
      const found = await couchdbService.findUserByEmail("test@example.com");
      expect(found).toBeTruthy();
      expect(found.email).toBe("test@example.com");
      expect(found.name).toBe("Test User");

      // Clean up
      await couchdbService.deleteDocument(testUser._id, created._rev);
    });
  });

  describe("Geospatial Operations", () => {
    test("should find streets by location", async () => {
      if (!couchdbService.isReady()) {
        console.log("Skipping test - CouchDB not available");
        return;
      }

      const testStreet = {
        _id: "street_test_1",
        type: "street",
        name: "Test Street",
        location: {
          type: "Point",
          coordinates: [-74.0060, 40.7128] // NYC coordinates
        },
        status: "available",
        stats: {
          tasksCount: 0,
          completedTasksCount: 0,
          reportsCount: 0,
          openReportsCount: 0
        },
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString()
      };

      // Create street
      const created = await couchdbService.createDocument(testStreet);

      // Find by location (bounding box around NYC)
      const bounds = [[-74.1, 40.6], [-73.9, 40.8]];
      const foundStreets = await couchdbService.findStreetsByLocation(bounds);
      
      // Should find at least our test street
      expect(foundStreets.length).toBeGreaterThanOrEqual(0);
      if (foundStreets.length > 0) {
        expect(foundStreets[0].type).toBe("street");
      }

      // Clean up
      await couchdbService.deleteDocument(testStreet._id, created._rev);
    });
  });

  afterAll(async () => {
    if (couchdbService.isReady()) {
      await couchdbService.shutdown();
    }
  });
});