import { ShortenerService } from "../src/services/shortener.service";
import { IDatabaseAdapter, IUrl } from "../src/interfaces/database.interface";

// Mock database adapter for testing collection name and uniqueness
class MockDatabaseAdapter implements IDatabaseAdapter {
  private urls: Map<string, IUrl> = new Map();
  private collectionName: string = "urls";

  constructor(collectionName?: string) {
    if (collectionName) {
      this.collectionName = collectionName;
    }
  }

  async connect(): Promise<boolean> {
    return true;
  }

  async disconnect(): Promise<void> {}

  isConnected(): boolean {
    return true;
  }

  async createUrl(url: Omit<IUrl, "createdAt">): Promise<IUrl> {
    const newUrl: IUrl = {
      ...url,
      createdAt: new Date(),
    };
    this.urls.set(url.urlId, newUrl);
    return newUrl;
  }

  async findUrlByUrlId(urlId: string): Promise<IUrl | null> {
    return this.urls.get(urlId) || null;
  }

  async findUrlByOriginalUrl(originalUrl: string): Promise<IUrl | null> {
    for (const url of this.urls.values()) {
      if (url.originalUrl === originalUrl) {
        return url;
      }
    }
    return null;
  }

  async updateUrlClicks(urlId: string, clicks: number): Promise<void> {
    const url = this.urls.get(urlId);
    if (url) {
      url.clicks = clicks;
    }
  }

  async deleteUrl(urlId: string): Promise<boolean> {
    return this.urls.delete(urlId);
  }

  on(event: string, listener: (...args: any[]) => void): this {
    return this;
  }

  emit(event: string, ...args: any[]): boolean {
    return true;
  }

  // Helper method to get collection name for testing
  getCollectionName(): string {
    return this.collectionName;
  }

  // Helper method to get all URLs for testing
  getAllUrls(): IUrl[] {
    return Array.from(this.urls.values());
  }
}

describe("Collection Name and URL ID Uniqueness Tests", () => {
  it("should preserve collection name case", () => {
    const customCollectionName = "MyCustomCollection";
    const mockDbAdapter = new MockDatabaseAdapter(customCollectionName);
    expect(mockDbAdapter.getCollectionName()).toBe(customCollectionName);
  });

  it("should generate unique URL IDs for same original URL", async () => {
    const mockDbAdapter = new MockDatabaseAdapter();
    const service = new ShortenerService("https://example.com/", mockDbAdapter);

    const originalUrl = "https://www.google.com";

    // Create first shortened URL
    const result1 = await service.shorten(originalUrl);
    expect(result1.originalUrl).toBe(originalUrl);
    expect(result1.urlId).toBeDefined();

    // Create second shortened URL with same original URL
    const result2 = await service.shorten(originalUrl);
    expect(result2.originalUrl).toBe(originalUrl);
    expect(result2.urlId).toBeDefined();

    // URL IDs should be different
    expect(result1.urlId).not.toBe(result2.urlId);

    // Both should be stored in database
    const allUrls = mockDbAdapter.getAllUrls();
    expect(allUrls).toHaveLength(2);
    expect(allUrls[0].urlId).not.toBe(allUrls[1].urlId);
  });

  it("should handle custom URL ID conflicts", async () => {
    const mockDbAdapter = new MockDatabaseAdapter();
    const service = new ShortenerService("https://example.com/", mockDbAdapter);

    const originalUrl1 = "https://www.google.com";
    const originalUrl2 = "https://www.github.com";
    const customUrlId = "custom123";

    // Create first URL with custom ID
    const result1 = await service.shorten(originalUrl1, { customUrlId });
    expect(result1.urlId).toBe(customUrlId);

    // Try to create second URL with same custom ID
    const result2 = await service.shorten(originalUrl2, { customUrlId });

    // Should generate a new URL ID instead of using the same one
    expect(result2.urlId).not.toBe(customUrlId);
    expect(result2.urlId).toBeDefined();

    // Both URLs should be stored
    const allUrls = mockDbAdapter.getAllUrls();
    expect(allUrls).toHaveLength(2);
    expect(allUrls[0].urlId).not.toBe(allUrls[1].urlId);
  });

  it("should handle multiple URL ID conflicts gracefully", async () => {
    const mockDbAdapter = new MockDatabaseAdapter();
    const service = new ShortenerService("https://example.com/", mockDbAdapter);

    // First, create a URL with a known ID to create a conflict
    await service.shorten("https://www.example1.com", {
      customUrlId: "conflict123",
    });

    // Mock the generateUrlId method to return predictable IDs for testing
    const originalGenerateId = service["generateUrlId"];
    let callCount = 0;
    service["generateUrlId"] = () => {
      callCount++;
      // Return same ID for first few calls to simulate conflicts
      if (callCount <= 3) {
        return "conflict123";
      }
      return "unique456";
    };

    const result = await service.shorten("https://www.example.com");

    // Should eventually get a unique ID
    expect(result.urlId).toBe("unique456");

    // Restore original method
    service["generateUrlId"] = originalGenerateId;
  });
});
