import {cloneElement, Component, isValidElement} from 'react';
import {browserHistory, PlainRoute, RouteComponentProps} from 'react-router';
import styled from '@emotion/styled';
import cloneDeep from 'lodash/cloneDeep';
import isEqual from 'lodash/isEqual';
import omit from 'lodash/omit';

import {
  createDashboard,
  deleteDashboard,
  updateDashboard,
} from 'sentry/actionCreators/dashboards';
import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
import {
  openAddDashboardWidgetModal,
  openWidgetViewerModal,
} from 'sentry/actionCreators/modal';
import {Client} from 'sentry/api';
import Feature from 'sentry/components/acl/feature';
import Breadcrumbs from 'sentry/components/breadcrumbs';
import ButtonBar from 'sentry/components/buttonBar';
import DatePageFilter from 'sentry/components/datePageFilter';
import EnvironmentPageFilter from 'sentry/components/environmentPageFilter';
import HookOrDefault from 'sentry/components/hookOrDefault';
import * as Layout from 'sentry/components/layouts/thirds';
import {
  isWidgetViewerPath,
  WidgetViewerQueryField,
} from 'sentry/components/modals/widgetViewerModal/utils';
import NoProjectMessage from 'sentry/components/noProjectMessage';
import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container';
import ProjectPageFilter from 'sentry/components/projectPageFilter';
import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
import {t} from 'sentry/locale';
import {PageContent} from 'sentry/styles/organization';
import space from 'sentry/styles/space';
import {Organization, PageFilters} from 'sentry/types';
import {defined} from 'sentry/utils';
import {trackAnalyticsEvent} from 'sentry/utils/analytics';
import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
import {ReleasesProvider} from 'sentry/utils/releases/releasesProvider';
import withApi from 'sentry/utils/withApi';
import withOrganization from 'sentry/utils/withOrganization';
import withPageFilters from 'sentry/utils/withPageFilters';

import {
  WidgetViewerContext,
  WidgetViewerContextProps,
} from './widgetViewer/widgetViewerContext';
import Controls from './controls';
import Dashboard from './dashboard';
import {DEFAULT_STATS_PERIOD} from './data';
import {
  assignDefaultLayout,
  calculateColumnDepths,
  getDashboardLayout,
} from './layoutUtils';
import ReleasesSelectControl from './releasesSelectControl';
import DashboardTitle from './title';
import {
  DashboardDetails,
  DashboardFilters,
  DashboardListItem,
  DashboardState,
  DashboardWidgetSource,
  MAX_WIDGETS,
  Widget,
  WidgetType,
} from './types';
import {cloneDashboard, hasSavedPageFilters} from './utils';

const UNSAVED_MESSAGE = t('You have unsaved changes, are you sure you want to leave?');

const HookHeader = HookOrDefault({hookName: 'component:dashboards-header'});

type RouteParams = {
  orgId: string;
  dashboardId?: string;
  widgetId?: number;
  widgetIndex?: number;
};

type Props = RouteComponentProps<RouteParams, {}> & {
  api: Client;
  dashboard: DashboardDetails;
  dashboards: DashboardListItem[];
  initialState: DashboardState;
  organization: Organization;
  route: PlainRoute;
  selection: PageFilters;
  newWidget?: Widget;
  onDashboardUpdate?: (updatedDashboard: DashboardDetails) => void;
  onSetNewWidget?: () => void;
};

type State = {
  dashboardState: DashboardState;
  modifiedDashboard: DashboardDetails | null;
  widgetLimitReached: boolean;
} & WidgetViewerContextProps;

class DashboardDetail extends Component<Props, State> {
  state: State = {
    dashboardState: this.props.initialState,
    modifiedDashboard: this.updateModifiedDashboard(this.props.initialState),
    widgetLimitReached: this.props.dashboard.widgets.length >= MAX_WIDGETS,
    setData: data => {
      this.setState(data);
    },
  };

  componentDidMount() {
    const {route, router} = this.props;
    router.setRouteLeaveHook(route, this.onRouteLeave);
    window.addEventListener('beforeunload', this.onUnload);
    this.checkIfShouldMountWidgetViewerModal();
  }

  componentDidUpdate(prevProps: Props) {
    this.checkIfShouldMountWidgetViewerModal();

    if (prevProps.initialState !== this.props.initialState) {
      // Widget builder can toggle Edit state when saving
      this.setState({dashboardState: this.props.initialState});
    }
  }

  componentWillUnmount() {
    window.removeEventListener('beforeunload', this.onUnload);
  }

  checkIfShouldMountWidgetViewerModal() {
    const {
      params: {widgetId, dashboardId},
      organization,
      dashboard,
      location,
      router,
    } = this.props;
    const {seriesData, tableData, pageLinks, totalIssuesCount} = this.state;
    if (isWidgetViewerPath(location.pathname)) {
      const widget =
        defined(widgetId) &&
        (dashboard.widgets.find(({id}) => id === String(widgetId)) ??
          dashboard.widgets[widgetId]);
      if (widget) {
        openWidgetViewerModal({
          organization,
          widget,
          seriesData,
          tableData,
          pageLinks,
          totalIssuesCount,
          onClose: () => {
            // Filter out Widget Viewer Modal query params when exiting the Modal
            const query = omit(location.query, Object.values(WidgetViewerQueryField));
            router.push({
              pathname: location.pathname.replace(/widget\/[0-9]+\/$/, ''),
              query,
            });
          },
          onEdit: () => {
            if (
              organization.features.includes('new-widget-builder-experience-design') &&
              !organization.features.includes(
                'new-widget-builder-experience-modal-access'
              )
            ) {
              const widgetIndex = dashboard.widgets.indexOf(widget);
              if (dashboardId) {
                router.push({
                  pathname: `/organizations/${organization.slug}/dashboard/${dashboardId}/widget/${widgetIndex}/edit/`,
                  query: {
                    ...location.query,
                    source: DashboardWidgetSource.DASHBOARDS,
                  },
                });
                return;
              }
            }
            openAddDashboardWidgetModal({
              organization,
              widget,
              onUpdateWidget: (nextWidget: Widget) => {
                const updateIndex = dashboard.widgets.indexOf(widget);
                const nextWidgetsList = cloneDeep(dashboard.widgets);
                nextWidgetsList[updateIndex] = nextWidget;
                this.handleUpdateWidgetList(nextWidgetsList);
              },
              source: DashboardWidgetSource.DASHBOARDS,
            });
          },
        });
        trackAdvancedAnalyticsEvent('dashboards_views.widget_viewer.open', {
          organization,
          widget_type: widget.widgetType ?? WidgetType.DISCOVER,
          display_type: widget.displayType,
        });
      } else {
        // Replace the URL if the widget isn't found and raise an error in toast
        router.replace({
          pathname: `/organizations/${organization.slug}/dashboard/${dashboard.id}/`,
          query: location.query,
        });
        addErrorMessage(t('Widget not found'));
      }
    }
  }

  updateModifiedDashboard(dashboardState: DashboardState) {
    const {dashboard} = this.props;
    switch (dashboardState) {
      case DashboardState.PREVIEW:
      case DashboardState.CREATE:
      case DashboardState.EDIT:
        return cloneDashboard(dashboard);
      default: {
        return null;
      }
    }
  }

  get isPreview() {
    const {dashboardState} = this.state;
    return DashboardState.PREVIEW === dashboardState;
  }

  get isEditing() {
    const {dashboardState} = this.state;
    return [
      DashboardState.EDIT,
      DashboardState.CREATE,
      DashboardState.PENDING_DELETE,
    ].includes(dashboardState);
  }

  get isWidgetBuilderRouter() {
    const {location, params, organization} = this.props;
    const {dashboardId, widgetIndex} = params;

    const widgetBuilderRoutes = [
      `/organizations/${organization.slug}/dashboards/new/widget/new/`,
      `/organizations/${organization.slug}/dashboard/${dashboardId}/widget/new/`,
      `/organizations/${organization.slug}/dashboards/new/widget/${widgetIndex}/edit/`,
      `/organizations/${organization.slug}/dashboard/${dashboardId}/widget/${widgetIndex}/edit/`,
    ];

    return widgetBuilderRoutes.includes(location.pathname);
  }

  get dashboardTitle() {
    const {dashboard} = this.props;
    const {modifiedDashboard} = this.state;
    return modifiedDashboard ? modifiedDashboard.title : dashboard.title;
  }

  onEdit = () => {
    const {dashboard} = this.props;

    trackAnalyticsEvent({
      eventKey: 'dashboards2.edit.start',
      eventName: 'Dashboards2: Edit start',
      organization_id: parseInt(this.props.organization.id, 10),
    });

    this.setState({
      dashboardState: DashboardState.EDIT,
      modifiedDashboard: cloneDashboard(dashboard),
    });
  };

  onRouteLeave = () => {
    const {dashboard} = this.props;
    const {modifiedDashboard} = this.state;

    if (
      ![
        DashboardState.VIEW,
        DashboardState.PENDING_DELETE,
        DashboardState.PREVIEW,
      ].includes(this.state.dashboardState) &&
      !isEqual(modifiedDashboard, dashboard)
    ) {
      return UNSAVED_MESSAGE;
    }
    return undefined;
  };

  onUnload = (event: BeforeUnloadEvent) => {
    const {dashboard} = this.props;
    const {modifiedDashboard} = this.state;

    if (
      [
        DashboardState.VIEW,
        DashboardState.PENDING_DELETE,
        DashboardState.PREVIEW,
      ].includes(this.state.dashboardState) ||
      isEqual(modifiedDashboard, dashboard)
    ) {
      return;
    }
    event.preventDefault();
    event.returnValue = UNSAVED_MESSAGE;
  };

  onDelete = (dashboard: State['modifiedDashboard']) => () => {
    const {api, organization, location} = this.props;
    if (!dashboard?.id) {
      return;
    }

    const previousDashboardState = this.state.dashboardState;

    this.setState({dashboardState: DashboardState.PENDING_DELETE}, () => {
      deleteDashboard(api, organization.slug, dashboard.id)
        .then(() => {
          addSuccessMessage(t('Dashboard deleted'));
          trackAnalyticsEvent({
            eventKey: 'dashboards2.delete',
            eventName: 'Dashboards2: Delete',
            organization_id: parseInt(this.props.organization.id, 10),
          });
          browserHistory.replace({
            pathname: `/organizations/${organization.slug}/dashboards/`,
            query: location.query,
          });
        })
        .catch(() => {
          this.setState({
            dashboardState: previousDashboardState,
          });
        });
    });
  };

  onCancel = () => {
    const {organization, dashboard, location, params} = this.props;
    const {modifiedDashboard} = this.state;

    let hasDashboardChanged = !isEqual(modifiedDashboard, dashboard);

    // If a dashboard has every layout undefined, then ignore the layout field
    // when checking equality because it is a dashboard from before the grid feature
    const isLegacyLayout = dashboard.widgets.every(({layout}) => !defined(layout));
    if (isLegacyLayout) {
      hasDashboardChanged = !isEqual(
        {
          ...modifiedDashboard,
          widgets: modifiedDashboard?.widgets.map(widget => omit(widget, 'layout')),
        },
        {...dashboard, widgets: dashboard.widgets.map(widget => omit(widget, 'layout'))}
      );
    }

    // Don't confirm preview cancellation regardless of dashboard state
    if (hasDashboardChanged && !this.isPreview) {
      // Ignore no-alert here, so that the confirm on cancel matches onUnload & onRouteLeave
      /* eslint no-alert:0 */
      if (!confirm(UNSAVED_MESSAGE)) {
        return;
      }
    }
    if (params.dashboardId) {
      trackAnalyticsEvent({
        eventKey: 'dashboards2.edit.cancel',
        eventName: 'Dashboards2: Edit cancel',
        organization_id: parseInt(this.props.organization.id, 10),
      });
      this.setState({
        dashboardState: DashboardState.VIEW,
        modifiedDashboard: null,
      });
      return;
    }
    trackAnalyticsEvent({
      eventKey: 'dashboards2.create.cancel',
      eventName: 'Dashboards2: Create cancel',
      organization_id: parseInt(this.props.organization.id, 10),
    });
    browserHistory.replace({
      pathname: `/organizations/${organization.slug}/dashboards/`,
      query: location.query,
    });
  };

  handleChangeFilter = (activeFilters: DashboardFilters) => {
    const {dashboard} = this.props;
    const {modifiedDashboard} = this.state;
    const newModifiedDashboard = modifiedDashboard || dashboard;
    this.setState({
      modifiedDashboard: {
        ...newModifiedDashboard,
        filters: {...newModifiedDashboard.filters, ...activeFilters},
      },
    });
  };

  handleUpdateWidgetList = (widgets: Widget[]) => {
    const {organization, dashboard, api, onDashboardUpdate, location} = this.props;
    const {modifiedDashboard} = this.state;

    // Use the new widgets for calculating layout because widgets has
    // the most up to date information in edit state
    const currentLayout = getDashboardLayout(widgets);
    const layoutColumnDepths = calculateColumnDepths(currentLayout);
    const newModifiedDashboard = {
      ...cloneDashboard(modifiedDashboard || dashboard),
      widgets: assignDefaultLayout(widgets, layoutColumnDepths),
    };
    this.setState({
      modifiedDashboard: newModifiedDashboard,
      widgetLimitReached: widgets.length >= MAX_WIDGETS,
    });
    if (this.isEditing || this.isPreview) {
      return;
    }
    updateDashboard(api, organization.slug, newModifiedDashboard).then(
      (newDashboard: DashboardDetails) => {
        if (onDashboardUpdate) {
          onDashboardUpdate(newDashboard);
          this.setState({
            modifiedDashboard: null,
          });
        }
        addSuccessMessage(t('Dashboard updated'));
        if (dashboard && newDashboard.id !== dashboard.id) {
          browserHistory.replace({
            pathname: `/organizations/${organization.slug}/dashboard/${newDashboard.id}/`,
            query: {
              ...location.query,
            },
          });
          return;
        }
      },
      () => undefined
    );
  };

  handleAddCustomWidget = (widget: Widget) => {
    const {dashboard} = this.props;
    const {modifiedDashboard} = this.state;
    const newModifiedDashboard = modifiedDashboard || dashboard;
    this.onUpdateWidget([...newModifiedDashboard.widgets, widget]);
  };

  onAddWidget = () => {
    const {
      organization,
      dashboard,
      router,
      location,
      params: {dashboardId},
    } = this.props;
    this.setState({
      modifiedDashboard: cloneDashboard(dashboard),
    });

    if (
      organization.features.includes('new-widget-builder-experience-design') &&
      !organization.features.includes('new-widget-builder-experience-modal-access')
    ) {
      if (dashboardId) {
        router.push({
          pathname: `/organizations/${organization.slug}/dashboard/${dashboardId}/widget/new/`,
          query: {
            ...location.query,
            source: DashboardWidgetSource.DASHBOARDS,
          },
        });
        return;
      }
    }
    openAddDashboardWidgetModal({
      organization,
      dashboard,
      onAddLibraryWidget: (widgets: Widget[]) => this.handleUpdateWidgetList(widgets),
      source: DashboardWidgetSource.LIBRARY,
    });
  };

  onCommit = () => {
    const {api, organization, location, dashboard, onDashboardUpdate} = this.props;
    const {modifiedDashboard, dashboardState} = this.state;

    switch (dashboardState) {
      case DashboardState.PREVIEW:
      case DashboardState.CREATE: {
        if (modifiedDashboard) {
          if (this.isPreview) {
            trackAdvancedAnalyticsEvent('dashboards_manage.templates.add', {
              organization,
              dashboard_id: dashboard.id,
              dashboard_title: dashboard.title,
              was_previewed: true,
            });
          }
          let newModifiedDashboard = modifiedDashboard;
          if (organization.features.includes('dashboards-top-level-filter')) {
            const {project, environment, statsPeriod, start, end} = location.query;
            newModifiedDashboard = {
              ...cloneDashboard(modifiedDashboard),
              // Ensure projects and environment are sent as arrays, or undefined in the request
              // location.query will return a string if there's only one value
              projects:
                project === undefined
                  ? []
                  : typeof project === 'string'
                  ? [Number(project)]
                  : project.map(Number),
              environment: typeof environment === 'string' ? [environment] : environment,
              period: statsPeriod,
              start,
              end,
            };
          }
          createDashboard(
            api,
            organization.slug,
            newModifiedDashboard,
            this.isPreview
          ).then(
            (newDashboard: DashboardDetails) => {
              addSuccessMessage(t('Dashboard created'));
              trackAnalyticsEvent({
                eventKey: 'dashboards2.create.complete',
                eventName: 'Dashboards2: Create complete',
                organization_id: parseInt(organization.id, 10),
              });
              this.setState({
                dashboardState: DashboardState.VIEW,
              });

              // redirect to new dashboard
              browserHistory.replace({
                pathname: `/organizations/${organization.slug}/dashboard/${newDashboard.id}/`,
                query: {
                  ...location.query,
                },
              });
            },
            () => undefined
          );
        }
        break;
      }
      case DashboardState.EDIT: {
        // only update the dashboard if there are changes
        if (modifiedDashboard) {
          if (isEqual(dashboard, modifiedDashboard)) {
            this.setState({
              dashboardState: DashboardState.VIEW,
              modifiedDashboard: null,
            });
            return;
          }
          updateDashboard(api, organization.slug, modifiedDashboard).then(
            (newDashboard: DashboardDetails) => {
              if (onDashboardUpdate) {
                onDashboardUpdate(newDashboard);
              }
              addSuccessMessage(t('Dashboard updated'));
              trackAnalyticsEvent({
                eventKey: 'dashboards2.edit.complete',
                eventName: 'Dashboards2: Edit complete',
                organization_id: parseInt(organization.id, 10),
              });
              this.setState({
                dashboardState: DashboardState.VIEW,
                modifiedDashboard: null,
              });

              if (dashboard && newDashboard.id !== dashboard.id) {
                browserHistory.replace({
                  pathname: `/organizations/${organization.slug}/dashboard/${newDashboard.id}/`,
                  query: {
                    ...location.query,
                  },
                });
                return;
              }
            },
            () => undefined
          );

          return;
        }
        this.setState({
          dashboardState: DashboardState.VIEW,
          modifiedDashboard: null,
        });
        break;
      }
      case DashboardState.VIEW:
      default: {
        this.setState({
          dashboardState: DashboardState.VIEW,
          modifiedDashboard: null,
        });
        break;
      }
    }
  };

  setModifiedDashboard = (dashboard: DashboardDetails) => {
    this.setState({
      modifiedDashboard: dashboard,
    });
  };

  onUpdateWidget = (widgets: Widget[]) => {
    this.setState((state: State) => ({
      ...state,
      widgetLimitReached: widgets.length >= MAX_WIDGETS,
      modifiedDashboard: {
        ...(state.modifiedDashboard || this.props.dashboard),
        widgets,
      },
    }));
  };

  renderWidgetBuilder() {
    const {children, dashboard} = this.props;
    const {modifiedDashboard} = this.state;

    return isValidElement(children)
      ? cloneElement(children, {
          dashboard: modifiedDashboard ?? dashboard,
          onSave: this.isEditing ? this.onUpdateWidget : this.handleUpdateWidgetList,
        })
      : children;
  }

  renderDefaultDashboardDetail() {
    const {organization, dashboard, dashboards, params, router, location} = this.props;
    const {modifiedDashboard, dashboardState, widgetLimitReached} = this.state;
    const {dashboardId} = params;

    return (
      <PageFiltersContainer
        defaultSelection={{
          datetime: {
            start: null,
            end: null,
            utc: false,
            period: DEFAULT_STATS_PERIOD,
          },
        }}
        skipLoadLastUsed={
          organization.features.includes('dashboards-top-level-filter') &&
          hasSavedPageFilters(dashboard)
        }
      >
        <PageContent>
          <NoProjectMessage organization={organization}>
            <StyledPageHeader>
              <StyledTitle>
                <DashboardTitle
                  dashboard={modifiedDashboard ?? dashboard}
                  onUpdate={this.setModifiedDashboard}
                  isEditing={this.isEditing}
                />
              </StyledTitle>
              <Controls
                organization={organization}
                dashboards={dashboards}
                onEdit={this.onEdit}
                onCancel={this.onCancel}
                onCommit={this.onCommit}
                onAddWidget={this.onAddWidget}
                onDelete={this.onDelete(dashboard)}
                dashboardState={dashboardState}
                widgetLimitReached={widgetLimitReached}
              />
            </StyledPageHeader>
            <PageFilterBar condensed>
              <ProjectPageFilter />
              <EnvironmentPageFilter />
              <DatePageFilter alignDropdown="left" />
            </PageFilterBar>
            <HookHeader organization={organization} />
            <Dashboard
              paramDashboardId={dashboardId}
              dashboard={modifiedDashboard ?? dashboard}
              organization={organization}
              isEditing={this.isEditing}
              widgetLimitReached={widgetLimitReached}
              onUpdate={this.onUpdateWidget}
              handleUpdateWidgetList={this.handleUpdateWidgetList}
              handleAddCustomWidget={this.handleAddCustomWidget}
              isPreview={this.isPreview}
              router={router}
              location={location}
            />
          </NoProjectMessage>
        </PageContent>
      </PageFiltersContainer>
    );
  }

  getBreadcrumbLabel() {
    const {dashboardState} = this.state;

    let label = this.dashboardTitle;
    if (dashboardState === DashboardState.CREATE) {
      label = t('Create Dashboard');
    } else if (this.isPreview) {
      label = t('Preview Dashboard');
    }
    return label;
  }

  renderDashboardDetail() {
    const {
      organization,
      dashboard,
      dashboards,
      params,
      router,
      location,
      newWidget,
      selection,
      onSetNewWidget,
    } = this.props;
    const {modifiedDashboard, dashboardState, widgetLimitReached, seriesData, setData} =
      this.state;
    const {dashboardId} = params;

    const {filters} = modifiedDashboard || dashboard;

    return (
      <SentryDocumentTitle title={dashboard.title} orgSlug={organization.slug}>
        <PageFiltersContainer
          defaultSelection={{
            datetime: {
              start: null,
              end: null,
              utc: false,
              period: DEFAULT_STATS_PERIOD,
            },
          }}
          skipLoadLastUsed={
            organization.features.includes('dashboards-top-level-filter') &&
            hasSavedPageFilters(dashboard)
          }
        >
          <StyledPageContent>
            <NoProjectMessage organization={organization}>
              <Layout.Header>
                <Layout.HeaderContent>
                  <Breadcrumbs
                    crumbs={[
                      {
                        label: t('Dashboards'),
                        to: `/organizations/${organization.slug}/dashboards/`,
                      },
                      {
                        label: this.getBreadcrumbLabel(),
                      },
                    ]}
                  />
                  <Layout.Title>
                    <DashboardTitle
                      dashboard={modifiedDashboard ?? dashboard}
                      onUpdate={this.setModifiedDashboard}
                      isEditing={this.isEditing}
                    />
                  </Layout.Title>
                </Layout.HeaderContent>
                <Layout.HeaderActions>
                  <Controls
                    organization={organization}
                    dashboards={dashboards}
                    onEdit={this.onEdit}
                    onCancel={this.onCancel}
                    onCommit={this.onCommit}
                    onAddWidget={this.onAddWidget}
                    onDelete={this.onDelete(dashboard)}
                    dashboardState={dashboardState}
                    widgetLimitReached={widgetLimitReached}
                  />
                </Layout.HeaderActions>
              </Layout.Header>
              <Layout.Body>
                <Layout.Main fullWidth>
                  <Wrapper>
                    <PageFilterBar condensed>
                      <ProjectPageFilter />
                      <EnvironmentPageFilter />
                      <DatePageFilter alignDropdown="left" />
                    </PageFilterBar>
                    <Feature features={['dashboards-top-level-filter']}>
                      <FilterButtons>
                        <ReleasesProvider
                          organization={organization}
                          selection={selection}
                        >
                          <ReleasesSelectControl
                            handleChangeFilter={this.handleChangeFilter}
                            selectedReleases={filters?.release || []}
                          />
                        </ReleasesProvider>
                      </FilterButtons>
                    </Feature>
                  </Wrapper>
                  <WidgetViewerContext.Provider value={{seriesData, setData}}>
                    <Dashboard
                      paramDashboardId={dashboardId}
                      dashboard={modifiedDashboard ?? dashboard}
                      organization={organization}
                      isEditing={this.isEditing}
                      widgetLimitReached={widgetLimitReached}
                      onUpdate={this.onUpdateWidget}
                      handleUpdateWidgetList={this.handleUpdateWidgetList}
                      handleAddCustomWidget={this.handleAddCustomWidget}
                      router={router}
                      location={location}
                      newWidget={newWidget}
                      onSetNewWidget={onSetNewWidget}
                      isPreview={this.isPreview}
                    />
                  </WidgetViewerContext.Provider>
                </Layout.Main>
              </Layout.Body>
            </NoProjectMessage>
          </StyledPageContent>
        </PageFiltersContainer>
      </SentryDocumentTitle>
    );
  }

  render() {
    const {organization} = this.props;

    if (this.isWidgetBuilderRouter) {
      return this.renderWidgetBuilder();
    }

    if (organization.features.includes('dashboards-edit')) {
      return this.renderDashboardDetail();
    }

    return this.renderDefaultDashboardDetail();
  }
}

const StyledPageHeader = styled('div')`
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-row-gap: ${space(2)};
  align-items: center;
  margin-bottom: ${space(2)};

  @media (min-width: ${p => p.theme.breakpoints.medium}) {
    grid-template-columns: minmax(0, 1fr) max-content;
    grid-column-gap: ${space(2)};
    height: 40px;
  }
`;

const StyledTitle = styled(Layout.Title)`
  margin-top: 0;
`;

const StyledPageContent = styled(PageContent)`
  padding: 0;
`;

const Wrapper = styled('div')`
  display: grid;
  gap: ${space(1.5)};
  margin-bottom: ${space(2)};

  @media (min-width: ${p => p.theme.breakpoints.small}) {
    grid-template-columns: min-content 1fr;
  }
`;

const FilterButtons = styled(ButtonBar)`
  @media (max-width: ${p => p.theme.breakpoints.small}) {
    display: flex;
    align-items: flex-start;
    gap: ${space(1.5)};
  }

  @media (min-width: ${p => p.theme.breakpoints.small}) {
    display: grid;
    grid-auto-columns: minmax(auto, 300px);
  }
`;

export default withApi(withOrganization(withPageFilters(DashboardDetail)));
