import 'dart:math' as math;

import 'package:flutter/material.dart';

import '../theme/app_typography.dart';
import '../theme/app_surfaces.dart';
import 'mono_text.dart';

/// A column configuration for the [TasQAdaptiveList] desktop table view.
class TasQColumn<T> {
  /// Creates a column configuration.
  const TasQColumn({
    required this.header,
    required this.cellBuilder,
    this.technical = false,
  });

  /// The column header text.
  final String header;

  /// Builds the cell content for each row.
  final Widget Function(BuildContext context, T item) cellBuilder;

  /// If true, applies monospace text style to the cell content.
  final bool technical;
}

/// Builds a mobile tile for [TasQAdaptiveList].
typedef TasQMobileTileBuilder<T> =
    Widget Function(BuildContext context, T item, List<Widget> actions);

/// Returns a list of action widgets for a given item.
typedef TasQRowActions<T> = List<Widget> Function(T item);

/// Callback when a row is tapped.
typedef TasQRowTap<T> = void Function(T item);

/// A adaptive list widget that renders as:
/// - **Mobile**: Tile-based list with infinite scroll listeners.
/// - **Desktop**: Data Table with paginated footer.
///
/// The widget requires a reactive data source ([items]) that responds to
/// pagination/search providers for server-side data fetching.
class TasQAdaptiveList<T> extends StatelessWidget {
  /// Creates an adaptive list.
  const TasQAdaptiveList({
    super.key,
    required this.items,
    required this.columns,
    required this.mobileTileBuilder,
    this.rowActions,
    this.onRowTap,
    this.rowsPerPage = 50,
    this.tableHeader,
    this.filterHeader,
    this.summaryDashboard,
    this.onRequestRefresh,
    this.isLoading = false,
  });

  /// The list of items to display.
  final List<T> items;

  /// The column configurations for the desktop table view.
  final List<TasQColumn<T>> columns;

  /// Builds the mobile tile for each item.
  final TasQMobileTileBuilder<T> mobileTileBuilder;

  /// Returns action widgets for each row (e.g., edit/delete buttons).
  final TasQRowActions<T>? rowActions;

  /// Callback when a row is tapped.
  final TasQRowTap<T>? onRowTap;

  /// Number of rows per page for desktop view.
  ///
  /// Per CLAUDE.md: Standard page size is 50 items for Desktop.
  final int rowsPerPage;

  /// Optional header widget for the desktop table.
  final Widget? tableHeader;

  /// Optional filter header widget that appears above the list/table.
  final Widget? filterHeader;

  /// Optional summary dashboard widget (e.g., status counts).
  final Widget? summaryDashboard;

  /// Callback when the user requests refresh (infinite scroll or pagination).
  final void Function()? onRequestRefresh;

  /// If true, shows a loading indicator for server-side pagination.
  final bool isLoading;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final isMobile = constraints.maxWidth < 600;

        if (isMobile) {
          return _buildMobile(context, constraints);
        }

        return _buildDesktop(context, constraints);
      },
    );
  }

  Widget _buildMobile(BuildContext context, BoxConstraints constraints) {
    final hasBoundedHeight = constraints.hasBoundedHeight;

    // Mobile: Single-column with infinite scroll listeners
    final listView = ListView.separated(
      padding: const EdgeInsets.only(bottom: 24),
      itemCount: items.length + (isLoading ? 1 : 0),
      separatorBuilder: (context, index) => const SizedBox(height: 12),
      itemBuilder: (context, index) {
        if (index >= items.length) {
          // Loading indicator for infinite scroll
          return Padding(
            padding: const EdgeInsets.only(top: 8),
            child: SizedBox(
              height: 24,
              child: Center(child: CircularProgressIndicator(strokeWidth: 2)),
            ),
          );
        }
        final item = items[index];
        final actions = rowActions?.call(item) ?? const <Widget>[];
        return _MobileTile(
          item: item,
          actions: actions,
          mobileTileBuilder: mobileTileBuilder,
          onRowTap: onRowTap,
        );
      },
    );

    // Shrink-wrapped list for unbounded height contexts
    final shrinkWrappedList = ListView.separated(
      padding: const EdgeInsets.only(bottom: 24),
      itemCount: items.length + (isLoading ? 1 : 0),
      separatorBuilder: (context, index) => const SizedBox(height: 12),
      itemBuilder: (context, index) {
        if (index >= items.length) {
          return Padding(
            padding: const EdgeInsets.only(top: 8),
            child: SizedBox(
              height: 24,
              child: Center(child: CircularProgressIndicator(strokeWidth: 2)),
            ),
          );
        }
        final item = items[index];
        final actions = rowActions?.call(item) ?? const <Widget>[];
        return _MobileTile(
          item: item,
          actions: actions,
          mobileTileBuilder: mobileTileBuilder,
          onRowTap: onRowTap,
        );
      },
      shrinkWrap: true,
      physics: const NeverScrollableScrollPhysics(),
    );

    final summarySection = summaryDashboard == null
        ? null
        : <Widget>[
            SizedBox(width: double.infinity, child: summaryDashboard!),
            const SizedBox(height: 12),
          ];
    final filterSection = filterHeader == null
        ? null
        : <Widget>[
            ExpansionTile(
              title: const Text('Filters'),
              children: [
                Padding(
                  padding: const EdgeInsets.only(bottom: 8),
                  child: filterHeader!,
                ),
              ],
            ),
            const SizedBox(height: 8),
          ];

    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          ...?summarySection,
          ...?filterSection,
          if (hasBoundedHeight)
            Expanded(child: _buildInfiniteScrollListener(listView))
          else
            _buildInfiniteScrollListener(shrinkWrappedList),
        ],
      ),
    );
  }

  Widget _buildInfiniteScrollListener(Widget listView) {
    if (onRequestRefresh == null) {
      return listView;
    }
    return NotificationListener<ScrollNotification>(
      onNotification: (notification) {
        if (notification is ScrollEndNotification &&
            notification.metrics.extentAfter == 0) {
          // User scrolled to bottom, trigger load more
          onRequestRefresh!();
        }
        return false;
      },
      child: listView,
    );
  }

  Widget _buildDesktop(BuildContext context, BoxConstraints constraints) {
    final dataSource = _TasQTableSource<T>(
      context: context,
      items: items,
      columns: columns,
      rowActions: rowActions,
      onRowTap: onRowTap,
    );

    final contentWidth = constraints.maxWidth * 0.8;
    final tableWidth = math.max(
      contentWidth,
      (columns.length + (rowActions == null ? 0 : 1)) * 140.0,
    );
    final effectiveRowsPerPage = math.min(
      rowsPerPage,
      math.max(1, items.length),
    );

    final tableWidget = SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      child: SizedBox(
        width: tableWidth,
        child: PaginatedDataTable(
          header: tableHeader,
          rowsPerPage: effectiveRowsPerPage,
          columnSpacing: 20,
          horizontalMargin: 16,
          showCheckboxColumn: false,
          headingRowColor: WidgetStateProperty.resolveWith(
            (states) => Theme.of(context).colorScheme.surfaceContainer,
          ),
          columns: [
            for (final column in columns)
              DataColumn(label: Text(column.header)),
            if (rowActions != null) const DataColumn(label: Text('Actions')),
          ],
          source: dataSource,
        ),
      ),
    );

    final summarySection = summaryDashboard == null
        ? null
        : <Widget>[
            SizedBox(width: contentWidth, child: summaryDashboard!),
            const SizedBox(height: 12),
          ];
    final filterSection = filterHeader == null
        ? null
        : <Widget>[filterHeader!, const SizedBox(height: 12)];

    return SingleChildScrollView(
      primary: true,
      child: Center(
        child: SizedBox(
          width: contentWidth,
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [...?summarySection, ...?filterSection, tableWidget],
          ),
        ),
      ),
    );
  }
}

/// Mobile tile wrapper that applies Material 2 style elevation.
class _MobileTile<T> extends StatelessWidget {
  const _MobileTile({
    required this.item,
    required this.actions,
    required this.mobileTileBuilder,
    required this.onRowTap,
  });

  final T item;
  final List<Widget> actions;
  final TasQMobileTileBuilder<T> mobileTileBuilder;
  final TasQRowTap<T>? onRowTap;

  @override
  Widget build(BuildContext context) {
    final tile = mobileTileBuilder(context, item, actions);

    // Apply Material 2 style elevation for Cards (per Hybrid M3/M2 guidelines).
    // Mobile tiles deliberately use a slightly smaller corner radius for
    // compactness, but they should inherit the global card elevation and
    // shadow color from the theme to maintain visual consistency.
    if (tile is Card) {
      final themeCard = Theme.of(context).cardTheme;
      return Card(
        color: tile.color,
        elevation: themeCard.elevation ?? 3,
        margin: tile.margin,
        // prefer the tile's explicit shape. For mobile tiles we intentionally
        // use the compact radius token so list items feel denser while
        // remaining theme-driven.
        shape: tile.shape ?? AppSurfaces.of(context).compactShape,
        shadowColor: AppSurfaces.of(context).compactShadowColor,
        clipBehavior: tile.clipBehavior,
        child: tile.child,
      );
    }

    return tile;
  }
}

class _TasQTableSource<T> extends DataTableSource {
  /// Creates a table source for [TasQAdaptiveList].
  _TasQTableSource({
    required this.context,
    required this.items,
    required this.columns,
    required this.rowActions,
    required this.onRowTap,
  });

  final BuildContext context;
  final List<T> items;
  final List<TasQColumn<T>> columns;
  final TasQRowActions<T>? rowActions;
  final TasQRowTap<T>? onRowTap;

  @override
  DataRow? getRow(int index) {
    if (index >= items.length) return null;
    final item = items[index];
    final cells = <DataCell>[];

    for (final column in columns) {
      final widget = column.cellBuilder(context, item);
      cells.add(DataCell(_applyTechnicalStyle(context, widget, column)));
    }

    if (rowActions != null) {
      final actions = rowActions!.call(item);
      cells.add(
        DataCell(Row(mainAxisSize: MainAxisSize.min, children: actions)),
      );
    }

    return DataRow(
      onSelectChanged: onRowTap == null ? null : (_) => onRowTap!(item),
      cells: cells,
    );
  }

  @override
  bool get isRowCountApproximate => false;

  @override
  int get rowCount => items.length;

  @override
  int get selectedRowCount => 0;
}

Widget _applyTechnicalStyle<T>(
  BuildContext context,
  Widget child,
  TasQColumn<T> column,
) {
  if (!column.technical) return child;
  if (child is Text && child.data != null) {
    return MonoText(
      child.data ?? '',
      style: child.style,
      maxLines: child.maxLines,
      overflow: child.overflow,
      textAlign: child.textAlign,
    );
  }

  final mono = AppMonoText.of(context).body;
  return DefaultTextStyle.merge(style: mono, child: child);
}
