// Copyright (c) 2017 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import React from 'react';
import { css } from 'emotion';
import cx from 'classnames';

import AccordianKeyValues from './AccordianKeyValues';
import AccordianLogs from './AccordianLogs';
import AccordianText from './AccordianText';
import DetailState from './DetailState';
import { formatDuration } from '../utils';
import CopyIcon from '../../common/CopyIcon';
import LabeledList from '../../common/LabeledList';

import { TNil } from '../../types';
import { TraceKeyValuePair, TraceLink, TraceLog, TraceSpan } from '../../types/trace';
import AccordianReferences from './AccordianReferences';
import { autoColor, createStyle, Theme, useTheme } from '../../Theme';
import { UIDivider } from '../../uiElementsContext';
import { ubFlex, ubFlexAuto, ubItemsCenter, ubM0, ubMb1, ubMy1, ubTxRightAlign } from '../../uberUtilityStyles';
import { DataLinkButton, TextArea } from '@grafana/ui';
import { CreateSpanLink } from '../types';

const getStyles = createStyle((theme: Theme) => {
  return {
    divider: css`
      label: divider;
      background: ${autoColor(theme, '#ddd')};
    `,
    dividerVertical: css`
      label: dividerVertical;
      display: block;
      height: 1px;
      width: 100%;
      margin: 24px 0;
      clear: both;
      vertical-align: middle;
      position: relative;
      top: -0.06em;
    `,
    debugInfo: css`
      label: debugInfo;
      display: block;
      letter-spacing: 0.25px;
      margin: 0.5em 0 -0.75em;
      text-align: right;
    `,
    debugLabel: css`
      label: debugLabel;
      &::before {
        color: ${autoColor(theme, '#bbb')};
        content: attr(data-label);
      }
    `,
    debugValue: css`
      label: debugValue;
      background-color: inherit;
      border: none;
      color: ${autoColor(theme, '#888')};
      cursor: pointer;
      &:hover {
        color: ${autoColor(theme, '#333')};
      }
    `,
    AccordianWarnings: css`
      label: AccordianWarnings;
      background: ${autoColor(theme, '#fafafa')};
      border: 1px solid ${autoColor(theme, '#e4e4e4')};
      margin-bottom: 0.25rem;
    `,
    AccordianWarningsHeader: css`
      label: AccordianWarningsHeader;
      background: ${autoColor(theme, '#fff7e6')};
      padding: 0.25rem 0.5rem;
      &:hover {
        background: ${autoColor(theme, '#ffe7ba')};
      }
    `,
    AccordianWarningsHeaderOpen: css`
      label: AccordianWarningsHeaderOpen;
      border-bottom: 1px solid ${autoColor(theme, '#e8e8e8')};
    `,
    AccordianWarningsLabel: css`
      label: AccordianWarningsLabel;
      color: ${autoColor(theme, '#d36c08')};
    `,
    Textarea: css`
      word-break: break-all;
      white-space: pre;
    `,
  };
});

type SpanDetailProps = {
  detailState: DetailState;
  linksGetter: ((links: TraceKeyValuePair[], index: number) => TraceLink[]) | TNil;
  logItemToggle: (spanID: string, log: TraceLog) => void;
  logsToggle: (spanID: string) => void;
  processToggle: (spanID: string) => void;
  span: TraceSpan;
  tagsToggle: (spanID: string) => void;
  traceStartTime: number;
  warningsToggle: (spanID: string) => void;
  stackTracesToggle: (spanID: string) => void;
  referencesToggle: (spanID: string) => void;
  focusSpan: (uiFind: string) => void;
  createSpanLink?: CreateSpanLink;
};

export default function SpanDetail(props: SpanDetailProps) {
  const {
    detailState,
    linksGetter,
    logItemToggle,
    logsToggle,
    processToggle,
    span,
    tagsToggle,
    traceStartTime,
    warningsToggle,
    stackTracesToggle,
    referencesToggle,
    focusSpan,
    createSpanLink,
  } = props;
  const {
    isTagsOpen,
    isProcessOpen,
    logs: logsState,
    isWarningsOpen,
    isReferencesOpen,
    isStackTracesOpen,
  } = detailState;
  const {
    operationName,
    process,
    duration,
    relativeStartTime,
    spanID,
    logs,
    tags,
    warnings,
    references,
    stackTraces,
  } = span;
  const overviewItems = [
    {
      key: 'svc',
      label: 'Service:',
      value: process.serviceName,
    },
    {
      key: 'duration',
      label: 'Duration:',
      value: formatDuration(duration),
    },
    {
      key: 'start',
      label: 'Start Time:',
      value: formatDuration(relativeStartTime),
    },
  ];
  const deepLinkCopyText = `${window.location.origin}${window.location.pathname}?uiFind=${spanID}`;
  const styles = getStyles(useTheme());
  const link = createSpanLink?.(span);

  return (
    <div>
      <div className={cx(ubFlex, ubItemsCenter, ubMb1)}>
        <h2 className={cx(ubFlexAuto, ubM0)}>{operationName}</h2>
        <LabeledList className={ubTxRightAlign} dividerClassName={styles.divider} items={overviewItems} />
      </div>
      {link ? (
        <DataLinkButton link={{ ...link, title: 'Logs for this span' } as any} buttonProps={{ icon: 'gf-logs' }} />
      ) : null}
      <UIDivider className={cx(styles.divider, styles.dividerVertical, ubMy1)} />
      <div>
        <div>
          <AccordianKeyValues
            data={tags}
            label="Tags"
            linksGetter={linksGetter}
            isOpen={isTagsOpen}
            onToggle={() => tagsToggle(spanID)}
          />
          {process.tags && (
            <AccordianKeyValues
              className={ubMb1}
              data={process.tags}
              label="Process"
              linksGetter={linksGetter}
              isOpen={isProcessOpen}
              onToggle={() => processToggle(spanID)}
            />
          )}
        </div>
        {logs && logs.length > 0 && (
          <AccordianLogs
            linksGetter={linksGetter}
            logs={logs}
            isOpen={logsState.isOpen}
            openedItems={logsState.openedItems}
            onToggle={() => logsToggle(spanID)}
            onItemToggle={(logItem) => logItemToggle(spanID, logItem)}
            timestamp={traceStartTime}
          />
        )}
        {warnings && warnings.length > 0 && (
          <AccordianText
            className={styles.AccordianWarnings}
            headerClassName={styles.AccordianWarningsHeader}
            label={<span className={styles.AccordianWarningsLabel}>Warnings</span>}
            data={warnings}
            isOpen={isWarningsOpen}
            onToggle={() => warningsToggle(spanID)}
          />
        )}
        {stackTraces && stackTraces.length && (
          <AccordianText
            label="Stack trace"
            data={stackTraces}
            isOpen={isStackTracesOpen}
            TextComponent={(textComponentProps) => {
              let text;
              if (textComponentProps.data?.length > 1) {
                text = textComponentProps.data
                  .map((stackTrace, index) => `StackTrace ${index + 1}:\n${stackTrace}`)
                  .join('\n');
              } else {
                text = textComponentProps.data?.[0];
              }
              return (
                <TextArea
                  className={styles.Textarea}
                  style={{ cursor: 'unset' }}
                  readOnly
                  cols={10}
                  rows={10}
                  value={text}
                />
              );
            }}
            onToggle={() => stackTracesToggle(spanID)}
          />
        )}
        {references && references.length > 1 && (
          <AccordianReferences
            data={references}
            isOpen={isReferencesOpen}
            onToggle={() => referencesToggle(spanID)}
            focusSpan={focusSpan}
          />
        )}
        <small className={styles.debugInfo}>
          <span className={styles.debugLabel} data-label="SpanID:" /> {spanID}
          <CopyIcon
            copyText={deepLinkCopyText}
            icon="link"
            placement="topRight"
            tooltipTitle="Copy deep link to this span"
          />
        </small>
      </div>
    </div>
  );
}
