import { throwOnInvliadResponse } from './http';

export interface CampaignOptions {
  /**
   * An access token that allows access to the Campaign API for the current user.
   */
  accessToken: string;

  /**
   * Optionally set a different REST API endpoint that should be used by this library.
   */
  campaignApiEndpoint?: string;
}

export interface Notification {
  title: string | null;
  body: string | null;
}

export type WifiConfigType = 'SURFTIME' | 'TIMESPAN';

export interface Campaign {
  campaignUrl: string;
  required: boolean;
  notification: Notification;
  timeUntilNextCampaignToWatch: number;

  /**
   * Type of wifi mode
   * If TIMESPAN -> Campaigns will be required to watch each time after defined time period to prolong access to wifi (for instance every 5 mins)
   * If SURFTIME -> Campaigns will be sent depending on connection session (eg 5 mins set -> after connection to access point session started and tracking
   * -> if user stop connection or leaving access point and used 3 mins from 5 -> timer stop tracking/campaign not required to watch -> next time user connecting
   * to the access point -> timer continue track time and sending campaign to prolong access to wifi after last 2 mins of limit will be reached)
   */
  type: WifiConfigType;
}

export interface CampaignService {
  /**
   * Get the next campaign that should be shown to the user.
   * @param deviceId A string that uniquely identifies this device.
   * @returns A promise that resolves to a `Campaign` object if the
   * campaign was fetched successfully. Rejects if fetching the next
   * campaign failed for any reason.
   * The `Campaign` object contains a flag `Campaign.required` that will
   * be `true` if the user needs to watch a campaign. This will be the case
   * if the timespan since watching the last campaign is greater than the
   * configured timespan. If `Campaign.required` is `false` then it is not
   * necessary to watch a campaign.
   */
  getNextCampaign(deviceId: string): Promise<Campaign>;

  /**
   * Method for checking when campaign view should be closed
   * @param campaignUrl A string to campaign
   */
  shouldCloseCampaignView(campaignUrl: string): boolean;
}

export class CampaignServiceImpl implements CampaignService {
  private readonly options: Required<CampaignOptions>;

  constructor(campaignOptions: CampaignOptions) {
    this.throwIfInvalidOptions(campaignOptions);
    this.options = this.mergeWithDefaultOptions(campaignOptions);
  }

  public async getNextCampaign(deviceId: string): Promise<Campaign> {
    const endpoint = `${this.options.campaignApiEndpoint}/api/v1/campaigns/next?deviceId=${deviceId}`;

    var response = await fetch(endpoint, {
      method: 'GET',
      headers: {
        Authorization: `Bearer ${this.options.accessToken}`,
        Accept: 'application/json',
      },
    });

    const body: Campaign = await response.json();

    throwOnInvliadResponse(response.status, body);

    // copy the values to ensure that the response always contains the required properties
    return {
      campaignUrl: body?.campaignUrl,
      required: body?.required ?? false,
      notification: {
        title: body?.notification?.title,
        body: body?.notification?.body,
      },
      type: body?.type,
      timeUntilNextCampaignToWatch: body?.timeUntilNextCampaignToWatch || 0,
    };
  }

  public shouldCloseCampaignView(campaignUrl: string): boolean {
    return campaignUrl.includes('close_campaign=true');
  }

  private mergeWithDefaultOptions(
    options: CampaignOptions
  ): Required<CampaignOptions> {
    return {
      accessToken: options.accessToken,
      campaignApiEndpoint:
        options?.campaignApiEndpoint ||
        'https://api.wifi-connect.campaign-manager.ads.abl-solutions.io',
    };
  }

  private throwIfInvalidOptions(options: CampaignOptions) {
    if (!options) {
      throw new Error('CampaignOptions must be not null.');
    }

    if (!options.accessToken) {
      throw new Error('CampaignOptions.accessToken must be not null.');
    }

    if (options.accessToken === '') {
      throw new Error('CampaignOptions.accessToken must be not empty.');
    }
  }
}
