import {Component, Fragment} from 'react';
import {browserHistory} from 'react-router';
import styled from '@emotion/styled';
import {Location, LocationDescriptorObject} from 'history';

import GridEditable, {
  COL_WIDTH_UNDEFINED,
  GridColumn,
} from 'sentry/components/gridEditable';
import SortLink from 'sentry/components/gridEditable/sortLink';
import Link from 'sentry/components/links/link';
import Pagination from 'sentry/components/pagination';
import Tag from 'sentry/components/tag';
import {IconStar} from 'sentry/icons';
import {t} from 'sentry/locale';
import {Organization, Project} from 'sentry/types';
import {trackAnalyticsEvent} from 'sentry/utils/analytics';
import EventView, {
  EventData,
  EventsMetaType,
  isFieldSortable,
} from 'sentry/utils/discover/eventView';
import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
import {fieldAlignment, getAggregateAlias, Sort} from 'sentry/utils/discover/fields';
import {WebVital} from 'sentry/utils/fields';
import VitalsDetailsTableQuery, {
  TableData,
  TableDataRow,
} from 'sentry/utils/performance/vitals/vitalsDetailsTableQuery';
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
import CellAction, {Actions, updateQuery} from 'sentry/views/eventsV2/table/cellAction';
import {TableColumn} from 'sentry/views/eventsV2/table/types';

import {DisplayModes} from '../transactionSummary/transactionOverview/charts';
import {
  normalizeSearchConditionsWithTransactionName,
  TransactionFilterOptions,
  transactionSummaryRouteWithQuery,
} from '../transactionSummary/utils';
import {getSelectedProjectPlatforms} from '../utils';

import {
  getVitalDetailTableMehStatusFunction,
  getVitalDetailTablePoorStatusFunction,
  vitalAbbreviations,
  vitalNameFromLocation,
  VitalState,
  vitalStateColors,
} from './utils';

const COLUMN_TITLES = ['Transaction', 'Project', 'Unique Users', 'Count'];

const getTableColumnTitle = (index: number, vitalName: WebVital) => {
  const abbrev = vitalAbbreviations[vitalName];
  const titles = [
    ...COLUMN_TITLES,
    `p50(${abbrev})`,
    `p75(${abbrev})`,
    `p95(${abbrev})`,
    `Status`,
  ];
  return titles[index];
};

export function getProjectID(
  eventData: EventData,
  projects: Project[]
): string | undefined {
  const projectSlug = (eventData?.project as string) || undefined;

  if (typeof projectSlug === undefined) {
    return undefined;
  }

  const project = projects.find(currentProject => currentProject.slug === projectSlug);

  if (!project) {
    return undefined;
  }

  return project.id;
}

type Props = {
  eventView: EventView;
  location: Location;
  organization: Organization;
  projects: Project[];
  setError: (msg: string | undefined) => void;

  summaryConditions: string;
};

type State = {
  widths: number[];
};

class Table extends Component<Props, State> {
  state: State = {
    widths: [],
  };

  handleCellAction = (column: TableColumn<keyof TableDataRow>) => {
    return (action: Actions, value: React.ReactText) => {
      const {eventView, location, organization} = this.props;

      trackAnalyticsEvent({
        eventKey: 'performance_views.overview.cellaction',
        eventName: 'Performance Views: Cell Action Clicked',
        organization_id: parseInt(organization.id, 10),
        action,
      });

      const searchConditions = normalizeSearchConditionsWithTransactionName(
        eventView.query
      );

      updateQuery(searchConditions, action, column, value);

      browserHistory.push({
        pathname: location.pathname,
        query: {
          ...location.query,
          cursor: undefined,
          query: searchConditions.formatString(),
        },
      });
    };
  };

  renderBodyCell(
    tableData: TableData | null,
    column: TableColumn<keyof TableDataRow>,
    dataRow: TableDataRow,
    vitalName: WebVital
  ): React.ReactNode {
    const {eventView, organization, projects, location, summaryConditions} = this.props;

    if (!tableData || !tableData.meta?.fields) {
      return dataRow[column.key];
    }
    const tableMeta = tableData.meta?.fields;

    const field = String(column.key);

    if (field === getVitalDetailTablePoorStatusFunction(vitalName)) {
      if (dataRow[field]) {
        return (
          <UniqueTagCell>
            <PoorTag>{t('Poor')}</PoorTag>
          </UniqueTagCell>
        );
      }
      if (dataRow[getVitalDetailTableMehStatusFunction(vitalName)]) {
        return (
          <UniqueTagCell>
            <MehTag>{t('Meh')}</MehTag>
          </UniqueTagCell>
        );
      }
      return (
        <UniqueTagCell>
          <GoodTag>{t('Good')}</GoodTag>
        </UniqueTagCell>
      );
    }

    const fieldRenderer = getFieldRenderer(field, tableMeta, false);
    const rendered = fieldRenderer(dataRow, {organization, location});

    const allowActions = [
      Actions.ADD,
      Actions.EXCLUDE,
      Actions.SHOW_GREATER_THAN,
      Actions.SHOW_LESS_THAN,
    ];

    if (field === 'transaction') {
      const projectID = getProjectID(dataRow, projects);
      const summaryView = eventView.clone();
      const conditions = new MutableSearch(summaryConditions);
      conditions.addFilterValues('has', [`${vitalName}`]);
      summaryView.query = conditions.formatString();

      const transaction = String(dataRow.transaction) || '';

      const target = transactionSummaryRouteWithQuery({
        orgSlug: organization.slug,
        transaction,
        query: summaryView.generateQueryStringObject(),
        projectID,
        showTransactions: TransactionFilterOptions.RECENT,
        display: DisplayModes.VITALS,
      });

      return (
        <CellAction
          column={column}
          dataRow={dataRow}
          handleCellAction={this.handleCellAction(column)}
          allowActions={allowActions}
        >
          <Link
            to={target}
            aria-label={t('See transaction summary of the transaction %s', transaction)}
            onClick={this.handleSummaryClick}
          >
            {rendered}
          </Link>
        </CellAction>
      );
    }

    if (field.startsWith('team_key_transaction')) {
      return rendered;
    }

    return (
      <CellAction
        column={column}
        dataRow={dataRow}
        handleCellAction={this.handleCellAction(column)}
        allowActions={allowActions}
      >
        {rendered}
      </CellAction>
    );
  }

  renderBodyCellWithData = (tableData: TableData | null, vitalName: WebVital) => {
    return (
      column: TableColumn<keyof TableDataRow>,
      dataRow: TableDataRow
    ): React.ReactNode => this.renderBodyCell(tableData, column, dataRow, vitalName);
  };

  renderHeadCell(
    column: TableColumn<keyof TableDataRow>,
    title: React.ReactNode,
    tableMeta?: EventsMetaType['fields']
  ): React.ReactNode {
    const {eventView, location} = this.props;
    // TODO: Need to map table meta keys to aggregate alias since eventView sorting still expects
    // aggregate aliases for now. We'll need to refactor event view to get rid of all aggregate
    // alias references and then we can remove this.
    const aggregateAliasTableMeta: EventsMetaType['fields'] | undefined = tableMeta
      ? {}
      : undefined;
    if (tableMeta) {
      Object.keys(tableMeta).forEach(key => {
        aggregateAliasTableMeta![getAggregateAlias(key)] = tableMeta[key];
      });
    }

    const align = fieldAlignment(column.name, column.type, aggregateAliasTableMeta);
    const field = {field: column.name, width: column.width};

    function generateSortLink(): LocationDescriptorObject | undefined {
      if (!aggregateAliasTableMeta) {
        return undefined;
      }

      const nextEventView = eventView.sortOnField(field, aggregateAliasTableMeta);
      const queryStringObject = nextEventView.generateQueryStringObject();

      return {
        ...location,
        query: {...location.query, sort: queryStringObject.sort},
      };
    }
    const currentSort = eventView.sortForField(field, aggregateAliasTableMeta);
    const canSort = isFieldSortable(field, aggregateAliasTableMeta);

    return (
      <SortLink
        align={align}
        title={title || field.field}
        direction={currentSort ? currentSort.kind : undefined}
        canSort={canSort}
        generateSortLink={generateSortLink}
      />
    );
  }

  renderHeadCellWithMeta = (
    vitalName: WebVital,
    tableMeta?: EventsMetaType['fields']
  ) => {
    return (column: TableColumn<keyof TableDataRow>, index: number): React.ReactNode =>
      this.renderHeadCell(column, getTableColumnTitle(index, vitalName), tableMeta);
  };

  renderPrependCellWithData = (tableData: TableData | null, vitalName: WebVital) => {
    const {eventView} = this.props;
    const teamKeyTransactionColumn = eventView
      .getColumns()
      .find((col: TableColumn<React.ReactText>) => col.name === 'team_key_transaction');
    return (isHeader: boolean, dataRow?: any) => {
      if (teamKeyTransactionColumn) {
        if (isHeader) {
          const star = (
            <IconStar
              key="keyTransaction"
              color="yellow400"
              isSolid
              data-test-id="key-transaction-header"
            />
          );
          return [
            this.renderHeadCell(teamKeyTransactionColumn, star, tableData?.meta?.fields),
          ];
        }
        return [
          this.renderBodyCell(tableData, teamKeyTransactionColumn, dataRow, vitalName),
        ];
      }
      return [];
    };
  };

  handleSummaryClick = () => {
    const {organization, projects, location} = this.props;
    trackAnalyticsEvent({
      eventKey: 'performance_views.overview.navigate.summary',
      eventName: 'Performance Views: Overview view summary',
      organization_id: parseInt(organization.id, 10),
      project_platforms: getSelectedProjectPlatforms(location, projects),
    });
  };

  handleResizeColumn = (columnIndex: number, nextColumn: GridColumn) => {
    const widths: number[] = [...this.state.widths];
    widths[columnIndex] = nextColumn.width
      ? Number(nextColumn.width)
      : COL_WIDTH_UNDEFINED;
    this.setState({widths});
  };

  getSortedEventView(vitalName: WebVital) {
    const {eventView} = this.props;

    const aggregateFieldPoor = getAggregateAlias(
      getVitalDetailTablePoorStatusFunction(vitalName)
    );
    const aggregateFieldMeh = getAggregateAlias(
      getVitalDetailTableMehStatusFunction(vitalName)
    );
    const isSortingByStatus = eventView.sorts.some(
      sort =>
        sort.field.includes(aggregateFieldPoor) || sort.field.includes(aggregateFieldMeh)
    );

    const additionalSorts: Sort[] = isSortingByStatus
      ? []
      : [
          {
            field: 'team_key_transaction',
            kind: 'desc',
          },
          {
            field: aggregateFieldPoor,
            kind: 'desc',
          },
          {
            field: aggregateFieldMeh,
            kind: 'desc',
          },
        ];

    return eventView.withSorts([...additionalSorts, ...eventView.sorts]);
  }

  render() {
    const {eventView, organization, location} = this.props;
    const {widths} = this.state;

    const fakeColumnView = eventView.clone();
    fakeColumnView.fields = [...eventView.fields];
    const columnOrder = fakeColumnView
      .getColumns()
      // remove key_transactions from the column order as we'll be rendering it
      // via a prepended column
      .filter((col: TableColumn<React.ReactText>) => col.name !== 'team_key_transaction')
      .slice(0, -1)
      .map((col: TableColumn<React.ReactText>, i: number) => {
        if (typeof widths[i] === 'number') {
          return {...col, width: widths[i]};
        }
        return col;
      });

    const vitalName = vitalNameFromLocation(location);
    const sortedEventView = this.getSortedEventView(vitalName);
    const columnSortBy = sortedEventView.getSorts();

    return (
      <div>
        <VitalsDetailsTableQuery
          eventView={sortedEventView}
          orgSlug={organization.slug}
          location={location}
          limit={10}
          referrer="api.performance.vital-detail"
        >
          {({pageLinks, isLoading, tableData}) => (
            <Fragment>
              <GridEditable
                isLoading={isLoading}
                data={tableData ? tableData.data : []}
                columnOrder={columnOrder}
                columnSortBy={columnSortBy}
                grid={{
                  onResizeColumn: this.handleResizeColumn,
                  renderHeadCell: this.renderHeadCellWithMeta(
                    vitalName,
                    tableData?.meta?.fields
                  ) as any,
                  renderBodyCell: this.renderBodyCellWithData(
                    tableData,
                    vitalName
                  ) as any,
                  renderPrependColumns: this.renderPrependCellWithData(
                    tableData,
                    vitalName
                  ) as any,
                  prependColumnWidths: ['max-content'],
                }}
                location={location}
              />
              <Pagination pageLinks={pageLinks} />
            </Fragment>
          )}
        </VitalsDetailsTableQuery>
      </div>
    );
  }
}

const UniqueTagCell = styled('div')`
  text-align: right;
  justify-self: flex-end;
  flex-grow: 1;
`;

const GoodTag = styled(Tag)`
  div {
    background-color: ${p => p.theme[vitalStateColors[VitalState.GOOD]]};
  }
  span {
    color: ${p => p.theme.white};
  }
`;

const MehTag = styled(Tag)`
  div {
    background-color: ${p => p.theme[vitalStateColors[VitalState.MEH]]};
  }
  span {
    color: ${p => p.theme.white};
  }
`;

const PoorTag = styled(Tag)`
  div {
    background-color: ${p => p.theme[vitalStateColors[VitalState.POOR]]};
  }
  span {
    color: ${p => p.theme.white};
  }
`;

export default Table;
