/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * 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 { query } from '../SqlLite';
import { NativeHookMalloc, NativeHookProcess, NativeHookSampleQueryInfo } from '../../bean/NativeHook';
import { MemoryType } from '../../bean/MemoryEnum';

export const queryNativeHookResponseTypes = (
  leftNs: number,
  rightNs: number,
  types: Array<string | number>,
  pid: number,
  isStatistic: boolean
): //@ts-ignore
  Promise<Array<unknown>> => {
  const table = isStatistic ? 'native_hook_statistic' : 'native_hook';
  const tsKey = isStatistic ? 'ts' : 'start_ts';
  const type = isStatistic ? 'type' : 'event_type';
  return query(
    'queryNativeHookResponseTypes',
    `select 
      distinct last_lib_id as lastLibId,
      data_dict.data as value 
    from 
      ${table} A ,trace_range B
      left join data_dict on A.last_lib_id = data_dict.id 
    where
      A.${tsKey} - B.start_ts between ${leftNs} and ${rightNs}
      and A.${type} in (${types.join(',')})
      and A.ipid = ${pid};
      `,
    { $leftNs: leftNs, $rightNs: rightNs, $types: types, $pid: pid }
  );
};
export const queryNativeHookStatistics = (
  leftNs: number,
  rightNs: number,
  ipid: number
): Promise<Array<NativeHookMalloc>> =>
  query(
    'queryNativeHookStatistics',
    `select 
      event_type as eventType,
      sub_type_id as subTypeId,
      max(heap_size) as max,
      sum(case when ((A.start_ts - B.start_ts) between ${leftNs} and ${rightNs}) then heap_size else 0 end) as allocByte,
      sum(case when ((A.start_ts - B.start_ts) between ${leftNs} and ${rightNs}) then 1 else 0 end) as allocCount,
      sum(case when ((A.end_ts - B.start_ts) between ${leftNs} and ${rightNs} ) then heap_size else 0 end) as freeByte,
      sum(case when ((A.end_ts - B.start_ts) between ${leftNs} and ${rightNs} ) then 1 else 0 end) as freeCount
    from
      native_hook A,
      trace_range B
    where
      (A.start_ts - B.start_ts) between ${leftNs} and ${rightNs}
     and event_type in (${MemoryType.APPLY_NM_EVENTS.map(e => `'${e}'`).join(', ')})
     and ipid = ${ipid}
    group by event_type;`,
    { $leftNs: leftNs, $rightNs: rightNs }
  );

export const queryNativeHookStatisticsMalloc = (
  leftNs: number,
  rightNs: number,
  ipid: number
): Promise<Array<NativeHookMalloc>> =>
  query(
    'queryNativeHookStatisticsMalloc',
    `select 
      event_type as eventType,
      heap_size as heapSize,
      sum(case when ((A.start_ts - B.start_ts) between ${leftNs} and ${rightNs}) then heap_size else 0 end) as allocByte,
      sum(case when ((A.start_ts - B.start_ts) between ${leftNs} and ${rightNs}) then 1 else 0 end) as allocCount,
      sum(case when ((A.end_ts - B.start_ts) between ${leftNs} and ${rightNs} ) then heap_size else 0 end) as freeByte,
      sum(case when ((A.end_ts - B.start_ts) between ${leftNs} and ${rightNs} ) then 1 else 0 end) as freeCount
    from
      native_hook A,
      trace_range B
    where
      (A.start_ts - B.start_ts) between ${leftNs} and ${rightNs}
    and
      event_type in (${MemoryType.APPLY_NM_EVENTS.map(e => `'${e}'`).join(', ')})
    and 
      sub_type_id is null
      and ipid = ${ipid}
    group by
      event_type,
      heap_size
    order by heap_size desc
    `,
    { $leftNs: leftNs, $rightNs: rightNs }
  );

export const queryNativeHookStatisticsSubType = (
  leftNs: number,
  rightNs: number,
  ipid: number
): Promise<Array<NativeHookMalloc>> =>
  query(
    'queryNativeHookStatisticsSubType',
    `select 
      event_type as eventType,
      sub_type_id as subTypeId,
      max(heap_size) as max,
      sum(case when ((NH.start_ts - TR.start_ts) between ${leftNs} and ${rightNs}) then heap_size else 0 end) as allocByte,
      sum(case when ((NH.start_ts - TR.start_ts) between ${leftNs} and ${rightNs}) then 1 else 0 end) as allocCount,
      sum(case when ((NH.end_ts - TR.start_ts) between ${leftNs} and ${rightNs} ) then heap_size else 0 end) as freeByte,
      sum(case when ((NH.end_ts - TR.start_ts) between ${leftNs} and ${rightNs} ) then 1 else 0 end) as freeCount
    from
      native_hook NH,
      trace_range TR
    where
      (NH.start_ts - TR.start_ts) between ${leftNs} and ${rightNs}
    and
      event_type in (${MemoryType.APPLY_NM_EVENTS.map(e => `'${e}'`).join(', ')})
    and ipid = ${ipid}
    group by
      event_type,sub_type_id;
        `,
    { $leftNs: leftNs, $rightNs: rightNs }
  );

export const queryNativeHookSubType = (
  leftNs: number,
  rightNs: number,
  ipid: number,
  types: Array<string | number>
): //@ts-ignore
  Promise<Array<unknown>> =>
  query(
    'queryNativeHookSubType',
    `select distinct
      (case when sub_type_id is null  then -1 else sub_type_id end) as subTypeId,
      (case when sub_type_id is null then lower(NH.event_type) else DD.data end) as subType
    from
      native_hook NH,trace_range TR
      left join data_dict DD on NH.sub_type_id = DD.id
    where event_type in (${types.map(type => `'${type}'`).join(',')})
      and (NH.start_ts - TR.start_ts) between ${leftNs} and ${rightNs}
      and ipid = ${ipid}`,
    { $leftNs: leftNs, $rightNs: rightNs }
  );

export const queryNativeHookStatisticSubType = (
  leftNs: number,
  rightNs: number,
  ipid: number,
  types: Array<string | number>
): //@ts-ignore
  Promise<Array<unknown>> =>
  query(
    'queryNativeHookStatisticSubType',
    `SELECT DISTINCT
 	  	   IFNULL(NHS.sub_type_id, -1) AS subTypeId,
 	   	   CASE
 	   	     WHEN NHS.sub_type_id IS NULL AND NHS.type = 0 THEN 'allocevent'
 	   	     WHEN NHS.sub_type_id IS NULL AND NHS.type = 1 THEN 'mmapevent'
 	   	     WHEN NHS.sub_type_id IS NULL AND NHS.type = 2 THEN 'FILE_PAGE_MSG'
 	   	     WHEN NHS.sub_type_id IS NULL AND NHS.type = 3 THEN 'MEMORY_USING_MSG'
 	   	     WHEN NHS.sub_type_id IS NULL AND NHS.type = 17 THEN 'ARK_GLOBAL_HANDLE_Alloc_Event'
 	   	     WHEN NHS.sub_type_id IS NULL AND NHS.type = 18 THEN 'ARK_LOCAL_HANDLE_Alloc_Event'
 	   	     WHEN type = 3 AND sub_type_id NOT NULL THEN D.data
 	   	     WHEN NHS.sub_type_id IS NOT NULL THEN D.data
 	   	   END AS subType
 	  	   FROM
 	  	     native_hook_statistic NHS
 	  	     LEFT JOIN data_dict D ON NHS.sub_type_id = D.id,
          trace_range TR
        WHERE
          NHS.type in (${types.map(type => `${type}`).join(',')})
          AND (NHS.ts - TR.start_ts) between ${leftNs} and ${rightNs}
          AND ipid = ${ipid}
        `,
    { $leftNs: leftNs, $rightNs: rightNs }
  );

export const queryStatisticType = (tableName: string): Promise<Array<{ type: number }>> =>
  query('queryStatisticType', `SELECT DISTINCT type from ${tableName}`, {});

export const queryNativeType = (tableName: string): Promise<Array<{ eventType: string }>> =>
  query('queryNativeType', `SELECT DISTINCT event_type as eventType from ${tableName}`, {});

export const queryNativeHookProcess = (table: string): Promise<Array<NativeHookProcess>> => {
  let sql = `select 
      distinct ${table}.ipid,
      pid,
      name
    from
      ${table}
    left join
      process p
    on
      ${table}.ipid = p.id
    `;
  return query('queryNativeHookProcess', sql, {});
};

export const queryNativeHookSnapshotTypes = (ipid: number): Promise<Array<NativeHookSampleQueryInfo>> =>
  query(
    'queryNativeHookSnapshotTypes',
    `select 
      event_type as eventType,
      data as subType
    from
      native_hook left join data_dict on native_hook.sub_type_id = data_dict.id
    where
      event_type  in (${MemoryType.APPLY_NM_EVENTS.map(e => `'${e}'`).join(', ')})
      and ipid = ${ipid}
    group by
      event_type,data;`,
    {}
  );

export const queryAllHookData = (rightNs: number, ipid: number): Promise<Array<NativeHookSampleQueryInfo>> =>
  query(
    'queryAllHookData',
    `select 
      callchain_id as eventId,
      event_type as eventType,
      data as subType,
      addr,
      heap_size as growth,
      (n.start_ts - t.start_ts) as startTs,
      (n.end_ts - t.start_ts) as endTs
    from
      native_hook n left join data_dict on n.sub_type_id = data_dict.id,
      trace_range t
    where
      event_type in (${MemoryType.APPLY_NM_EVENTS.map(e => `'${e}'`).join(', ')})
      and ipid = ${ipid}
    and
      n.start_ts between t.start_ts and ${rightNs} + t.start_ts`,
    { $rightNs: rightNs }
  );

export const querySubType = (
  leftNs: number,
  rightNs: number,
  ipid: number,
  tableName: string,
  eventTypes: Array<string | number>
): Promise<Array<{ subTypeId: number, subType: string, eventType: string }>> => {
  let eventTypeFiled = 'event_type';
  let timestampFiled = 'start_ts';
  if (tableName.includes('statistic')) {
    eventTypeFiled = 'type';
    timestampFiled = 'ts';
  }
  return query(
    'querySubType',
    `select distinct
          case when NH.sub_type_id is null then -1 else NH.sub_type_id end as subTypeId,
          case when NH.sub_type_id is null then 'Other ' || NH.${eventTypeFiled} else DD.data end as subType,
          NH.${eventTypeFiled} as eventType
        from
            ${tableName} NH,
            trace_range TR
            left join data_dict DD on NH.sub_type_id = DD.id
        where NH.${eventTypeFiled} in (${eventTypes.map(e => `'${e}'`).join(', ')})
            and (NH.${timestampFiled} - TR.start_ts) between ${leftNs} and ${rightNs}
            and NH.ipid = ${ipid}
            group by subType`,
    { $leftNs: leftNs, $rightNs: rightNs }
  );
}