/*
 * Copyright OpenSearch Contributors
 * SPDX-License-Identifier: Apache-2.0
 */

import {
  EuiFlexGroup,
  EuiFlexItem,
  EuiFormRow,
  EuiLink,
  EuiSelect,
  EuiSelectOption,
  EuiSpacer,
  EuiText,
  EuiBadge,
} from '@elastic/eui';
import moment from 'moment';
import { PeriodSchedule } from '../../models/interfaces';
import React from 'react';
import { DEFAULT_EMPTY_DATA, scheduleUnitText } from './constants';
import {
  RuleItem,
  RuleItemInfo,
} from '../pages/CreateDetector/components/DefineDetector/components/DetectionRules/types/interfaces';
import { compile, TopLevelSpec } from 'vega-lite';
import { parse, View } from 'vega/build-es5/vega.js';
import { expressionInterpreter as vegaExpressionInterpreter } from 'vega-interpreter/build/vega-interpreter';
import { RuleInfo } from '../../server/models/interfaces';
import { NotificationsStart } from 'opensearch-dashboards/public';
import { OpenSearchService } from '../services';
import { ruleSeverity, ruleTypes } from '../pages/Rules/utils/constants';
import { Handler } from 'vega-tooltip';
import _ from 'lodash';

export const parseStringsToOptions = (strings: string[]) => {
  return strings.map((str) => ({ id: str, label: str }));
};

export const renderTime = (time: number | string) => {
  const momentTime = moment(time);
  if (time && momentTime.isValid()) return momentTime.format('MM/DD/YY h:mm a');
  return DEFAULT_EMPTY_DATA;
};

export function createTextDetailsGroup(data: { label: string; content: any; url?: string }[]) {
  const createFormRow = (label: string, content: string, url?: string) => {
    const dataTestSubj = label.toLowerCase().replace(/ /g, '-');
    return (
      <EuiFormRow fullWidth label={<EuiText color={'subdued'}>{label}</EuiText>}>
        {url ? (
          <EuiLink data-test-subj={`text-details-group-content-${dataTestSubj}`}>
            {content ?? DEFAULT_EMPTY_DATA}
          </EuiLink>
        ) : (
          <EuiText data-test-subj={`text-details-group-content-${dataTestSubj}`}>
            {content ?? DEFAULT_EMPTY_DATA}
          </EuiText>
        )}
      </EuiFormRow>
    );
  };
  return data.length <= 1 ? (
    !data.length ? null : (
      createFormRow(data[0].label, data[0].content, data[0].url)
    )
  ) : (
    <>
      <EuiFlexGroup className={'detailsFormRow'}>
        {data.map(({ label, content, url }, index) => {
          return (
            <EuiFlexItem key={index} grow={true}>
              {createFormRow(label, content, url)}
            </EuiFlexItem>
          );
        })}
      </EuiFlexGroup>
      <EuiSpacer size={'xl'} />
    </>
  );
}

export const pluralize = (count: number, singular: string, plural = singular + 's') => {
  return [1, -1].includes(Number(count)) ? singular : plural;
};

export function parseSchedule({ period: { interval, unit } }: PeriodSchedule) {
  return `Every ${interval} ${pluralize(interval, scheduleUnitText[unit])}`;
}

export function translateToRuleItems(
  prePackagedRules: RuleInfo[],
  customRules: RuleInfo[],
  detectorType: string,
  isEnabled: (rule: RuleInfo) => boolean
) {
  let ruleItemInfos: RuleItemInfo[] = prePackagedRules.map((rule) => ({
    ...rule,
    enabled: isEnabled(rule),
    prePackaged: true,
  }));

  ruleItemInfos = ruleItemInfos.concat(
    customRules.map((rule) => ({
      ...rule,
      enabled: isEnabled(rule),
      prePackaged: false,
    }))
  );

  return ruleItemInfosToItems(detectorType, ruleItemInfos);
}

export function ruleItemInfosToItems(
  detectorType: string,
  ruleItemsInfo: RuleItemInfo[]
): RuleItem[] {
  if (ruleItemsInfo) {
    return ruleItemsInfo.map((itemInfo) => ({
      id: itemInfo._id,
      active: itemInfo.enabled,
      description: itemInfo._source.description,
      library: itemInfo.prePackaged ? 'Sigma' : 'Custom',
      logType: detectorType.toLowerCase(),
      name: itemInfo._source.title,
      severity: itemInfo._source.level,
      ruleInfo: itemInfo,
    }));
  }

  return [];
}

export function getUpdatedEnabledRuleIds(
  existingEnabledIds: Set<string>,
  ruleId: string,
  isActive: boolean
) {
  let newEnabledIds;
  // 1. not enabled previously
  const wasActive = existingEnabledIds.has(ruleId);
  if (wasActive && !isActive) {
    const clonedIds = new Set(existingEnabledIds);
    clonedIds.delete(ruleId);
    newEnabledIds = [...clonedIds];
  }
  // 2. enabled previously and now disabled
  else if (!wasActive && isActive) {
    const clonedIds = new Set(existingEnabledIds);
    clonedIds.add(ruleId);
    newEnabledIds = [...clonedIds];
  }

  return newEnabledIds;
}

export function renderVisualization(spec: TopLevelSpec, containerId: string) {
  let view;

  try {
    renderVegaSpec(compile({ ...spec, width: 'container', height: 400 }).spec).catch((err: Error) =>
      console.error(err)
    );
  } catch (error) {
    console.log(error);
  }

  function renderVegaSpec(spec: {}) {
    let chartColoredItems: any[] = [];
    const handler = new Handler({
      formatTooltip: (value, sanitize) => {
        let tooltipData = { ...value };
        let values = Object.entries(tooltipData);
        if (!values.length) return '';
        const tooltipItem = chartColoredItems.filter((groupItem: any) =>
          _.isEqual(groupItem.tooltip, tooltipData)
        );
        const color = tooltipItem.length
          ? tooltipItem[0].fill || tooltipItem[0].stroke
          : 'transparent';

        const firstItem = values.pop() || ['', ''];

        let rowData = '';
        values.forEach((item: any) => {
          rowData += `
            <tr>
              <td>${sanitize(item[0])}</td>
              <td>${sanitize(item[1])}</td>
            </tr>
          `;
        });

        return `
          <div class="vg-tooltip-innerContainer">
            <div class="vg-tooltip-header">
              <table>
                <tr>
                  <td><div class="vg-tooltip-color" style="background-color: ${color}"></div></td>
                  <td>${sanitize(firstItem[0])}</td>
                  <td>${sanitize(firstItem[1])}</td>
                </tr>
              </table>
            </div>
            <div class="vg-tooltip-body">
             <table>${rowData}</table>
            </div>
          </div>
        `;
      },
    });
    view = new View(parse(spec, null, { expr: vegaExpressionInterpreter }), {
      renderer: 'canvas', // renderer (canvas or svg)
      container: `#${containerId}`, // parent DOM container
      hover: true, // enable hover processing
    });
    view.tooltip(handler.call);
    return view.runAsync().then((view: any) => {
      const items = view.scenegraph().root.items[0].items || [];
      const groups = items.filter(
        (item: any) => item.name && item.name.match(/^(layer_).*(_marks)$/)
      );
      for (let item of groups) {
        chartColoredItems = chartColoredItems.concat(item.items);
      }
    });
  }
}

export function createSelectComponent(
  options: EuiSelectOption[],
  value: string,
  id: string,
  onChange: React.ChangeEventHandler<HTMLSelectElement>
) {
  return (
    <EuiFlexGroup justifyContent="flexEnd" alignItems="center">
      <EuiFlexItem grow={false}>
        <EuiSelect id={id} options={options} value={value} onChange={onChange} prepend="Group by" />
      </EuiFlexItem>
    </EuiFlexGroup>
  );
}

export const capitalizeFirstLetter = (str: string) => {
  if (!str) {
    return '';
  }

  return `${str.charAt(0).toUpperCase()}${str.slice(1).toLowerCase()}`;
};

// A helper function that shows toast messages for backend errors.
export const errorNotificationToast = (
  notifications: NotificationsStart | null,
  actionName: string,
  objectName: string,
  errorMessage: string = '',
  displayTime: number = 5000 // 5 seconds; default is 10 seconds
) => {
  const message = `Failed to ${actionName} ${objectName}:`;
  console.error(message, errorMessage);
  notifications?.toasts.addDanger({
    title: message,
    text: errorMessage,
    toastLifeTimeMs: displayTime,
  });
};

// A helper function that shows toast messages for successful actions.
export const successNotificationToast = (
  notifications: NotificationsStart | null,
  actionName: string,
  objectName: string,
  successMessage: string = '',
  displayTime: number = 5000 // 5 seconds; default is 10 seconds
) => {
  notifications?.toasts.addSuccess({
    title: `Successfully ${actionName} ${objectName}`,
    text: successMessage,
    toastLifeTimeMs: displayTime,
  });
};

export const getPlugins = async (opensearchService: OpenSearchService) => {
  try {
    const pluginsResponse = await opensearchService.getPlugins();
    if (pluginsResponse.ok) {
      return pluginsResponse.response.map((plugin) => plugin.component);
    } else {
      return [];
    }
  } catch (e) {
    return [];
  }
};

export const formatRuleType = (matchingRuleType: string) => {
  return (
    ruleTypes.find((ruleType) => ruleType.label.toLowerCase() === matchingRuleType.toLowerCase())
      ?.label || DEFAULT_EMPTY_DATA
  );
};

export const getSeverityBadge = (severity: string) => {
  const severityLevel = ruleSeverity.find((sev) => sev.value === severity);
  return (
    <EuiBadge color={severityLevel?.color.background} style={{ color: severityLevel?.color.text }}>
      {severity || DEFAULT_EMPTY_DATA}
    </EuiBadge>
  );
};