// Copyright (C) 2025 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 m from 'mithril';
import type {Engine} from '../../trace_processor/engine';
import {NUM_NULL, STR} from '../../trace_processor/query_result';
import {Icon} from '../../widgets/icon';
import {Tooltip} from '../../widgets/tooltip';
import {Card} from '../../widgets/card';
import {Grid, GridCell, GridHeaderCell} from '../../widgets/grid';

// All possible tab keys - single source of truth
export const ALL_TAB_KEYS = [
  'overview',
  'config',
  'android',
  'traces',
  'machines',
  'import_errors',
  'trace_errors',
  'data_losses',
  'ui_loading_errors',
  'stats',
] as const;

// Derive the TabKey type from the array
export type TabKey = (typeof ALL_TAB_KEYS)[number];

// Type guard to check if a string is a valid TabKey
export function isValidTabKey(key: string): key is TabKey {
  return ALL_TAB_KEYS.includes(key as TabKey);
}

// Shared stats spec and type
export const statsSpec = {
  name: STR,
  value: NUM_NULL,
  description: STR,
  idx: STR,
  severity: STR,
  source: STR,
  machineId: NUM_NULL,
  traceId: NUM_NULL,
};

export type StatsSectionRow = typeof statsSpec;

export interface TraceInfo {
  readonly id: number;
  readonly traceIndex: number;
}

export async function getTraceInfos(
  engine: Engine,
): Promise<Map<number, TraceInfo>> {
  const result = await engine.query(`
    select id
    from __intrinsic_trace_file
    where is_container = 0
    order by id
  `);

  const map = new Map<number, TraceInfo>();
  let traceIndex = 0;
  for (const it = result.iter({id: NUM_NULL}); it.valid(); it.next()) {
    const id = it.id;
    if (id === null) continue;
    map.set(id, {
      id,
      traceIndex: traceIndex++,
    });
  }
  return map;
}

// Generic error category interface
export interface ErrorCategory {
  name: string;
  description: string;
  totalCount: number;
  entries: StatsSectionRow[];
}

// Load stats with a custom WHERE clause
export async function loadStatsWithFilter(
  engine: Engine,
  whereClause: string,
): Promise<StatsSectionRow[]> {
  const result = await engine.query(`
    select
      name,
      value,
      cast(ifnull(idx, '') as text) as idx,
      description,
      severity,
      source,
      machine_id as machineId,
      trace_id as traceId
    from stats
    where ${whereClause}
    order by trace_id, machine_id, name, idx
  `);

  const stats: StatsSectionRow[] = [];
  for (const iter = result.iter(statsSpec); iter.valid(); iter.next()) {
    stats.push({
      name: iter.name,
      value: iter.value,
      description: iter.description,
      idx: iter.idx,
      severity: iter.severity,
      source: iter.source,
      machineId: iter.machineId,
      traceId: iter.traceId,
    });
  }

  return stats;
}

// Group stats by category (name)
export function groupByCategory(stats: StatsSectionRow[]): ErrorCategory[] {
  const categoryMap = new Map<string, ErrorCategory>();
  for (const stat of stats) {
    const existing = categoryMap.get(stat.name);
    if (existing) {
      existing.totalCount += stat.value ?? 0;
      existing.entries.push(stat);
    } else {
      categoryMap.set(stat.name, {
        name: stat.name,
        description: stat.description,
        totalCount: stat.value ?? 0,
        entries: [stat],
      });
    }
  }
  return Array.from(categoryMap.values());
}

// Render an error category card
export function renderErrorCategoryCard(
  category: ErrorCategory,
  severity: 'danger' | 'warning',
  icon: string,
): m.Children {
  const scrollToSection = () => {
    const targetId = categoryToId(category.name);
    const element = document.getElementById(targetId);
    if (element) {
      element.scrollIntoView({behavior: 'smooth', block: 'start'});
    }
  };

  return m(
    Card,
    {
      className: `pf-trace-info-page__status-card pf-trace-info-page__status-card--${severity} pf-trace-info-page__status-card--clickable`,
      onclick: scrollToSection,
    },
    m(
      '.pf-trace-info-page__status-card-main',
      m(Icon, {
        icon,
        className: 'pf-trace-info-page__status-icon',
        filled: true,
      }),
      m(
        '.pf-trace-info-page__status-content',
        m(
          '.pf-trace-info-page__status-title',
          {title: category.name},
          category.name,
          category.description &&
            m(
              Tooltip,
              {
                trigger: m(Icon, {
                  icon: 'help_outline',
                  className: 'pf-trace-info-page__help-icon',
                }),
              },
              category.description,
            ),
        ),
        m('.pf-trace-info-page__status-value', category.totalCount),
      ),
      m(
        '.pf-trace-info-page__status-link',
        m(Icon, {
          icon: 'arrow_downward',
          className: 'pf-trace-info-page__status-link-icon',
        }),
      ),
    ),
  );
}

// Helper to generate a safe HTML ID from a category name
function categoryToId(categoryName: string): string {
  return `category-${categoryName.replace(/[^a-zA-Z0-9_-]/g, '_')}`;
}

// Render a category section with detailed breakdown
export function renderCategorySection(
  category: ErrorCategory,
  options?: {
    className?: string;
    isMultiTrace?: boolean;
    isMultiMachine?: boolean;
  },
): m.Children {
  const isMultiTrace = options?.isMultiTrace ?? false;
  const isMultiMachine = options?.isMultiMachine ?? false;
  const columns = [];
  if (isMultiTrace) {
    columns.push({key: 'trace_id', header: m(GridHeaderCell, 'Trace')});
  }
  if (isMultiMachine) {
    columns.push({key: 'machine_id', header: m(GridHeaderCell, 'Machine')});
  }
  columns.push(
    {key: 'idx', header: m(GridHeaderCell, 'Index')},
    {key: 'value', header: m(GridHeaderCell, 'Count')},
  );
  return m(
    '',
    m('h3', {id: categoryToId(category.name)}, category.name),
    category.description && m('p', category.description),
    m(Grid, {
      columns,
      rowData: category.entries.map((row) => {
        const cells = [];
        if (isMultiTrace) {
          cells.push(m(GridCell, row.traceId !== null ? row.traceId : '-'));
        }
        if (isMultiMachine) {
          cells.push(m(GridCell, row.machineId !== null ? row.machineId : '-'));
        }
        cells.push(
          m(GridCell, row.idx !== '' ? row.idx : '-'),
          m(GridCell, row.value !== null ? row.value : '-'),
        );
        return cells;
      }),
      className: options?.className,
    }),
  );
}
