// Copyright (C) 2018 The Android Open Source Project
//
// 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 {EngineProxy} from '../trace_processor/engine';
import {Row} from '../trace_processor/query_result';

const MAX_DISPLAY_ROWS = 10000;

export interface QueryResponse {
  query: string;
  error?: string;
  totalRowCount: number;
  durationMs: number;
  columns: string[];
  rows: Row[];
  statementCount: number;
  statementWithOutputCount: number;
  lastStatementSql: string;
}

export interface QueryRunParams {
  // If true, replaces nulls with "NULL" string. Default is true.
  convertNullsToString?: boolean;
}

export async function runQuery(
    sqlQuery: string, engine: EngineProxy, params?: QueryRunParams):
    Promise<QueryResponse> {
  const startMs = performance.now();
  const queryRes = engine.query(sqlQuery);

  // TODO(primiano): once the controller thread is gone we should pass down
  // the result objects directly to the frontend, iterate over the result
  // and deal with pagination there. For now we keep the old behavior and
  // truncate to 10k rows.

  try {
    await queryRes.waitAllRows();
  } catch {
    // In the case of a query error we don't want the exception to bubble up
    // as a crash. The |queryRes| object will be populated anyways.
    // queryRes.error() is used to tell if the query errored or not. If it
    // errored, the frontend will show a graceful message instead.
  }

  const convertNullsToString = params?.convertNullsToString ?? true;

  const durationMs = performance.now() - startMs;
  const rows: Row[] = [];
  const columns = queryRes.columns();
  let numRows = 0;
  for (const iter = queryRes.iter({}); iter.valid(); iter.next()) {
    const row: Row = {};
    for (const colName of columns) {
      const value = iter.get(colName);
      row[colName] = value === null && convertNullsToString ? 'NULL' : value;
    }
    rows.push(row);
    if (++numRows >= MAX_DISPLAY_ROWS) break;
  }

  const result: QueryResponse = {
    query: sqlQuery,
    durationMs,
    error: queryRes.error(),
    totalRowCount: queryRes.numRows(),
    columns,
    rows,
    statementCount: queryRes.statementCount(),
    statementWithOutputCount: queryRes.statementWithOutputCount(),
    lastStatementSql: queryRes.lastStatementSql(),
  };
  return result;
}
