import m, {Vnode} from 'mithril';
import {Engine} from '../../trace_processor/engine';
import {NUM, STR_NULL} from '../../trace_processor/query_result';
import {linkify} from '../../widgets/anchor';

enum FtraceEvent {
  CPU_FREQUENCY = 'power/cpu_frequency',
  CPU_IDLE = 'power/cpu_idle',
  DEVFREQ_FREQUENCY = 'devfreq/devfreq_frequency',
  CPUHP_ENTER = 'cpuhp/cpuhp_enter',
  CPUHP_EXIT = 'cpuhp/cpuhp_exit',
  CPUHP_MULTI_ENTER = 'cpuhp/cpuhp_multi_enter',
  PRINT = 'ftrace/print',
  SUSPEND_RESUME = 'power/suspend_resume',
  SCHED_SWITCH = 'sched/sched_switch',
}

// Walk through user's Perfetto Trace Configs and check
// against bare minimum configs that makes Wattson work.
// Add the missing ones to missingEvents, display in UI.
export async function missingWattsonCpuConfigs(
  engine: Engine,
): Promise<string[]> {
  // 1. Determine required events first
  const requiredEvents = new Set<FtraceEvent>([FtraceEvent.CPU_FREQUENCY]);

  const dsuDependencyQuery = await engine.query(
    `
    INCLUDE PERFETTO MODULE wattson.curves.utils;
    SELECT count(*) AS count FROM _cpu_w_dsu_dependency;
    `,
  );

  if (dsuDependencyQuery.firstRow({count: NUM}).count > 0) {
    requiredEvents.add(FtraceEvent.DEVFREQ_FREQUENCY);
  }

  // 2. Check configured events
  const query = `
    SELECT extract_metadata('trace_config_pbtxt') as str_value;
    `;

  const result = await engine.query(query);
  const row = result.maybeFirstRow({str_value: STR_NULL});
  const traceConfig = row?.str_value || '';

  const foundEvents = new Set<FtraceEvent>();

  if (/cpufreq_period_ms:\s*\d+/.test(traceConfig)) {
    foundEvents.add(FtraceEvent.CPU_FREQUENCY);
  }

  // below events are included in "freq" Atrace category.
  if (/atrace_categories:\s*"freq"/.test(traceConfig)) {
    foundEvents.add(FtraceEvent.CPU_FREQUENCY);
    foundEvents.add(FtraceEvent.DEVFREQ_FREQUENCY);
    foundEvents.add(FtraceEvent.SUSPEND_RESUME);
    foundEvents.add(FtraceEvent.CPUHP_ENTER);
    foundEvents.add(FtraceEvent.CPUHP_EXIT);
  }

  for (const event of Object.values(FtraceEvent)) {
    const eventPattern = new RegExp(`ftrace_events:\\s*"${event}"`);
    if (eventPattern.test(traceConfig)) {
      foundEvents.add(event);
    }
  }

  // 3. Compare required events with found events
  const missingEvents: string[] = [];
  for (const requiredEvent of requiredEvents) {
    if (!foundEvents.has(requiredEvent)) {
      missingEvents.push(requiredEvent);
    }
  }

  return missingEvents;
}

export function createCpuWarnings(
  missingEvents: string[],
  realCpuIdleCounters: boolean,
): Vnode | undefined {
  const warningMsg: Vnode[] = [];

  if (missingEvents.length > 0) {
    warningMsg.push(
      m(
        '.pf-wattson-warning',
        linkify(
          `See https://source.android.com/docs/core/power/wattson/how-to-wattson for more details on Wattson's required trace configuration. The following ftrace_events are necessary for Wattson to make power estimates:`,
        ),
        m(
          '.pf-wattson-warning__list',
          missingEvents.map((event) => m('li', event)),
        ),
      ),
    );
  }

  if (!realCpuIdleCounters) {
    if (warningMsg.length > 0) {
      warningMsg.push(m('hr'));
    }
    warningMsg.push(
      m(
        'p',
        '`cpu_idle` counters are not available in this trace; deriving cpu_idle counters from the swapper thread.',
      ),
    );
  }

  return warningMsg.length > 0
    ? m('.pf-wattson-warning', warningMsg)
    : undefined;
}
