import { describe, it, expect, beforeEach, afterAll, mock } from 'bun:test';
import { GroupSyncService } from '../../../src/services/group-sync';
import { db } from '../../../src/db';
import { Database } from 'bun:sqlite';
import { initializeDatabase } from '../../../src/db';
import { setDb } from '../../../src/db/locator';

// Store original globals
const originalFetch = globalThis.fetch;
const originalConsoleError = console.error;

describe('GroupSyncService', () => {
  let fetchMock: any;
  let testDb: Database;

  beforeEach(() => {
    // Create a new in-memory database for each test
    testDb = new Database(':memory:');
    initializeDatabase(testDb);
    
    // Assign the isolated DB to the service (via locator)
    setDb(testDb);
    
    // Clear and reset test data
    testDb.exec('DELETE FROM groups');
    testDb.exec('DELETE FROM sqlite_sequence WHERE name="groups"');
    GroupSyncService['lastSyncAttempt'] = 0;
    
    // Setup mock fetch to return proper Response object
    fetchMock = mock(async () => {
      const groups = [
        { id: 'group1', subject: 'Group 1', linkedParent: 'test-community' },
        { id: 'group2', subject: 'Group 2', linkedParent: 'other-community' },
        { id: 'group3', subject: 'Group 3' } // No linkedParent
      ];
      return {
        ok: true,
        status: 200,
        statusText: 'OK',
        headers: new Headers({'Content-Type': 'application/json'}),
        text: async () => JSON.stringify(groups),
        json: async () => groups
      };
    });
    globalThis.fetch = fetchMock;

    // Setup env vars
    process.env.WHATSAPP_COMMUNITY_ID = 'test-community';
    process.env.EVOLUTION_API_URL = 'http://test-api';
    process.env.EVOLUTION_API_INSTANCE = 'test-instance';
    process.env.EVOLUTION_API_KEY = 'test-key';
  });

  afterAll(() => {
    globalThis.fetch = originalFetch;
  });

  describe('syncGroups', () => {
    it('should skip sync if called too soon', async () => {
      GroupSyncService['lastSyncAttempt'] = Date.now() - 1000;
      const result = await GroupSyncService.syncGroups();
      expect(result).toEqual({ added: 0, updated: 0 });
      expect(fetchMock).not.toHaveBeenCalled();
    });

    it('should upsert all groups even if WHATSAPP_COMMUNITY_ID is missing (multicommunity)', async () => {
      process.env.WHATSAPP_COMMUNITY_ID = '';
      
      const result = await GroupSyncService.syncGroups();
      expect(result).toEqual({ added: 3, updated: 0 });
    });

    it('should upsert all groups regardless of community ID (multicommunity)', async () => {
      // Reset last sync attempt to force a sync
      GroupSyncService['lastSyncAttempt'] = 0;
      
      const result = await GroupSyncService.syncGroups();
      
      // Verify all groups were processed
      expect(result).toEqual({
        added: 3,
        updated: 0
      });
      
      // Verify database state
      const groups = testDb.query('SELECT id, community_id, name, active FROM groups').all() as any[];
      expect(groups).toHaveLength(3);

      const map = new Map(groups.map((g: any) => [g.id, g]));
      expect(map.get('group1')).toEqual({
        id: 'group1',
        community_id: 'test-community',
        name: 'Group 1',
        active: 1
      });
      expect(map.get('group2')).toEqual({
        id: 'group2',
        community_id: 'other-community',
        name: 'Group 2',
        active: 1
      });
      expect(map.get('group3')).toEqual({
        id: 'group3',
        community_id: '',
        name: 'Group 3',
        active: 1
      });
      
      // Verify API was called once
      expect(fetchMock).toHaveBeenCalledTimes(1);
    });

    it('should update existing groups and insert new ones (multicommunity)', async () => {
      // Add initial group
      testDb.exec(
        "INSERT INTO groups (id, community_id, name, active) VALUES ('group1', 'test-community', 'Old Name', 1)"
      );

      const result = await GroupSyncService.syncGroups();
      expect(result).toEqual({
        added: 2,
        updated: 1
      });

      const all = testDb.query('SELECT id, community_id, name, active FROM groups ORDER BY id').all();
      expect(all).toEqual([
        { id: 'group1', community_id: 'test-community', name: 'Group 1', active: 1 },
        { id: 'group2', community_id: 'other-community', name: 'Group 2', active: 1 },
        { id: 'group3', community_id: '', name: 'Group 3', active: 1 }
      ]);
    });

    it('should mark non-matching groups as inactive', async () => {
      // Add initial group not in current sync
      testDb.exec(
        "INSERT INTO groups (id, community_id, name, active, last_verified) VALUES ('old-group', 'test-community', 'Old Group', 1, '2023-01-01')"
      );

      await GroupSyncService.syncGroups();
      const group = testDb.query('SELECT * FROM groups WHERE id = ?').get('old-group');
      expect(group).toEqual(expect.objectContaining({
        id: 'old-group',
        community_id: 'test-community',
        name: 'Old Group',
        active: 0,
        last_verified: expect.any(String),
        onboarding_prompted_at: null
      }));
      expect(group.last_verified).not.toBe('2023-01-01'); // Should be updated
    });

    it('should handle API errors', async () => {
      const consoleErrorMock = mock(() => {});
      console.error = consoleErrorMock;
      
      globalThis.fetch = mock(async () => ({
        ok: false,
        status: 404,
        statusText: 'Not Found',
        headers: new Headers(),
        text: async () => 'Not Found',
        json: async () => ({ error: 'Not Found' })
      }));
      
      await expect(GroupSyncService.syncGroups()).rejects.toThrow('API request failed: 404 Not Found');
      expect(consoleErrorMock).toHaveBeenCalled();
      
      console.error = originalConsoleError;
    });
  });

  describe('isGroupActive', () => {
    beforeEach(() => {
      // Clear cache and add test groups
      GroupSyncService.activeGroupsCache.clear();
      testDb.exec('DELETE FROM groups');
      testDb.exec("INSERT INTO groups (id, community_id, name, active) VALUES ('active-group', 'test-community', 'Active Group', 1)");
      testDb.exec("INSERT INTO groups (id, community_id, name, active) VALUES ('inactive-group', 'test-community', 'Inactive Group', 0)");
      // Populate cache
      GroupSyncService['cacheActiveGroups']();
    });

    it('should return true for active groups', () => {
      const result = GroupSyncService.isGroupActive('active-group');
      expect(result).toBe(true);
    });

    it('should return false for inactive groups', () => {
      const result = GroupSyncService.isGroupActive('inactive-group');
      expect(result).toBe(false);
    });

    it('should return false for non-existent groups', () => {
      const result = GroupSyncService.isGroupActive('non-existent-group');
      expect(result).toBe(false);
    });
  });
});
