/**
 * Copyright 2024 Phenix Real Time Solutions, Inc. Confidential and Proprietary. All Rights Reserved.
 */
import config from '../../config';
import {LoggingLevel} from '../logger/Logger';
import TelemetryConfiguration from './TelemetryConfiguration';

// Extend Window interface to include custom properties
declare global {
  interface Window {
    __phenixPageLoadTime?: number;
    __pageLoadTime?: number;
  }
}

const requestSizeLimit = 8192;
const pageLoadTime = window.__phenixPageLoadTime || window.__pageLoadTime || Date.now();

interface ILogItem {
  timestamp: string;
  tenancy: string;
  level: string;
  category: string;
  message: string;
  sessionId: string;
  userId: string;
  version: string;
  environment: string;
  fullQualifiedName: string;
  source: string;
  runtime: number;
}

export default class TelemetryService {
  private readonly _telemetryConfiguration: TelemetryConfiguration;

  private _logs: Array<ILogItem> = [];
  private _isSending: boolean = false;
  private _domain = location.hostname;

  constructor(telemetryConfiguration: TelemetryConfiguration) {
    this._telemetryConfiguration = telemetryConfiguration;
  }

  push(logLevel: LoggingLevel, message: string, category: string, timestamp: Date): void {
    const now = Date.now();
    const runtime = (now - pageLoadTime) / 1000;
    const logRecord = {
      timestamp: timestamp.toISOString(),
      tenancy: this._telemetryConfiguration.tenancy,
      userId: this._telemetryConfiguration.userId,
      level: LoggingLevel[logLevel],
      runtime,
      category,
      message,
      sessionId: this._telemetryConfiguration.sessionId,
      version: config.controlCenterVersion,
      environment: this._telemetryConfiguration.environment,
      fullQualifiedName: this._domain,
      source: `Portal (${this._telemetryConfiguration.browser})`
    } as ILogItem;

    if (logLevel < LoggingLevel.Error) {
      this._logs.push(logRecord);
    } else {
      this._logs.unshift(logRecord);
    }

    // @ts-expect-error: Unused variable intentionally
    const ignored = this.sendLogsIfAble();
  }

  private async sendLogs(logMessages: Array<ILogItem>): Promise<Response | void> {
    const formData = new FormData();

    formData.append('jsonBody', JSON.stringify({records: logMessages}));

    return await fetch(this._telemetryConfiguration.url, {
      method: 'POST',
      body: formData
    });
  }

  private async sendLogsIfAble(): Promise<Response | void> {
    if (this._logs.length <= 0 || this._isSending) {
      return;
    }

    let numberOfLogsToSend = 0;
    let sizeOfLogsToSend = 0;

    this._isSending = true;

    const getLogSize = (log: ILogItem): number => Object.values(log).reduce((sum, item) => sum + (item ? `${item}`.length : 0), 0);

    while (this._logs.length > numberOfLogsToSend && getLogSize(this._logs[numberOfLogsToSend]) + sizeOfLogsToSend < requestSizeLimit) {
      sizeOfLogsToSend += getLogSize(this._logs[numberOfLogsToSend]);
      numberOfLogsToSend++;
    }

    if (!numberOfLogsToSend) {
      this._logs[numberOfLogsToSend].message = this._logs[numberOfLogsToSend].message.substring(
        0,
        getLogSize(this._logs[numberOfLogsToSend]) + (requestSizeLimit - getLogSize(this._logs[numberOfLogsToSend]))
      );
      numberOfLogsToSend = 1;
    }

    const logMessages = this._logs.slice(0, numberOfLogsToSend);

    this._logs = this._logs.slice(numberOfLogsToSend);

    return this.sendLogs(logMessages)
      .then(response => {
        this._isSending = false;

        // @ts-expect-error: Unused variable intentionally

        const ignored = this.sendLogsIfAble();

        return response;
      })
      .catch(() => {
        this._isSending = false;

        // @ts-expect-error: Unused variable intentionally

        const ignored = this.sendLogsIfAble();
      });
  }
}
