import 'dart:async';

import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:intl/intl.dart' as intl;
import 'package:lucide_icons_flutter/lucide_icons.dart' as lucide;
import 'package:shadcn_ui/shadcn_ui.dart';

import '../../../../../core/common/models/paginated_result.dart';
import '../../../../../core/constants/app_sections.dart';
import '../../../../../widgets/app_layout.dart';
import '../../../../../widgets/components/filter_bar.dart';
import '../../../../../widgets/components/form_field.dart';
import '../../../../../widgets/components/superport_date_picker.dart';
import '../../../../../widgets/components/superport_table.dart';
import '../../../summary/application/inventory_service.dart';
import '../../../summary/domain/entities/inventory_event.dart';
import '../../../summary/domain/entities/inventory_summary.dart';
import '../../../summary/domain/entities/inventory_warehouse_balance.dart';
import '../controllers/inventory_detail_controller.dart';
import '../controllers/inventory_summary_controller.dart';
import '../../../shared/widgets/warehouse_select_field.dart';

/// 재고 현황 목록/상세 화면.
const Map<int, double> _inventorySummaryColumnWidths = {
  0: 56,
  1: 320,
  2: 200,
  3: 120,
  4: 260,
  5: 180,
};

class InventorySummaryPage extends StatefulWidget {
  const InventorySummaryPage({
    super.key,
    required this.routeUri,
    this.debugRowHeight,
  });

  final Uri routeUri;
  final double? debugRowHeight;

  @override
  State<InventorySummaryPage> createState() => _InventorySummaryPageState();
}

class _InventorySummaryPageState extends State<InventorySummaryPage> {
  late final InventorySummaryController _summaryController;
  late final InventoryDetailController _detailController;
  final TextEditingController _searchController = TextEditingController();
  final TextEditingController _productController = TextEditingController();
  final TextEditingController _vendorController = TextEditingController();

  String _pendingQuery = '';
  String? _pendingProductName;
  String? _pendingVendorName;
  int? _pendingWarehouseId;
  bool _pendingIncludeEmpty = false;
  DateTime? _pendingUpdatedSince;
  SuperportTableSortState? _sortState;
  bool _syncedInitialFilters = false;
  bool _autoRefreshEnabled = true;
  Timer? _autoRefreshTimer;

  static const Duration _autoRefreshInterval = Duration(seconds: 30);

  static const Map<int, String> _columnSortKeys = {
    1: 'product_name',
    2: 'vendor_name',
    3: 'total_quantity',
    5: 'last_event_at',
  };

  @override
  void initState() {
    super.initState();
    final service = GetIt.I<InventoryService>();
    _summaryController = InventorySummaryController(service: service)
      ..addListener(_onSummaryChanged);
    _detailController = InventoryDetailController(service: service);
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _summaryController.fetch();
      _syncPendingFilters(force: true);
      _restartAutoRefreshTimer();
    });
  }

  @override
  void dispose() {
    _summaryController.removeListener(_onSummaryChanged);
    _summaryController.dispose();
    _detailController.dispose();
    _searchController.dispose();
    _productController.dispose();
    _vendorController.dispose();
    _autoRefreshTimer?.cancel();
    super.dispose();
  }

  void _onSummaryChanged() {
    if (!_syncedInitialFilters) {
      _syncPendingFilters(force: true);
    }
    final summarySort = _summaryController.sort;
    if (summarySort != null) {
      final columnIndex = _columnSortKeys.entries
          .firstWhere(
            (entry) => entry.value == summarySort,
            orElse: () => const MapEntry(-1, ''),
          )
          .key;
      if (columnIndex != -1) {
        final ascending = (_summaryController.order ?? 'desc') == 'asc';
        _sortState = SuperportTableSortState(
          columnIndex: columnIndex,
          ascending: ascending,
        );
      }
    }
    setState(() {});
  }

  void _syncPendingFilters({bool force = false}) {
    if (!force && _syncedInitialFilters) {
      return;
    }
    _pendingQuery = _summaryController.query;
    _pendingProductName = _summaryController.productName;
    _pendingVendorName = _summaryController.vendorName;
    _pendingWarehouseId = _summaryController.warehouseId;
    _pendingIncludeEmpty = _summaryController.includeEmpty;
    _pendingUpdatedSince = _summaryController.updatedSince;
    _searchController.text = _pendingQuery;
    _productController.text = _pendingProductName ?? '';
    _vendorController.text = _pendingVendorName ?? '';
    _syncedInitialFilters = true;
  }

  void _restartAutoRefreshTimer() {
    _autoRefreshTimer?.cancel();
    if (!_autoRefreshEnabled) {
      return;
    }
    _autoRefreshTimer = Timer.periodic(_autoRefreshInterval, (_) {
      if (!mounted || _summaryController.isLoading) {
        return;
      }
      _summaryController.refresh();
    });
  }

  void _toggleAutoRefresh(bool value) {
    if (_autoRefreshEnabled == value) {
      return;
    }
    setState(() => _autoRefreshEnabled = value);
    _restartAutoRefreshTimer();
  }

  bool get _hasActiveFilters {
    final controller = _summaryController;
    return controller.query.isNotEmpty ||
        (controller.productName?.isNotEmpty ?? false) ||
        (controller.vendorName?.isNotEmpty ?? false) ||
        controller.warehouseId != null ||
        controller.includeEmpty ||
        controller.updatedSince != null;
  }

  bool get _hasPendingChanges {
    final controller = _summaryController;
    final normalizedPendingProduct = _pendingProductName?.trim() ?? '';
    final normalizedPendingVendor = _pendingVendorName?.trim() ?? '';
    final normalizedControllerProduct = controller.productName ?? '';
    final normalizedControllerVendor = controller.vendorName ?? '';
    final sameDate = _isSameDay(controller.updatedSince, _pendingUpdatedSince);
    return controller.query != _pendingQuery.trim() ||
        normalizedControllerProduct != normalizedPendingProduct ||
        normalizedControllerVendor != normalizedPendingVendor ||
        controller.warehouseId != _pendingWarehouseId ||
        controller.includeEmpty != _pendingIncludeEmpty ||
        !sameDate;
  }

  Future<void> _applyFilters() async {
    _summaryController
      ..updateQuery(_pendingQuery)
      ..updateProductName(_pendingProductName)
      ..updateVendorName(_pendingVendorName)
      ..updateWarehouse(_pendingWarehouseId)
      ..toggleIncludeEmpty(_pendingIncludeEmpty)
      ..updateUpdatedSince(_pendingUpdatedSince);
    await _summaryController.fetch(page: 1);
  }

  Future<void> _resetFilters() async {
    setState(() {
      _pendingQuery = '';
      _pendingProductName = null;
      _pendingVendorName = null;
      _pendingWarehouseId = null;
      _pendingIncludeEmpty = false;
      _pendingUpdatedSince = null;
      _searchController.clear();
      _productController.clear();
      _vendorController.clear();
      _sortState = null;
    });
    _summaryController
      ..updateQuery('')
      ..updateProductName(null)
      ..updateVendorName(null)
      ..updateWarehouse(null)
      ..toggleIncludeEmpty(false)
      ..updateUpdatedSince(null)
      ..updateSort(null);
    await _summaryController.fetch(page: 1);
  }

  void _handleSortChanged(int columnIndex, bool ascending) {
    final sortKey = _columnSortKeys[columnIndex];
    if (sortKey == null) {
      return;
    }
    setState(() {
      _sortState = SuperportTableSortState(
        columnIndex: columnIndex,
        ascending: ascending,
      );
    });
    _summaryController
      ..updateSort(sortKey, order: ascending ? 'asc' : 'desc')
      ..fetch(page: 1);
  }

  @override
  Widget build(BuildContext context) {
    final result = _summaryController.result;
    final items = result?.items ?? const <InventorySummary>[];
    final hasData = items.isNotEmpty;
    final lastEventTime = hasData
        ? items.first.recentEvent?.occurredAt ?? items.first.updatedAt
        : null;

    return AppLayout(
      title: '재고 현황',
      subtitle: '제품별 총 수량, 창고 잔량, 최근 변동 이벤트를 모니터링합니다.',
      breadcrumbs: const [
        AppBreadcrumbItem(label: '대시보드', path: dashboardRoutePath),
        AppBreadcrumbItem(label: '재고 현황'),
      ],
      actions: const [
        ShadBadge.outline(
          child: Padding(
            padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
            child: Text('View Only'),
          ),
        ),
      ],
      toolbar: Column(
        children: [
          _buildMetaSection(
            total: result?.total ?? 0,
            lastEventTime: lastEventTime,
            lastRefreshedAt: _summaryController.lastRefreshedAt,
          ),
          const SizedBox(height: 16),
          _buildFilterBar(),
        ],
      ),
      child: Column(
        children: [
          if (_summaryController.errorMessage != null)
            Padding(
              padding: const EdgeInsets.only(bottom: 16),
              child: _ErrorBanner(
                message: _summaryController.errorMessage!,
                onClose: _summaryController.clearError,
              ),
            ),
          _buildTable(items, result),
        ],
      ),
    );
  }

  Widget _buildMetaSection({
    required int total,
    DateTime? lastEventTime,
    DateTime? lastRefreshedAt,
  }) {
    final lastEventLabel = lastEventTime != null
        ? _formatDateTime(lastEventTime)
        : '정보 없음';
    final refreshLabel = lastRefreshedAt != null
        ? _formatDateTime(lastRefreshedAt)
        : '정보 없음';
    return Wrap(
      spacing: 12,
      runSpacing: 12,
      children: [
        _SummaryCard(
          icon: lucide.LucideIcons.box,
          label: '총 제품 수',
          value: total.toString(),
        ),
        _SummaryCard(
          icon: lucide.LucideIcons.activity,
          label: '최근 이벤트 기준',
          value: lastEventLabel,
        ),
        _AutoRefreshCard(
          enabled: _autoRefreshEnabled,
          lastRefreshedAt: refreshLabel,
          interval: _autoRefreshInterval,
          onChanged: _toggleAutoRefresh,
        ),
      ],
    );
  }

  Widget _buildFilterBar() {
    return FilterBar(
      title: '검색 및 필터',
      actionConfig: FilterBarActionConfig(
        onApply: _applyFilters,
        onReset: _resetFilters,
        hasPendingChanges: _hasPendingChanges,
        hasActiveFilters: _hasActiveFilters,
        applyEnabled: _hasPendingChanges,
        resetEnabled: _hasActiveFilters || _hasPendingChanges,
        applyKey: const Key('inventory_filter_apply'),
        resetKey: const Key('inventory_filter_reset'),
      ),
      children: [
        SizedBox(
          width: 280,
          child: ShadInput(
            key: const Key('inventory_filter_query_field'),
            controller: _searchController,
            placeholder: const Text('제품명, 코드 검색'),
            onChanged: (value) => setState(() => _pendingQuery = value),
            leading: const Icon(lucide.LucideIcons.search, size: 16),
          ),
        ),
        SizedBox(
          width: 220,
          child: ShadInput(
            controller: _productController,
            placeholder: const Text('정확 제품명'),
            onChanged: (value) => setState(
              () => _pendingProductName = value.trim().isEmpty ? null : value,
            ),
            leading: const Icon(lucide.LucideIcons.box, size: 16),
          ),
        ),
        SizedBox(
          width: 220,
          child: ShadInput(
            controller: _vendorController,
            placeholder: const Text('벤더명'),
            onChanged: (value) => setState(
              () => _pendingVendorName = value.trim().isEmpty ? null : value,
            ),
            leading: const Icon(lucide.LucideIcons.factory, size: 16),
          ),
        ),
        SizedBox(
          width: 240,
          child: InventoryWarehouseSelectField(
            key: ValueKey(_pendingWarehouseId ?? -1),
            includeAllOption: true,
            initialWarehouseId: _pendingWarehouseId,
            placeholder: const Text('창고 선택'),
            onChanged: (option) {
              setState(() {
                _pendingWarehouseId = option == null || option.id == -1
                    ? null
                    : option.id;
              });
            },
          ),
        ),
        SizedBox(
          width: 200,
          child: SuperportDatePickerButton(
            value: _pendingUpdatedSince,
            placeholder: '업데이트 기준일',
            onChanged: (date) {
              setState(() {
                _pendingUpdatedSince = DateTime(
                  date.year,
                  date.month,
                  date.day,
                );
              });
            },
          ),
        ),
        SuperportSwitchField(
          label: '0 수량 포함',
          value: _pendingIncludeEmpty,
          onChanged: (value) => setState(() => _pendingIncludeEmpty = value),
          caption: '0개 창고도 함께 조회합니다.',
        ),
      ],
    );
  }

  Widget _buildTable(
    List<InventorySummary> items,
    PaginatedResult<InventorySummary>? result,
  ) {
    final rows = <List<ShadTableCell>>[];
    for (var index = 0; index < items.length; index++) {
      rows.add(_buildRow(index, items[index]));
    }
    final pagination = result == null
        ? null
        : SuperportTablePagination(
            currentPage: result.page,
            totalPages: result.pageSize == 0
                ? 1
                : (result.total / result.pageSize).ceil().clamp(1, 9999),
            totalItems: result.total,
            pageSize: result.pageSize == 0
                ? InventorySummaryController.defaultPageSize
                : result.pageSize,
            pageSizeOptions: const [20, 50, 100],
          );

    return ShadCard(
      padding: const EdgeInsets.all(0),
      child: SizedBox(
        width: double.infinity,
        child: SuperportTable.fromCells(
          rowHeight: widget.debugRowHeight ?? 72,
          header: const [
            ShadTableCell.header(child: Text('#')),
            ShadTableCell.header(child: Text('제품명 / 코드')),
            ShadTableCell.header(child: Text('벤더')),
            ShadTableCell.header(child: Text('총 수량')),
            ShadTableCell.header(child: Text('최근 변동')),
            ShadTableCell.header(child: Text('업데이트')),
          ],
          rows: rows,
          columnSpanExtent: _columnSpanForIndex,
          sortableColumns: _columnSortKeys.keys.toSet(),
          sortState: _sortState,
          onSortChanged: _handleSortChanged,
          pagination: pagination,
          onPageChange: (page) => _summaryController.fetch(page: page),
          onPageSizeChange: (pageSize) {
            _summaryController.updatePageSize(pageSize);
            _summaryController.fetch(page: 1);
          },
          onRowTap: (index) => _openDetail(items[index]),
          isLoading: _summaryController.isLoading,
          emptyLabel: _summaryController.isLoading
              ? '재고 데이터를 불러오는 중입니다...'
              : '조건에 맞는 재고 데이터가 없습니다.',
        ),
      ),
    );
  }

  List<ShadTableCell> _buildRow(int index, InventorySummary summary) {
    final theme = ShadTheme.of(context);
    final totalQuantity = summary.totalQuantity;
    final displayTotal = intl.NumberFormat.decimalPattern().format(
      totalQuantity,
    );
    final totalColor = totalQuantity < 0
        ? theme.colorScheme.destructive
        : theme.colorScheme.foreground;
    final rowNumber =
        ((_summaryController.page - 1) * _summaryController.pageSize) +
        index +
        1;

    return [
      ShadTableCell(child: Text('$rowNumber')),
      ShadTableCell(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              summary.product.name,
              style: ShadTheme.of(
                context,
              ).textTheme.small.copyWith(fontWeight: FontWeight.w600),
            ),
            Text(summary.product.code, style: theme.textTheme.muted),
          ],
        ),
      ),
      ShadTableCell(child: Text(summary.product.vendor?.name ?? '-')),
      ShadTableCell(
        child: Text(
          displayTotal,
          style: theme.textTheme.small.copyWith(color: totalColor),
        ),
      ),
      ShadTableCell(child: _RecentEventCell(event: summary.recentEvent)),
      ShadTableCell(child: Text(_formatDateTime(summary.updatedAt))),
    ];
  }

  Future<void> _openDetail(InventorySummary summary) async {
    await showModalBottomSheet<void>(
      context: context,
      isScrollControlled: true,
      builder: (context) =>
          InventoryDetailSheet(controller: _detailController, summary: summary),
    );
  }
}

TableSpanExtent _columnSpanForIndex(int index) {
  final width = _inventorySummaryColumnWidths[index];
  return FixedTableSpanExtent(width ?? 160);
}

bool _isSameDay(DateTime? a, DateTime? b) {
  if (a == null && b == null) {
    return true;
  }
  if (a == null || b == null) {
    return false;
  }
  return a.year == b.year && a.month == b.month && a.day == b.day;
}

String _formatDateTime(DateTime? value) {
  if (value == null) {
    return '-';
  }
  return intl.DateFormat('yyyy-MM-dd HH:mm').format(value.toLocal());
}

class _SummaryCard extends StatelessWidget {
  const _SummaryCard({
    required this.icon,
    required this.label,
    required this.value,
  });

  final IconData icon;
  final String label;
  final String value;

  @override
  Widget build(BuildContext context) {
    final theme = ShadTheme.of(context);
    return ShadCard(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            Icon(icon, size: 20, color: theme.colorScheme.primary),
            const SizedBox(width: 12),
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(label, style: theme.textTheme.muted),
                Text(value, style: theme.textTheme.h3),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

class _AutoRefreshCard extends StatelessWidget {
  const _AutoRefreshCard({
    required this.enabled,
    required this.lastRefreshedAt,
    required this.interval,
    required this.onChanged,
  });

  final bool enabled;
  final String lastRefreshedAt;
  final Duration interval;
  final ValueChanged<bool> onChanged;

  @override
  Widget build(BuildContext context) {
    final theme = ShadTheme.of(context);
    final intervalLabel = '${interval.inSeconds}초마다 자동 새로고침';
    return ShadCard(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Row(
          children: [
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text('자동 새로고침', style: theme.textTheme.muted),
                  const SizedBox(height: 4),
                  Text(
                    '마지막 리프레시: $lastRefreshedAt',
                    style: theme.textTheme.small,
                  ),
                  const SizedBox(height: 4),
                  Text(intervalLabel, style: theme.textTheme.muted),
                ],
              ),
            ),
            Semantics(
              label: '자동 새로고침 전환',
              value: enabled ? '사용 중' : '중지됨',
              toggled: enabled,
              child: ShadSwitch(value: enabled, onChanged: onChanged),
            ),
          ],
        ),
      ),
    );
  }
}

class _WarehouseChips extends StatelessWidget {
  const _WarehouseChips({required this.balances});

  final List<InventoryWarehouseBalance> balances;

  @override
  Widget build(BuildContext context) {
    if (balances.isEmpty) {
      return const Text('-');
    }
    return Wrap(
      spacing: 6,
      runSpacing: 6,
      children: balances.take(3).map((balance) {
        final label = '${balance.warehouse.name} · ${balance.quantity}';
        return ShadBadge(
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
            child: Text(label, style: const TextStyle(fontSize: 12)),
          ),
        );
      }).toList(),
    );
  }
}

class _WarehouseBalanceChart extends StatelessWidget {
  const _WarehouseBalanceChart({required this.balances});

  final List<InventoryWarehouseBalance> balances;

  @override
  Widget build(BuildContext context) {
    if (balances.isEmpty) {
      return const Text('창고 잔량 데이터가 없습니다.');
    }
    final theme = ShadTheme.of(context);
    final maxQuantity = balances
        .map((balance) => balance.quantity.abs())
        .fold<int>(0, (max, value) => value > max ? value : max);
    final safeMax = maxQuantity == 0 ? 1 : maxQuantity;
    return Column(
      children: balances.map((balance) {
        final normalized = balance.quantity.abs() / safeMax;
        final ratio = normalized.clamp(0, 1).toDouble();
        final barColor = balance.quantity < 0
            ? theme.colorScheme.destructive
            : theme.colorScheme.primary;
        final semanticsLabel = '${balance.warehouse.name} 잔량';
        final semanticsValue = '${balance.quantity}개';
        return Padding(
          padding: const EdgeInsets.symmetric(vertical: 6),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              MergeSemantics(
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text(balance.warehouse.name, style: theme.textTheme.small),
                    Text('${balance.quantity}개', style: theme.textTheme.small),
                  ],
                ),
              ),
              const SizedBox(height: 4),
              Semantics(
                label: semanticsLabel,
                value: semanticsValue,
                child: ClipRRect(
                  borderRadius: BorderRadius.circular(999),
                  child: LinearProgressIndicator(
                    value: ratio,
                    minHeight: 8,
                    backgroundColor: theme.colorScheme.mutedForeground
                        .withValues(alpha: 0.2),
                    valueColor: AlwaysStoppedAnimation<Color>(barColor),
                  ),
                ),
              ),
            ],
          ),
        );
      }).toList(),
    );
  }
}

class _RecentEventCell extends StatelessWidget {
  const _RecentEventCell({required this.event});

  final InventoryEvent? event;

  @override
  Widget build(BuildContext context) {
    final theme = ShadTheme.of(context);
    if (event == null) {
      return Semantics(
        label: '최근 이벤트 없음',
        child: Text('최근 이벤트 없음', style: theme.textTheme.muted),
      );
    }
    final delta = event!.deltaQuantity;
    final deltaText = delta > 0 ? '+$delta' : '$delta';
    final deltaColor = delta > 0
        ? theme.colorScheme.primary
        : delta < 0
        ? theme.colorScheme.destructive
        : theme.colorScheme.foreground;
    final icon = _eventIcon(event!.eventKind);
    final occurredLabel = _formatDateTime(event!.occurredAt);
    final changeDescriptor = delta > 0
        ? '증가'
        : delta < 0
        ? '감소'
        : '변화 없음';
    final counterpartyName = event!.counterparty?.name;
    final semanticsValue = StringBuffer()
      ..write('수량 ${delta.abs()}개 $changeDescriptor, 발생 $occurredLabel');
    if (counterpartyName != null) {
      semanticsValue.write(', 거래처 $counterpartyName');
    }
    return Semantics(
      label: '최근 이벤트 ${event!.eventLabel}',
      value: semanticsValue.toString(),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              Icon(icon, size: 14, color: theme.colorScheme.primary),
              const SizedBox(width: 6),
              Expanded(
                child: Text(
                  event!.eventLabel,
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                ),
              ),
              const SizedBox(width: 6),
              Text(
                deltaText,
                maxLines: 1,
                overflow: TextOverflow.ellipsis,
                style: TextStyle(color: deltaColor),
              ),
            ],
          ),
          const SizedBox(height: 4),
          Text(
            occurredLabel,
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
            style: theme.textTheme.muted,
          ),
          if (counterpartyName != null) ...[
            const SizedBox(height: 2),
            Text(
              '거래처: $counterpartyName',
              maxLines: 1,
              overflow: TextOverflow.ellipsis,
              style: theme.textTheme.muted,
            ),
          ],
        ],
      ),
    );
  }
}

class InventoryDetailSheet extends StatefulWidget {
  const InventoryDetailSheet({
    super.key,
    required this.controller,
    required this.summary,
  });

  final InventoryDetailController controller;
  final InventorySummary summary;

  @override
  State<InventoryDetailSheet> createState() => _InventoryDetailSheetState();
}

class _InventoryDetailSheetState extends State<InventoryDetailSheet> {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      widget.controller.fetch(widget.summary.product.id, force: true);
    });
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: widget.controller,
      builder: (context, _) {
        final productId = widget.summary.product.id;
        final detail = widget.controller.detailOf(productId);
        final filter = widget.controller.filterOf(productId);
        final isLoading = widget.controller.isLoading(productId);
        final error = widget.controller.errorOf(productId);
        final theme = ShadTheme.of(context);

        return DraggableScrollableSheet(
          expand: false,
          initialChildSize: 0.7,
          maxChildSize: 0.95,
          builder: (context, scrollController) {
            final scrollBehavior = ScrollConfiguration.of(
              context,
            ).copyWith(scrollbars: false);
            final scrollbarTheme = ScrollbarTheme.of(context);
            final resolvedThickness =
                scrollbarTheme.thickness?.resolve({WidgetState.hovered}) ??
                scrollbarTheme.thickness?.resolve(const <WidgetState>{}) ??
                6.0;

            return ShadCard(
              child: ScrollConfiguration(
                behavior: scrollBehavior,
                child: Scrollbar(
                  controller: scrollController,
                  thickness: resolvedThickness,
                  radius: scrollbarTheme.radius ?? const Radius.circular(999),
                  child: Padding(
                    padding: const EdgeInsets.fromLTRB(24, 16, 24, 32),
                    child: SingleChildScrollView(
                      controller: scrollController,
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Row(
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
                            children: [
                              Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  Text(
                                    widget.summary.product.name,
                                    style: theme.textTheme.h3,
                                  ),
                                  Text(
                                    widget.summary.product.code,
                                    style: theme.textTheme.muted,
                                  ),
                                ],
                              ),
                              IconButton(
                                icon: const Icon(lucide.LucideIcons.x),
                                onPressed: () => Navigator.of(context).pop(),
                              ),
                            ],
                          ),
                          const SizedBox(height: 16),
                          Wrap(
                            spacing: 12,
                            runSpacing: 12,
                            children: [
                              _SummaryCard(
                                icon: lucide.LucideIcons.box,
                                label: '총 수량',
                                value: '${widget.summary.totalQuantity}',
                              ),
                              _SummaryCard(
                                icon: lucide.LucideIcons.refreshCw,
                                label: '뷰 리프레시',
                                value: _formatDateTime(detail?.lastRefreshedAt),
                              ),
                            ],
                          ),
                          const SizedBox(height: 16),
                          Row(
                            children: [
                              SizedBox(
                                width: 180,
                                child: ShadSelect<int>(
                                  key: ValueKey(filter.eventLimit),
                                  initialValue: filter.eventLimit,
                                  selectedOptionBuilder: (context, value) =>
                                      Text('$value건'),
                                  onChanged: (value) {
                                    if (value != null) {
                                      widget.controller.updateEventLimit(
                                        productId,
                                        value,
                                      );
                                    }
                                  },
                                  options: [10, 20, 50, 100]
                                      .map(
                                        (limit) => ShadOption(
                                          value: limit,
                                          child: Text('$limit건'),
                                        ),
                                      )
                                      .toList(),
                                ),
                              ),
                              const SizedBox(width: 12),
                              SizedBox(
                                width: 220,
                                child: InventoryWarehouseSelectField(
                                  includeAllOption: true,
                                  initialWarehouseId: filter.warehouseId,
                                  onChanged: (option) {
                                    widget.controller.updateWarehouseFilter(
                                      productId,
                                      option == null || option.id == -1
                                          ? null
                                          : option.id,
                                    );
                                  },
                                ),
                              ),
                            ],
                          ),
                          const SizedBox(height: 16),
                          if (isLoading)
                            const Center(
                              child: Padding(
                                padding: EdgeInsets.all(24),
                                child: CircularProgressIndicator(),
                              ),
                            ),
                          if (error != null)
                            Padding(
                              padding: const EdgeInsets.only(bottom: 16),
                              child: _ErrorBanner(
                                message: error,
                                onClose: () =>
                                    widget.controller.clearError(productId),
                              ),
                            ),
                          if (detail != null) ...[
                            Text('창고 잔량', style: theme.textTheme.h3),
                            const SizedBox(height: 8),
                            _WarehouseBalanceChart(
                              balances: detail.warehouseBalances,
                            ),
                            const SizedBox(height: 12),
                            _WarehouseChips(balances: detail.warehouseBalances),
                            const SizedBox(height: 16),
                            Text('최근 이벤트', style: theme.textTheme.h3),
                            const SizedBox(height: 8),
                            if (detail.recentEvents.isEmpty)
                              Text(
                                '최근 이벤트가 없습니다.',
                                style: theme.textTheme.muted,
                              )
                            else
                              Column(
                                children: detail.recentEvents.map((event) {
                                  return Padding(
                                    padding: const EdgeInsets.symmetric(
                                      vertical: 8,
                                    ),
                                    child: _RecentEventCell(event: event),
                                  );
                                }).toList(),
                              ),
                          ],
                        ],
                      ),
                    ),
                  ),
                ),
              ),
            );
          },
        );
      },
    );
  }
}

class _ErrorBanner extends StatelessWidget {
  const _ErrorBanner({required this.message, required this.onClose});

  final String message;
  final VoidCallback onClose;

  @override
  Widget build(BuildContext context) {
    final theme = ShadTheme.of(context);
    return ShadCard(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Icon(
              lucide.LucideIcons.info,
              size: 18,
              color: theme.colorScheme.destructive,
            ),
            const SizedBox(width: 12),
            Expanded(child: Text(message, style: theme.textTheme.small)),
            ShadButton.ghost(onPressed: onClose, child: const Text('닫기')),
          ],
        ),
      ),
    );
  }
}

IconData _eventIcon(String kind) {
  switch (kind) {
    case 'receipt':
      return lucide.LucideIcons.packagePlus;
    case 'issue':
      return lucide.LucideIcons.packageMinus;
    case 'rental_out':
      return lucide.LucideIcons.share2;
    case 'rental_return':
      return lucide.LucideIcons.undo2;
    default:
      return lucide.LucideIcons.clock3;
  }
}
