import { Logger } from "winston";
import axios from "axios";
import { LaplaceConfiguration } from "../utilities/configuration";
import {
  NewsClient,
  NewsType,
  NewsOrderBy,
  NewsLane,
} from "../client/news";
import "./client_test_suite";
import { Region, Locale } from "../client/collections";
import { SortDirection } from "../client/broker";

const mockNewsHighlightsResponse = {
  tech: [
    "Alphabet ve Amazon'un desteğiyle Anthropic, 2026 başlarında Hindistan'ın Bengaluru kentinde bir ofis açacak."
  ],
  other: [
    "ABD Yüksek Mahkemesi, Epic Games'in davası kapsamında Google'ın Play uygulamalarındaki değişikliği engellemeyecek."
  ],
  finance: [
    "Fifth Third Bank, Comerica'yı 10,9 milyar dolara satın alacak ve böylece ABD'nin 9. en büyük bankası olacak."
  ],
  consumer: [
    "Tesla, rekabet ortamında pazar payını geri almak için daha ucuz Model Y ve Model 3'ü piyasaya sürdü; duyuru hisseleri etkiledi."
  ],
  healthcare: [
    "İlaç üreticileri, Amgen ve Novo Nordisk'in de dahil olduğu şekilde, Trump'ın ilaç fiyatlarını düşürme planıyla uyumlu olarak tele-sağlık satışlarını artırıyor."
  ],
  energyAndUtilities: [
    "ABD Enerji Bakanlığı, Stellantis ve GM'ye verilen 1,1 milyar dolarlık hibeleri iptal edebilir."
  ],
  industrialsAndMaterials: [
    "Boeing, bir grevi sona erdirmek için IAM Sendikası ile geçici bir anlaşmaya vardı; detaylar açıklanmadı."
  ]
};

const mockNewsCategoriesResponse = [
  { id: "13702", name: "General News" },
  { id: "13703", name: "Sector News" },
  { id: "13704", name: "Market News" },
  { id: "13705", name: "Stock Spesific News" }
];

const mockNewsLanesResponse = [
  { id: "global_macro", label: "Global Macro" },
  { id: "tr_ekonomi", label: "TR Ekonomi" },
  { id: "bist", label: "BIST" },
  { id: "fast_movers", label: "Fast Movers" }
];

const mockNewsHighlightsPaginatedResponse = {
  items: [
    { id: "hl_1", createdAt: "2026-06-15T00:00:00Z", ...mockNewsHighlightsResponse },
    { id: "hl_2", createdAt: "2026-06-14T00:00:00Z", ...mockNewsHighlightsResponse }
  ],
  recordCount: 2
};

const mockNewsResponse = {
  items: [
    {
      id: "68e5a1b2c3d4e5f60718293a",
      url: "https://www.reuters.com/business/energy/commonwealth-lng-wants-more-time-build-planned-export-facility-louisiana-2025-10-07/",
      content: {
        title: "Commonwealth LNG wants more time to build planned export facility in Louisiana",
        content: [
          "Commonwealth LNG has requested a four-year extension from federal regulators to construct & begin exporting liquefied natural gas..."
        ],
        summary: [
          "Commonwealth LNG has requested a four-year extension from federal regulators..."
        ],
        description:
          "Commonwealth LNG has asked federal regulators for a four-year extension...",
        investorInsight:
          "What it means for investors: The extension request could postpone..."
      },
      sectors: { id: "65533e047844ee7afe9941bf", name: "Energy" },
      tickers: [{ id: "6203d1ba1e674875275558f7", name: "EQT Corp", symbol: "EQT" }],
      imageUrl: "",
      createdAt: "2025-10-07T17:10:01.560644Z",
      publisher: { name: "Reuters", logoUrl: null },
      timestamp: "2025-10-07T16:50:16Z",
      categories: { id: "3", name: "Sector News", categoryType: "StockSpesific" },
      industries: { id: "65533e441fa5c7b58afa0944", name: "Oil/Gas (Production and Exploration)" },
      publisherUrl: "Reuters",
      qualityScore: 0,
      relatedTickers: [{ id: "6203d1ba1e674875275558f7", name: "EQT Corp", symbol: "EQT" }]
    }
  ],
  recordCount: 352
};

const mockNewsV2Response = {
  items: mockNewsResponse.items.map(({ relatedTickers, ...rest }) => rest),
  recordCount: mockNewsResponse.recordCount
};

describe("NewsClient", () => {
  let client: NewsClient;

  beforeAll(() => {
    const config = (global as any).testSuite.config as LaplaceConfiguration;
    const logger: Logger = {
      info: jest.fn(),
      error: jest.fn(),
      warn: jest.fn(),
      debug: jest.fn(),
    } as unknown as Logger;

    client = new NewsClient(config, logger);
  });

  describe("Integration Tests", () => {
    jest.setTimeout(60_000);

    test("getHighlights returns valid paginated data", async () => {
      const resp = await client.getHighlights(Region.Us, Locale.Tr);

      expect(resp).toBeDefined();
      expect(typeof resp.recordCount).toBe("number");
      expect(Array.isArray(resp.items)).toBe(true);

      if (resp.items.length > 0) {
        const item = resp.items[0];
        expect(typeof item.id).toBe("string");
        expect(typeof item.createdAt).toBe("string");
        expect(Array.isArray(item.consumer)).toBe(true);
        expect(Array.isArray(item.energyAndUtilities)).toBe(true);
        expect(Array.isArray(item.finance)).toBe(true);
        expect(Array.isArray(item.healthcare)).toBe(true);
        expect(Array.isArray(item.industrialsAndMaterials)).toBe(true);
        expect(Array.isArray(item.tech)).toBe(true);
        expect(Array.isArray(item.other)).toBe(true);
      }
    });

    test("getHighlights narrows to a date range", async () => {
      const resp = await client.getHighlights(Region.Us, Locale.Tr, {
        from: "2026-06-01",
        to: "2026-07-01",
        skip: 0,
        top: 20,
      });

      expect(resp).toBeDefined();
      expect(typeof resp.recordCount).toBe("number");
      expect(Array.isArray(resp.items)).toBe(true);
    });

    test("getNewsCategories returns valid data", async () => {
      const resp = await client.getNewsCategories(Locale.En);

      expect(Array.isArray(resp)).toBe(true);
      expect(resp.length).toBeGreaterThan(0);

      const c = resp[0];
      expect(typeof c.id).toBe("string");
      expect(typeof c.name).toBe("string");
    });

    test("getNewsLanes returns valid data", async () => {
      const resp = await client.getNewsLanes();

      expect(Array.isArray(resp)).toBe(true);
      if (resp.length > 0) {
        const l = resp[0];
        expect(typeof l.id).toBe("string");
        expect(typeof l.label).toBe("string");
      }
    });

    test("getApiSourceNames returns valid data", async () => {
      const resp = await client.getApiSourceNames();

      expect(Array.isArray(resp)).toBe(true);
      if (resp.length > 0) {
        const s = resp[0];
        expect(typeof s.id).toBe("string");
        expect(typeof s.name).toBe("string");
      }
    });

    test("getNews returns valid paginated data", async () => {
      const resp = await client.getNews(Region.Us, Locale.Tr, {
        newsType: NewsType.BRIEFS,
        page: 0,
        size: 10,
        orderBy: NewsOrderBy.TIMESTAMP,
        orderByDirection: SortDirection.Desc,
      });

      expect(resp).toBeDefined();
      expect(typeof resp.recordCount).toBe("number");
      expect(resp.recordCount).toBeGreaterThanOrEqual(0);
      expect(Array.isArray(resp.items)).toBe(true);

      if (resp.items.length > 0) {
        const n = resp.items[0];

        expect(typeof n.id).toBe("string");
        expect(typeof n.url).toBe("string");
        expect(typeof n.imageUrl).toBe("string");
        expect(typeof n.timestamp).toBe("string");
        expect(typeof n.publisherUrl).toBe("string");
        expect(typeof n.qualityScore).toBe("number");
        expect(typeof n.createdAt).toBe("string");

        expect(n.publisher).toBeDefined();
        expect(typeof n.publisher.name).toBe("string");
        expect(
          typeof n.publisher.logoUrl === "string" || n.publisher.logoUrl == null
        ).toBe(true);

        expect(Array.isArray(n.relatedTickers)).toBe(true);
        if (n.relatedTickers.length > 0) {
          const t = n.relatedTickers[0];
          expect(typeof t.id).toBe("string");
          expect(typeof t.name).toBe("string");
          expect(typeof t.symbol === "string" || t.symbol == null).toBe(true);
        }

        if (n.tickers != null) {
          expect(Array.isArray(n.tickers)).toBe(true);
        }

        if (n.categories != null) {
          expect(typeof n.categories.id).toBe("string");
          expect(typeof n.categories.name).toBe("string");
          expect(
            typeof n.categories.categoryType === "string" ||
            n.categories.categoryType == null ||
            n.categories.categoryType === undefined
          ).toBe(true);
        }

        if (n.sectors != null) {
          expect(typeof n.sectors.id).toBe("string");
          expect(typeof n.sectors.name).toBe("string");
        }

        if (n.industries != null) {
          expect(typeof n.industries.id).toBe("string");
          expect(typeof n.industries.name).toBe("string");
        }

        if (n.content != null) {
          expect(typeof n.content.title).toBe("string");
          expect(typeof n.content.description).toBe("string");
          expect(Array.isArray(n.content.content)).toBe(true);
          expect(Array.isArray(n.content.summary)).toBe(true);
          expect(typeof n.content.investorInsight).toBe("string");
        }
      }
    });

    test("getNewsV2 returns valid paginated data", async () => {
      const resp = await client.getNewsV2(Region.Us, Locale.Tr, {
        newsType: NewsType.BRIEFS,
        page: 0,
        size: 10,
        orderBy: NewsOrderBy.TIMESTAMP,
        orderByDirection: SortDirection.Desc,
      });

      expect(resp).toBeDefined();
      expect(typeof resp.recordCount).toBe("number");
      expect(resp.recordCount).toBeGreaterThanOrEqual(0);
      expect(Array.isArray(resp.items)).toBe(true);

      if (resp.items.length > 0) {
        const n = resp.items[0];

        expect(typeof n.id).toBe("string");
        expect(typeof n.url).toBe("string");
        expect(typeof n.imageUrl).toBe("string");
        expect(typeof n.timestamp).toBe("string");
        expect(typeof n.publisherUrl).toBe("string");
        expect(typeof n.qualityScore).toBe("number");
        expect(typeof n.createdAt).toBe("string");

        expect((n as any).relatedTickers).toBeUndefined();

        expect(n.publisher).toBeDefined();
        expect(typeof n.publisher.name).toBe("string");
      }
    });

    test("streamNews yields item before timeout or throws gracefully if none arrive", async () => {
      let newsItemsReceived = 0;
      const { events, cancel } = client.streamNews(Region.Us, Locale.Tr);

      const receivePromise = (async () => {
        for await (const items of events) {
          if (items && items.length > 0) {
            newsItemsReceived += items.length;
            break;
          }
        }
      })();

      const timeoutPromise = new Promise((resolve) => setTimeout(resolve, 8000));
      await Promise.race([receivePromise, timeoutPromise]);
      cancel();
      expect(newsItemsReceived).toBeGreaterThanOrEqual(0);
    });
  });

  describe("Mock Tests", () => {
    let client: NewsClient;
    let cli: { request: jest.Mock };

    beforeEach(() => {
      cli = { request: jest.fn() };

      const config = (global as any).testSuite.config as LaplaceConfiguration;
      const logger: Logger = {
        info: jest.fn(),
        error: jest.fn(),
        warn: jest.fn(),
        debug: jest.fn()
      } as unknown as Logger;

      client = new NewsClient(config, logger, cli as any);
    });

    describe("getHighlights", () => {
      test("calls correct endpoint/params and matches raw response", async () => {
        cli.request.mockResolvedValueOnce({ data: mockNewsHighlightsPaginatedResponse });

        const resp = await client.getHighlights(Region.Tr, Locale.Tr);

        expect(cli.request).toHaveBeenCalledTimes(1);
        const call = cli.request.mock.calls[0][0];

        expect(call.method).toBe("GET");
        expect(call.url).toBe("/api/v1/news/highlights");
        expect(call.params).toEqual({ region: Region.Tr, locale: Locale.Tr });

        expect(resp.recordCount).toBe(2);
        expect(resp.items).toHaveLength(2);
        expect(resp.items[0].id).toBe("hl_1");
        expect(resp.items[0].createdAt).toBe("2026-06-15T00:00:00Z");
        expect(resp.items[0].tech).toEqual(mockNewsHighlightsResponse.tech);
        expect(resp.items[0].consumer).toEqual(mockNewsHighlightsResponse.consumer);
      });

      test("sends from/to/skip/top when provided", async () => {
        cli.request.mockResolvedValueOnce({ data: mockNewsHighlightsPaginatedResponse });

        await client.getHighlights(Region.Us, Locale.Tr, {
          from: "2026-06-01",
          to: "2026-07-01",
          skip: 0,
          top: 20,
        });

        const call = cli.request.mock.calls[0][0];
        expect(call.params).toEqual({
          region: Region.Us,
          locale: Locale.Tr,
          from: "2026-06-01",
          to: "2026-07-01",
          skip: 0,
          top: 20,
        });
      });

      test("omits from/to/skip/top when not provided", async () => {
        cli.request.mockResolvedValueOnce({ data: mockNewsHighlightsPaginatedResponse });

        await client.getHighlights(Region.Us, Locale.Tr, {});

        const call = cli.request.mock.calls[0][0];
        expect(call.params).toEqual({ region: Region.Us, locale: Locale.Tr });
      });

      test("bubbles up request error", async () => {
        cli.request.mockRejectedValueOnce(new Error("Failed to fetch highlights"));

        await expect(client.getHighlights(Region.Tr, Locale.Tr)).rejects.toThrow(
          "Failed to fetch highlights"
        );

        expect(cli.request).toHaveBeenCalledTimes(1);
      });
    });

    describe("getNewsCategories", () => {
      test("calls correct endpoint/params and matches raw response", async () => {
        cli.request.mockResolvedValueOnce({ data: mockNewsCategoriesResponse });

        const resp = await client.getNewsCategories(Locale.En);

        expect(cli.request).toHaveBeenCalledTimes(1);
        const call = cli.request.mock.calls[0][0];

        expect(call.method).toBe("GET");
        expect(call.url).toBe("/api/v1/news/categories");
        expect(call.params).toEqual({ locale: Locale.En });

        expect(resp).toEqual(mockNewsCategoriesResponse);
      });

      test("does not send locale when undefined", async () => {
        cli.request.mockResolvedValueOnce({ data: mockNewsCategoriesResponse });

        await client.getNewsCategories();

        const call = cli.request.mock.calls[0][0];
        expect(call.params).toEqual({});
      });

      test("bubbles up request error", async () => {
        cli.request.mockRejectedValueOnce(new Error("Failed to fetch categories"));

        await expect(client.getNewsCategories(Locale.En)).rejects.toThrow(
          "Failed to fetch categories"
        );

        expect(cli.request).toHaveBeenCalledTimes(1);
      });
    });

    describe("getNewsLanes", () => {
      test("calls correct endpoint and matches raw response", async () => {
        cli.request.mockResolvedValueOnce({ data: mockNewsLanesResponse });

        const resp = await client.getNewsLanes();

        expect(cli.request).toHaveBeenCalledTimes(1);
        const call = cli.request.mock.calls[0][0];

        expect(call.method).toBe("GET");
        expect(call.url).toBe("/api/v1/news/lanes");
        expect(call.params).toEqual({});

        expect(resp).toEqual(mockNewsLanesResponse);
      });

      test("sends region when provided", async () => {
        cli.request.mockResolvedValueOnce({ data: mockNewsLanesResponse });

        await client.getNewsLanes(Region.Us);

        const call = cli.request.mock.calls[0][0];
        expect(call.params).toEqual({ region: Region.Us });
      });

      test("bubbles up request error", async () => {
        cli.request.mockRejectedValueOnce(new Error("Failed to fetch lanes"));

        await expect(client.getNewsLanes()).rejects.toThrow(
          "Failed to fetch lanes"
        );

        expect(cli.request).toHaveBeenCalledTimes(1);
      });
    });

    describe("getApiSourceNames", () => {
      const mockApiSourceNames = [
        { id: "BBCBusiness", name: "BBC Business" },
        { id: "MarketWatch", name: "MarketWatch" },
        { id: "GazeteOksijen", name: "Gazete Oksijen" }
      ];

      test("calls correct endpoint and matches raw response", async () => {
        cli.request.mockResolvedValueOnce({ data: mockApiSourceNames });

        const resp = await client.getApiSourceNames();

        expect(cli.request).toHaveBeenCalledTimes(1);
        const call = cli.request.mock.calls[0][0];

        expect(call.method).toBe("GET");
        expect(call.url).toBe("/api/v1/news/api-source-names");
        expect(call.params).toEqual({});

        expect(resp).toEqual(mockApiSourceNames);
      });

      test("sends region and language when provided", async () => {
        cli.request.mockResolvedValueOnce({ data: mockApiSourceNames });

        await client.getApiSourceNames(Region.Us, Locale.Tr);

        const call = cli.request.mock.calls[0][0];
        expect(call.params).toEqual({ region: Region.Us, language: Locale.Tr });
      });

      test("bubbles up request error", async () => {
        cli.request.mockRejectedValueOnce(
          new Error("Failed to fetch api source names")
        );

        await expect(client.getApiSourceNames()).rejects.toThrow(
          "Failed to fetch api source names"
        );

        expect(cli.request).toHaveBeenCalledTimes(1);
      });
    });

    describe("getNews", () => {
      test("calls correct endpoint/params and matches raw response", async () => {
        cli.request.mockResolvedValueOnce({ data: mockNewsResponse });

        const resp = await client.getNews(Region.Tr, Locale.Tr, {
          lane: NewsLane.BIST,
          apiSource: "BBCBusiness,MarketWatch",
          newsType: NewsType.BRIEFS,
          page: 1,
          size: 10,
          orderBy: NewsOrderBy.TIMESTAMP,
          orderByDirection: SortDirection.Desc,
          symbols: "AAPL,MSFT",
          categoryIds: "1,2",
          sectorIds: "65533e047844ee7afe9941bf",
          industryIds: "65533e441fa5c7b58afa0944",
          qualityScoreMin: 7,
          qualityScoreMax: 10,
          timestampFrom: "2026-05-01",
          timestampTo: "2026-06-01",
        });

        expect(cli.request).toHaveBeenCalledTimes(1);
        const call = cli.request.mock.calls[0][0];

        expect(call.method).toBe("GET");
        expect(call.url).toBe("/api/v1/news");
        expect(call.params).toEqual({
          region: Region.Tr,
          locale: Locale.Tr,
          lane: NewsLane.BIST,
          apiSource: "BBCBusiness,MarketWatch",
          newsType: NewsType.BRIEFS,
          page: 1,
          size: 10,
          orderBy: NewsOrderBy.TIMESTAMP,
          orderByDirection: SortDirection.Desc,
          symbols: "AAPL,MSFT",
          categoryIds: "1,2",
          sectorIds: "65533e047844ee7afe9941bf",
          industryIds: "65533e441fa5c7b58afa0944",
          qualityScoreMin: 7,
          qualityScoreMax: 10,
          timestampFrom: "2026-05-01",
          timestampTo: "2026-06-01",
        });

        expect(resp.recordCount).toBe(352);
        expect(resp.items).toHaveLength(1);

        const n = resp.items[0];

        expect(n.id).toBe(mockNewsResponse.items[0].id);
        expect(n.url).toBe(mockNewsResponse.items[0].url);
        expect(n.imageUrl).toBe(mockNewsResponse.items[0].imageUrl);
        expect(n.timestamp).toBe(mockNewsResponse.items[0].timestamp);
        expect(n.publisherUrl).toBe(mockNewsResponse.items[0].publisherUrl);
        expect(n.qualityScore).toBe(mockNewsResponse.items[0].qualityScore);
        expect(n.createdAt).toBe(mockNewsResponse.items[0].createdAt);

        expect(n.publisher.name).toBe(mockNewsResponse.items[0].publisher.name);
        expect(n.publisher.logoUrl).toBeNull();

        expect(n.relatedTickers).toHaveLength(1);
        expect(n.relatedTickers[0].id).toBe(mockNewsResponse.items[0].relatedTickers[0].id);
        expect(n.relatedTickers[0].name).toBe(mockNewsResponse.items[0].relatedTickers[0].name);
        expect(n.relatedTickers[0].symbol).toBe(mockNewsResponse.items[0].relatedTickers[0].symbol);

        expect(n.tickers).toHaveLength(1);
        expect(n.tickers![0].symbol).toBe("EQT");

        expect(n.categories?.id).toBe(mockNewsResponse.items[0].categories.id);
        expect(n.categories?.name).toBe(mockNewsResponse.items[0].categories.name);
        expect(n.categories?.categoryType).toBe(mockNewsResponse.items[0].categories.categoryType);

        expect(n.sectors?.id).toBe(mockNewsResponse.items[0].sectors.id);
        expect(n.sectors?.name).toBe(mockNewsResponse.items[0].sectors.name);

        expect(n.industries?.id).toBe(mockNewsResponse.items[0].industries.id);
        expect(n.industries?.name).toBe(mockNewsResponse.items[0].industries.name);

        expect(n.content?.title).toBe(mockNewsResponse.items[0].content.title);
        expect(n.content?.description).toBe(mockNewsResponse.items[0].content.description);
        expect(n.content?.content).toEqual(mockNewsResponse.items[0].content.content);
        expect(n.content?.summary).toEqual(mockNewsResponse.items[0].content.summary);
        expect(n.content?.investorInsight).toBe(mockNewsResponse.items[0].content.investorInsight);
      });

      test("does not send optional params when undefined", async () => {
        cli.request.mockResolvedValueOnce({ data: mockNewsResponse });

        await client.getNews(Region.Tr, Locale.Tr);

        const call = cli.request.mock.calls[0][0];
        expect(call.params).toEqual({
          region: Region.Tr,
          locale: Locale.Tr
        });
      });

      test("bubbles up request error", async () => {
        cli.request.mockRejectedValueOnce(new Error("Failed to fetch news"));

        await expect(
          client.getNews(Region.Tr, Locale.Tr, {
            newsType: NewsType.REUTERS,
            page: 0,
            size: 10,
            orderBy: NewsOrderBy.TIMESTAMP,
            orderByDirection: SortDirection.Desc,
          })
        ).rejects.toThrow("Failed to fetch news");

        expect(cli.request).toHaveBeenCalledTimes(1);
      });
    });

    describe("getNewsV2", () => {
      test("calls correct endpoint/params and matches raw response", async () => {
        cli.request.mockResolvedValueOnce({ data: mockNewsV2Response });

        const resp = await client.getNewsV2(Region.Tr, Locale.Tr, {
          lane: NewsLane.GLOBAL_MACRO,
          apiSource: "BBCBusiness,MarketWatch",
          newsType: NewsType.BRIEFS,
          page: 1,
          size: 10,
          orderBy: NewsOrderBy.TIMESTAMP,
          orderByDirection: SortDirection.Desc,
          symbols: "AAPL,MSFT",
          categoryIds: "1,2",
          sectorIds: "65533e047844ee7afe9941bf",
          industryIds: "65533e441fa5c7b58afa0944",
          qualityScoreMin: 7,
          qualityScoreMax: 10,
          timestampFrom: "2026-05-01",
          timestampTo: "2026-06-01",
        });

        expect(cli.request).toHaveBeenCalledTimes(1);
        const call = cli.request.mock.calls[0][0];

        expect(call.method).toBe("GET");
        expect(call.url).toBe("/api/v2/news");
        expect(call.params).toEqual({
          region: Region.Tr,
          locale: Locale.Tr,
          lane: NewsLane.GLOBAL_MACRO,
          apiSource: "BBCBusiness,MarketWatch",
          newsType: NewsType.BRIEFS,
          page: 1,
          size: 10,
          orderBy: NewsOrderBy.TIMESTAMP,
          orderByDirection: SortDirection.Desc,
          symbols: "AAPL,MSFT",
          categoryIds: "1,2",
          sectorIds: "65533e047844ee7afe9941bf",
          industryIds: "65533e441fa5c7b58afa0944",
          qualityScoreMin: 7,
          qualityScoreMax: 10,
          timestampFrom: "2026-05-01",
          timestampTo: "2026-06-01",
        });

        expect(resp.recordCount).toBe(352);
        expect(resp.items).toHaveLength(1);

        const n = resp.items[0];

        expect((n as any).relatedTickers).toBeUndefined();
        expect(n.id).toBe(mockNewsV2Response.items[0].id);
        expect(n.url).toBe(mockNewsV2Response.items[0].url);
      });

      test("does not send optional params when undefined", async () => {
        cli.request.mockResolvedValueOnce({ data: mockNewsV2Response });

        await client.getNewsV2(Region.Tr, Locale.Tr);

        const call = cli.request.mock.calls[0][0];
        expect(call.params).toEqual({
          region: Region.Tr,
          locale: Locale.Tr
        });
      });

      test("bubbles up request error", async () => {
        cli.request.mockRejectedValueOnce(new Error("Failed to fetch news v2"));

        await expect(
          client.getNewsV2(Region.Tr, Locale.Tr, {
            newsType: NewsType.REUTERS,
            page: 0,
            size: 10,
            orderBy: NewsOrderBy.TIMESTAMP,
            orderByDirection: SortDirection.Desc,
          })
        ).rejects.toThrow("Failed to fetch news v2");

        expect(cli.request).toHaveBeenCalledTimes(1);
      });
    });

    describe("streamNews", () => {
      test("calls correct endpoint/params and correctly yields stream entities", async () => {
        const eventsList: any[] = [];

        // Mock get response to return a readable stream
        const mockStreamData = [
          "data: " + JSON.stringify([{ url: "http://example.com/stream-news-1", publiser: { name: "test-publisher" } }]) + "\n\n",
          "data: " + JSON.stringify([{ url: "http://example.com/stream-news-2", publiser: { name: "test-publisher-2" } }]) + "\n\n",
        ];

        const mockAsyncIterator = {
          async *[Symbol.asyncIterator]() {
            for (const chunk of mockStreamData) {
              yield new TextEncoder().encode(chunk);
            }
          }
        };

        const axiosGetSpy = jest.spyOn(axios, 'get').mockResolvedValueOnce({
          data: mockAsyncIterator
        });

        const { events, cancel } = client.streamNews(Region.Us, Locale.Tr);

        for await (const newsList of events) {
          eventsList.push(newsList);
        }

        expect(axiosGetSpy).toHaveBeenCalledTimes(1);
        const callArgs = axiosGetSpy.mock.calls[0];
        expect(callArgs[0]).toBe(`${client["baseUrl"]}/api/v1/news/stream?locale=tr&region=us`);
        expect(callArgs[1]?.responseType).toBe('stream');

        expect(eventsList).toHaveLength(2);
        expect(eventsList[0][0].url).toBe("http://example.com/stream-news-1");
        expect(eventsList[1][0].url).toBe("http://example.com/stream-news-2");

        cancel();
        axiosGetSpy.mockRestore();
      });

      test("calls correct endpoint with optional parameters", async () => {
        const mockAsyncIterator = {
          async *[Symbol.asyncIterator]() {
            yield new TextEncoder().encode("data: " + JSON.stringify([]) + "\n\n");
          }
        };

        const axiosGetSpy = jest.spyOn(axios, 'get').mockResolvedValueOnce({
          data: mockAsyncIterator
        });

        const { events, cancel } = client.streamNews(Region.Us, Locale.En, ["tech"], ["AAPL"], ["category"], ["software"], NewsLane.GLOBAL_MACRO, ["BBCBusiness"]);

        for await (const _ of events) {
          break;
        }

        expect(axiosGetSpy).toHaveBeenCalledTimes(1);
        const callArgs = axiosGetSpy.mock.calls[0];
        expect(callArgs[0]).toBe(`${client["baseUrl"]}/api/v1/news/stream?locale=en&region=us&lane=global_macro&apiSource=BBCBusiness&sectorIds=tech&symbols=AAPL&categoryIds=category&industryIds=software`);

        cancel();
        axiosGetSpy.mockRestore();
      });
    });
  });
});
