/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 {
  FC,
  KeyboardEvent,
  MouseEvent,
  useCallback,
  useEffect,
  useState,
} from 'react';
import { useSelector } from 'react-redux';
import rison from 'rison';
import { SupersetClient, t } from '@superset-ui/core';
import { styled, useTheme } from '@apache-superset/core/ui';
import {
  Icons,
  Switch,
  Button,
  Skeleton,
  Card,
  Space,
} from '@superset-ui/core/components';
import { CopyToClipboard } from 'src/components';
import { RootState } from 'src/dashboard/types';
import { findPermission } from 'src/utils/findPermission';
import CodeSyntaxHighlighter, {
  SupportedLanguage,
  preloadLanguages,
} from '@superset-ui/core/components/CodeSyntaxHighlighter';
import { useHistory } from 'react-router-dom';
import { ExplorePageState } from 'src/explore/types';

export interface ViewQueryProps {
  sql: string;
  datasource: string;
  language?: SupportedLanguage;
}

const StyledSyntaxContainer = styled.div`
  height: 100%;
  display: flex;
  flex-direction: column;
`;

const StyledThemedSyntaxHighlighter = styled(CodeSyntaxHighlighter)`
  flex: 1;
`;

const StyledFooter = styled.div`
  display: flex;
  justify-content: space-between;
  align-items: center;
`;

const DATASET_BACKEND_QUERY = {
  keys: ['none'],
  columns: ['database.backend'],
};

const ViewQuery: FC<ViewQueryProps> = props => {
  const { sql, language = 'sql', datasource } = props;
  const theme = useTheme();
  const datasetId = datasource?.split('__')[0];
  const exploreBackend = useSelector(
    (state: ExplorePageState) => state.explore?.datasource?.database?.backend,
  );
  const [formattedSQL, setFormattedSQL] = useState<string>();
  const [showFormatSQL, setShowFormatSQL] = useState(true);
  const history = useHistory();
  const currentSQL = (showFormatSQL ? formattedSQL : sql) ?? sql;
  const canAccessSQLLab = useSelector((state: RootState) =>
    findPermission('menu_access', 'SQL Lab', state.user?.roles),
  );

  // Preload the language when component mounts to ensure smooth experience
  useEffect(() => {
    preloadLanguages([language]);
  }, [language]);

  const formatCurrentQuery = useCallback(async () => {
    if (formattedSQL) {
      setShowFormatSQL(val => !val);
      return;
    }
    try {
      let backend = exploreBackend;

      // Fetch backend info if not available in Redux state
      if (!backend) {
        const queryParams = rison.encode(DATASET_BACKEND_QUERY);
        const response = await SupersetClient.get({
          endpoint: `/api/v1/dataset/${datasetId}?q=${queryParams}`,
        });
        const { backend: datasetBackend } = response.json.result.database;
        backend = datasetBackend;
      }

      // Format the SQL query
      const formatResponse = await SupersetClient.post({
        endpoint: `/api/v1/sqllab/format_sql/`,
        body: JSON.stringify({
          sql,
          engine: backend,
        }),
        headers: { 'Content-Type': 'application/json' },
      });

      setFormattedSQL(formatResponse.json.result);
      setShowFormatSQL(true);
    } catch (error) {
      setShowFormatSQL(false);
    }
  }, [sql, datasetId, formattedSQL]);

  const navToSQLLab = useCallback(
    (domEvent: KeyboardEvent<HTMLElement> | MouseEvent<HTMLElement>) => {
      const requestedQuery = {
        datasourceKey: datasource,
        sql: currentSQL,
      };
      if (domEvent.metaKey || domEvent.ctrlKey) {
        domEvent.preventDefault();
        window.open(
          `/sqllab?datasourceKey=${datasource}&sql=${encodeURIComponent(currentSQL)}`,
          '_blank',
        );
      } else {
        history.push({ pathname: '/sqllab', state: { requestedQuery } });
      }
    },
    [history, datasource, currentSQL],
  );

  useEffect(() => {
    formatCurrentQuery();
  }, [sql]);

  return (
    <Card bodyStyle={{ padding: theme.sizeUnit * 4 }}>
      <StyledSyntaxContainer key={sql}>
        {!formattedSQL && showFormatSQL ? (
          <Skeleton active />
        ) : (
          <StyledThemedSyntaxHighlighter
            language={language}
            customStyle={{ flex: 1, marginBottom: theme.sizeUnit * 3 }}
          >
            {currentSQL}
          </StyledThemedSyntaxHighlighter>
        )}

        <StyledFooter>
          <Space size={theme.sizeUnit * 2}>
            <CopyToClipboard
              text={currentSQL}
              shouldShowText={false}
              copyNode={
                <Button
                  buttonStyle="secondary"
                  buttonSize="small"
                  icon={<Icons.CopyOutlined />}
                >
                  {t('Copy')}
                </Button>
              }
            />
            {canAccessSQLLab && (
              <Button
                buttonStyle="secondary"
                buttonSize="small"
                onClick={navToSQLLab}
              >
                {t('View in SQL Lab')}
              </Button>
            )}
          </Space>

          <Space size={theme.sizeUnit * 2} align="center">
            <Icons.ConsoleSqlOutlined />
            <Switch
              id="formatSwitch"
              checked={showFormatSQL}
              onChange={formatCurrentQuery}
              checkedChildren={t('formatted')}
              unCheckedChildren={t('original')}
            />
          </Space>
        </StyledFooter>
      </StyledSyntaxContainer>
    </Card>
  );
};

export default ViewQuery;
