import Consola from 'consola';
import { describe, it, vi, expect } from 'vitest';
import { createMocks } from 'node-mocks-http';

import GetSummary from './summary';
import { ConfigType } from '../../../../types/config';

const mockedGetConfig = vi.fn();

describe('DNS hole', () => {
  it('combine and return aggregated data', async () => {
    // arrange
    const { req, res } = createMocks({
      method: 'GET',
    });

    vi.mock('./../../../../tools/config/getConfig.ts', () => ({
      get getConfig() {
        return mockedGetConfig;
      },
    }));

    mockedGetConfig.mockReturnValue({
      apps: [
        {
          url: 'http://pi.hole',
          integration: {
            type: 'pihole',
            properties: [
              {
                field: 'apiKey',
                type: 'private',
                value: 'hf3829fj238g8',
              },
            ],
          },
        },
      ],
    } as ConfigType);
    const errorLogSpy = vi.spyOn(Consola, 'error');
    const warningLogSpy = vi.spyOn(Consola, 'warn');

    fetchMock.mockResponse((request) => {
      if (request.url === 'http://pi.hole/admin/api.php?summaryRaw&auth=hf3829fj238g8') {
        return JSON.stringify({
          domains_being_blocked: 780348,
          dns_queries_today: 36910,
          ads_blocked_today: 9700,
          ads_percentage_today: 26.280142,
          unique_domains: 6217,
          queries_forwarded: 12943,
          queries_cached: 13573,
          clients_ever_seen: 20,
          unique_clients: 17,
          dns_queries_all_types: 36910,
          reply_UNKNOWN: 947,
          reply_NODATA: 3313,
          reply_NXDOMAIN: 1244,
          reply_CNAME: 5265,
          reply_IP: 25635,
          reply_DOMAIN: 97,
          reply_RRNAME: 4,
          reply_SERVFAIL: 28,
          reply_REFUSED: 0,
          reply_NOTIMP: 0,
          reply_OTHER: 0,
          reply_DNSSEC: 0,
          reply_NONE: 0,
          reply_BLOB: 377,
          dns_queries_all_replies: 36910,
          privacy_level: 0,
          status: 'enabled',
          gravity_last_updated: {
            file_exists: true,
            absolute: 1682216493,
            relative: {
              days: 5,
              hours: 17,
              minutes: 52,
            },
          },
        });
      }

      return Promise.reject(new Error(`Bad url: ${request.url}`));
    });

    // Act
    await GetSummary(req, res);

    // Assert
    expect(res._getStatusCode()).toBe(200);
    expect(res.finished).toBe(true);
    expect(JSON.parse(res._getData())).toEqual({
      adsBlockedToday: 9700,
      adsBlockedTodayPercentage: 0.26280140883229475,
      dnsQueriesToday: 36910,
      domainsBeingBlocked: 780348,
      status: [
        {
          status: 'enabled',
        },
      ],
    });

    expect(errorLogSpy).not.toHaveBeenCalled();
    expect(warningLogSpy).not.toHaveBeenCalled();

    errorLogSpy.mockRestore();
  });

  it('combine and return aggregated data when multiple instances', async () => {
    // arrange
    const { req, res } = createMocks({
      method: 'GET',
    });

    vi.mock('./../../../../tools/config/getConfig.ts', () => ({
      get getConfig() {
        return mockedGetConfig;
      },
    }));

    mockedGetConfig.mockReturnValue({
      apps: [
        {
          id: 'app1',
          url: 'http://pi.hole',
          integration: {
            type: 'pihole',
            properties: [
              {
                field: 'apiKey',
                type: 'private',
                value: 'hf3829fj238g8',
              },
            ],
          },
        },
        {
          id: 'app2',
          url: 'http://pi2.hole',
          integration: {
            type: 'pihole',
            properties: [
              {
                field: 'apiKey',
                type: 'private',
                value: 'ayaka',
              },
            ],
          },
        },
      ],
    } as ConfigType);
    const errorLogSpy = vi.spyOn(Consola, 'error');
    const warningLogSpy = vi.spyOn(Consola, 'warn');

    fetchMock.mockResponse((request) => {
      if (request.url === 'http://pi.hole/admin/api.php?summaryRaw&auth=hf3829fj238g8') {
        return JSON.stringify({
          domains_being_blocked: 3,
          dns_queries_today: 8,
          ads_blocked_today: 5,
          ads_percentage_today: 26,
          unique_domains: 4,
          queries_forwarded: 2,
          queries_cached: 2,
          clients_ever_seen: 2,
          unique_clients: 3,
          dns_queries_all_types: 3,
          reply_UNKNOWN: 2,
          reply_NODATA: 3,
          reply_NXDOMAIN: 5,
          reply_CNAME: 6,
          reply_IP: 5,
          reply_DOMAIN: 3,
          reply_RRNAME: 2,
          reply_SERVFAIL: 2,
          reply_REFUSED: 0,
          reply_NOTIMP: 0,
          reply_OTHER: 0,
          reply_DNSSEC: 0,
          reply_NONE: 0,
          reply_BLOB: 1,
          dns_queries_all_replies: 36910,
          privacy_level: 0,
          status: 'enabled',
          gravity_last_updated: {
            file_exists: true,
            absolute: 1682216493,
            relative: {
              days: 5,
              hours: 17,
              minutes: 52,
            },
          },
        });
      }

      if (request.url === 'http://pi2.hole/admin/api.php?summaryRaw&auth=ayaka') {
        return JSON.stringify({
          domains_being_blocked: 1,
          dns_queries_today: 3,
          ads_blocked_today: 2,
          ads_percentage_today: 47,
          unique_domains: 4,
          queries_forwarded: 4,
          queries_cached: 2,
          clients_ever_seen: 2,
          unique_clients: 2,
          dns_queries_all_types: 1,
          reply_UNKNOWN: 3,
          reply_NODATA: 2,
          reply_NXDOMAIN: 1,
          reply_CNAME: 3,
          reply_IP: 2,
          reply_DOMAIN: 97,
          reply_RRNAME: 4,
          reply_SERVFAIL: 28,
          reply_REFUSED: 0,
          reply_NOTIMP: 0,
          reply_OTHER: 0,
          reply_DNSSEC: 0,
          reply_NONE: 0,
          reply_BLOB: 2,
          dns_queries_all_replies: 4,
          privacy_level: 0,
          status: 'disabled',
          gravity_last_updated: {
            file_exists: true,
            absolute: 1682216493,
            relative: {
              days: 5,
              hours: 17,
              minutes: 52,
            },
          },
        });
      }

      return Promise.reject(new Error(`Bad url: ${request.url}`));
    });

    // Act
    await GetSummary(req, res);

    // Assert
    expect(res._getStatusCode()).toBe(200);
    expect(res.finished).toBe(true);
    expect(JSON.parse(res._getData())).toStrictEqual({
      adsBlockedToday: 7,
      adsBlockedTodayPercentage: 0.6363636363636364,
      dnsQueriesToday: 11,
      domainsBeingBlocked: 4,
      status: [
        {
          appId: 'app1',
          status: 'enabled',
        },
        {
          appId: 'app2',
          status: 'disabled',
        },
      ],
    });

    expect(errorLogSpy).not.toHaveBeenCalled();
    expect(warningLogSpy).not.toHaveBeenCalled();

    errorLogSpy.mockRestore();
  });
});
