import type { RequestOptions } from 'https';
import Request, { BasicAuth, RequestData, PushRequestData } from './request';
import { Region, RegionUS } from './regions';
import { isEmpty, isIdentifierType, MissingParamError } from './utils';
import { IdentifierType } from './types';

type TrackDefaults = RequestOptions & { region: Region; url?: string };

export class TrackClient {
  siteid: BasicAuth['siteid'];
  apikey: BasicAuth['apikey'];
  defaults: TrackDefaults;
  request: Request;
  trackRoot: string;

  constructor(siteid: BasicAuth['siteid'], apikey: BasicAuth['apikey'], defaults: Partial<TrackDefaults> = {}) {
    if (defaults.region && !(defaults.region instanceof Region)) {
      throw new Error('region must be one of Regions.US or Regions.EU');
    }

    this.siteid = siteid;
    this.apikey = apikey;
    this.defaults = { ...defaults, region: defaults.region || RegionUS };
    this.request = new Request({ siteid: this.siteid, apikey: this.apikey }, this.defaults);

    this.trackRoot = this.defaults.url ? this.defaults.url : this.defaults.region.trackUrl;
  }

  identify(customerId: string | number, data: RequestData = {}) {
    if (isEmpty(customerId)) {
      throw new MissingParamError('customerId');
    }

    return this.request.put(`${this.trackRoot}/customers/${encodeURIComponent(customerId)}`, data);
  }

  destroy(customerId: string | number) {
    if (isEmpty(customerId)) {
      throw new MissingParamError('customerId');
    }

    return this.request.destroy(`${this.trackRoot}/customers/${encodeURIComponent(customerId)}`);
  }

  suppress(customerId: string | number) {
    if (isEmpty(customerId)) {
      throw new MissingParamError('customerId');
    }

    return this.request.post(`${this.trackRoot}/customers/${encodeURIComponent(customerId)}/suppress`);
  }

  unsuppress(customerId: string | number) {
    if (isEmpty(customerId)) {
      throw new MissingParamError('customerId');
    }

    return this.request.post(`${this.trackRoot}/customers/${encodeURIComponent(customerId)}/unsuppress`);
  }

  track(customerId: string | number, data: RequestData = {}) {
    if (isEmpty(customerId)) {
      throw new MissingParamError('customerId');
    }

    if (isEmpty(data.name)) {
      throw new MissingParamError('data.name');
    }

    return this.request.post(`${this.trackRoot}/customers/${encodeURIComponent(customerId)}/events`, data);
  }

  trackAnonymous(anonymousId: string | number, data: RequestData = {}) {
    if (isEmpty(data.name)) {
      throw new MissingParamError('data.name');
    }

    let payload = { ...data };

    if (!isEmpty(anonymousId)) {
      payload['anonymous_id'] = anonymousId;
    }

    return this.request.post(`${this.trackRoot}/events`, payload);
  }

  trackPageView(customerId: string | number, path: string) {
    if (isEmpty(customerId)) {
      throw new MissingParamError('customerId');
    }

    if (isEmpty(path)) {
      throw new MissingParamError('path');
    }

    return this.request.post(`${this.trackRoot}/customers/${encodeURIComponent(customerId)}/events`, {
      type: 'page',
      name: path,
    });
  }

  trackPush(data: PushRequestData = {}) {
    return this.request.post(`${this.trackRoot}/push/events`, data);
  }

  addDevice(customerId: string | number, device_id: string, platform: string, data: Record<string, any> = {}) {
    if (isEmpty(customerId)) {
      throw new MissingParamError('customerId');
    }

    if (isEmpty(device_id)) {
      throw new MissingParamError('device_id');
    }

    if (isEmpty(platform)) {
      throw new MissingParamError('platform');
    }

    let { last_used, ...attributes } = data;

    return this.request.put(`${this.trackRoot}/customers/${encodeURIComponent(customerId)}/devices`, {
      device: {
        id: device_id,
        platform,
        ...(last_used && { last_used }),
        ...(Object.keys(attributes).length && { attributes }),
      },
    });
  }

  deleteDevice(customerId: string | number, deviceToken: string | number) {
    if (isEmpty(customerId)) {
      throw new MissingParamError('customerId');
    }

    if (isEmpty(deviceToken)) {
      throw new MissingParamError('deviceToken');
    }

    return this.request.destroy(
      `${this.trackRoot}/customers/${encodeURIComponent(customerId)}/devices/${encodeURIComponent(deviceToken)}`,
    );
  }

  mergeCustomers(
    primaryIdType: IdentifierType,
    primaryId: string | number,
    secondaryIdType: IdentifierType,
    secondaryId: string | number,
  ) {
    if (isEmpty(primaryId)) {
      throw new MissingParamError('primaryId');
    }

    if (isEmpty(secondaryId)) {
      throw new MissingParamError('secondaryId');
    }

    if (!isIdentifierType(primaryIdType) || !isIdentifierType(secondaryIdType)) {
      throw new Error('primaryIdType and secondaryIdType must be one of "id", "cio_id", or "email"');
    }

    return this.request.post(`${this.trackRoot}/merge_customers`, {
      primary: {
        [primaryIdType]: primaryId,
      },
      secondary: {
        [secondaryIdType]: secondaryId,
      },
    });
  }
}
