import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart';
import '../../connection/connection.dart';
import '../../connection/connection_registry.dart';
import '../../database/app_database.dart';
import '../../focus/focusable_wrapper.dart';
import '../../focus/input_mode_tracker.dart';
import '../../media/media_item.dart';
import '../../providers/download_provider.dart';
import '../../providers/multi_server_provider.dart';
import '../../services/sync_rule_executor.dart';
import '../../utils/content_utils.dart';
import '../../utils/download_utils.dart';
import '../../widgets/focused_scroll_scaffold.dart';
import '../libraries/state_messages.dart';
import '../../i18n/strings.g.dart';

class SyncRulesScreen extends StatelessWidget {
  const SyncRulesScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Consumer<DownloadProvider>(
      builder: (context, downloadProvider, _) {
        final syncRules = downloadProvider.syncRules;
        final multiServerProvider = context.watch<MultiServerProvider>();
        final connectionRegistry = context.read<ConnectionRegistry>();

        return StreamBuilder<List<Connection>>(
          stream: connectionRegistry.watchConnections(),
          initialData: const [],
          builder: (context, snapshot) {
            final connections = snapshot.data ?? const <Connection>[];
            final autofocusFirstRule = InputModeTracker.isKeyboardMode(context);
            return FocusedScrollScaffold(
              title: Text(t.downloads.activeSyncRules),
              slivers: [
                if (syncRules.isEmpty)
                  SliverFillRemaining(
                    child: EmptyStateWidget(message: t.downloads.noSyncRules, icon: Symbols.sync_rounded, iconSize: 80),
                  )
                else
                  SliverList(
                    delegate: SliverChildBuilderDelegate((context, index) {
                      final entry = syncRules.entries.elementAt(index);
                      final rule = entry.value;
                      return _SyncRuleTile(
                        rule: rule,
                        metadata: downloadProvider.metadata,
                        downloadProvider: downloadProvider,
                        multiServerProvider: multiServerProvider,
                        connections: connections,
                        autofocus: autofocusFirstRule && index == 0,
                      );
                    }, childCount: syncRules.length),
                  ),
              ],
            );
          },
        );
      },
    );
  }
}

class _RuleServerInfo {
  final String label;
  final bool isKnown;

  const _RuleServerInfo({required this.label, required this.isKnown});
}

class _SyncRuleTile extends StatefulWidget {
  final SyncRuleItem rule;
  final Map<String, MediaItem> metadata;
  final DownloadProvider downloadProvider;
  final MultiServerProvider multiServerProvider;
  final List<Connection> connections;
  final bool autofocus;

  const _SyncRuleTile({
    required this.rule,
    required this.metadata,
    required this.downloadProvider,
    required this.multiServerProvider,
    required this.connections,
    this.autofocus = false,
  });

  @override
  State<_SyncRuleTile> createState() => _SyncRuleTileState();
}

class _SyncRuleTileState extends State<_SyncRuleTile> {
  final _rowFocusNode = FocusNode(debugLabel: 'sync_rule_row');
  final _switchFocusNode = FocusNode(debugLabel: 'sync_rule_switch');

  SyncRuleItem get rule => widget.rule;
  Map<String, MediaItem> get metadata => widget.metadata;
  DownloadProvider get downloadProvider => widget.downloadProvider;
  MultiServerProvider get multiServerProvider => widget.multiServerProvider;
  List<Connection> get connections => widget.connections;

  @override
  void dispose() {
    _rowFocusNode.dispose();
    _switchFocusNode.dispose();
    super.dispose();
  }

  IconData _leadingIcon() {
    switch (rule.targetType) {
      case ContentTypes.playlist:
        return Symbols.playlist_play_rounded;
      case ContentTypes.collection:
        return Symbols.collections_bookmark_rounded;
      case ContentTypes.show:
      case ContentTypes.season:
        return Symbols.tv_rounded;
      default:
        return Symbols.sync_rounded;
    }
  }

  String _subtitle() {
    switch (rule.targetType) {
      case ContentTypes.collection:
      case ContentTypes.playlist:
        return rule.downloadFilter == SyncRuleFilter.all ? t.downloads.syncAllItems : t.downloads.syncUnwatchedItems;
      default:
        return t.downloads.keepNUnwatched(count: rule.episodeCount.toString());
    }
  }

  _RuleServerInfo _serverLabelForRule() {
    final activeName = multiServerProvider.getClientForServer(rule.serverId)?.serverName;
    if (activeName != null && activeName.isNotEmpty) {
      return _RuleServerInfo(label: activeName, isKnown: true);
    }

    final publicGlobalKey = '${rule.serverId}:${rule.ratingKey}';
    final meta = metadata[rule.globalKey] ?? metadata[publicGlobalKey];
    final metadataName = meta?.serverName;
    if (metadataName != null && metadataName.isNotEmpty) {
      return _RuleServerInfo(label: metadataName, isKnown: true);
    }

    for (final connection in connections) {
      switch (connection) {
        case PlexAccountConnection(:final servers):
          for (final server in servers) {
            if (server.clientIdentifier == rule.serverId && server.name.isNotEmpty) {
              return _RuleServerInfo(label: server.name, isKnown: true);
            }
          }
        case JellyfinConnection(:final serverMachineId, :final serverName):
          if (serverMachineId == rule.serverId && serverName.isNotEmpty) {
            return _RuleServerInfo(label: serverName, isKnown: true);
          }
      }
    }

    return _RuleServerInfo(label: rule.serverId, isKnown: false);
  }

  String _serverStatusForRule(_RuleServerInfo serverInfo) {
    if (!serverInfo.isKnown) return t.downloads.syncRuleUnknownServer;
    if (multiServerProvider.authErrorServerIds.contains(rule.serverId)) return t.downloads.syncRuleSignInRequired;
    if (!multiServerProvider.serverIds.contains(rule.serverId)) return t.downloads.syncRuleNotAvailableForProfile;
    return multiServerProvider.isServerOnline(rule.serverId)
        ? t.downloads.syncRuleAvailable
        : t.downloads.syncRuleOffline;
  }

  Future<void> _onTap(BuildContext context) async {
    if (rule.isListRule) {
      await editSyncRuleFilter(
        context,
        downloadProvider: downloadProvider,
        globalKey: rule.globalKey,
        currentFilter: rule.downloadFilter,
      );
    } else {
      await editSyncRuleCount(
        context,
        downloadProvider: downloadProvider,
        globalKey: rule.globalKey,
        currentCount: rule.episodeCount,
        displayTitle: _title(),
      );
    }
  }

  String _title() {
    final publicGlobalKey = '${rule.serverId}:${rule.ratingKey}';
    final meta = metadata[rule.globalKey] ?? metadata[publicGlobalKey];
    return meta?.title ?? rule.ratingKey;
  }

  Future<void> _removeRule(BuildContext context) async {
    await removeSyncRuleAndSnack(
      context,
      downloadProvider: downloadProvider,
      globalKey: rule.globalKey,
      displayTitle: _title(),
    );
  }

  @override
  Widget build(BuildContext context) {
    final title = _title();
    final serverInfo = _serverLabelForRule();
    final serverLine = t.downloads.syncRuleServerContext(
      server: serverInfo.label,
      status: _serverStatusForRule(serverInfo),
    );

    return _SwipeRevealDeleteAction(
      onDelete: () => _removeRule(context),
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
        child: FocusableWrapper(
          autofocus: widget.autofocus,
          focusNode: _rowFocusNode,
          disableScale: true,
          useBackgroundFocus: true,
          borderRadius: 12,
          onSelect: () => _onTap(context),
          onNavigateRight: () => _switchFocusNode.requestFocus(),
          child: ListTile(
            dense: true,
            visualDensity: const VisualDensity(vertical: -3),
            shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12))),
            leading: Icon(_leadingIcon(), color: rule.enabled ? Colors.teal : null, size: 20),
            title: Text(title, maxLines: 1, overflow: TextOverflow.ellipsis),
            subtitle: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisSize: MainAxisSize.min,
              children: [
                Text(_subtitle(), maxLines: 1, overflow: TextOverflow.ellipsis),
                Text(serverLine, maxLines: 1, overflow: TextOverflow.ellipsis),
              ],
            ),
            trailing: FocusableWrapper(
              focusNode: _switchFocusNode,
              disableScale: true,
              useBackgroundFocus: true,
              descendantsAreFocusable: false,
              borderRadius: 20,
              onSelect: () => downloadProvider.setSyncRuleEnabled(rule.globalKey, !rule.enabled),
              onNavigateLeft: () => _rowFocusNode.requestFocus(),
              child: Switch(
                value: rule.enabled,
                onChanged: (value) => downloadProvider.setSyncRuleEnabled(rule.globalKey, value),
              ),
            ),
            onTap: () => _onTap(context),
          ),
        ),
      ),
    );
  }
}

class _SwipeRevealDeleteAction extends StatefulWidget {
  final Widget child;
  final VoidCallback onDelete;

  const _SwipeRevealDeleteAction({required this.child, required this.onDelete});

  @override
  State<_SwipeRevealDeleteAction> createState() => _SwipeRevealDeleteActionState();
}

class _SwipeRevealDeleteActionState extends State<_SwipeRevealDeleteAction> {
  static const double _deleteWidth = 88;
  double _dragExtent = 0;

  void _handleDragUpdate(DragUpdateDetails details) {
    setState(() {
      _dragExtent = (_dragExtent - details.delta.dx).clamp(0, _deleteWidth);
    });
  }

  void _handleDragEnd(DragEndDetails details) {
    final shouldOpen =
        _dragExtent > _deleteWidth / 2 || details.primaryVelocity != null && details.primaryVelocity! < -500;
    setState(() => _dragExtent = shouldOpen ? _deleteWidth : 0);
  }

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;
    final theme = Theme.of(context);
    return ClipRect(
      child: Stack(
        children: [
          if (_dragExtent > 0)
            Positioned.fill(
              right: 8,
              child: Align(
                alignment: Alignment.centerRight,
                child: SizedBox(
                  width: _deleteWidth,
                  child: ExcludeFocus(
                    child: Padding(
                      padding: const EdgeInsets.symmetric(vertical: 4),
                      child: Material(
                        color: colorScheme.error,
                        borderRadius: BorderRadius.circular(12),
                        clipBehavior: Clip.antiAlias,
                        child: InkWell(
                          key: const ValueKey('sync_rule_swipe_delete'),
                          onTap: widget.onDelete,
                          child: Tooltip(
                            message: t.downloads.removeSyncRule,
                            child: Column(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: [
                                Icon(Symbols.delete_rounded, color: colorScheme.onError, size: 20),
                                const SizedBox(height: 2),
                                Text(
                                  t.common.delete,
                                  style: theme.textTheme.labelSmall?.copyWith(
                                    color: colorScheme.onError,
                                    fontWeight: FontWeight.w600,
                                  ),
                                ),
                              ],
                            ),
                          ),
                        ),
                      ),
                    ),
                  ),
                ),
              ),
            ),
          GestureDetector(
            behavior: HitTestBehavior.translucent,
            onHorizontalDragUpdate: _handleDragUpdate,
            onHorizontalDragEnd: _handleDragEnd,
            child: AnimatedContainer(
              duration: const Duration(milliseconds: 160),
              curve: Curves.easeOutCubic,
              transform: Matrix4.translationValues(-_dragExtent, 0, 0),
              child: DecoratedBox(
                decoration: BoxDecoration(color: theme.scaffoldBackgroundColor),
                child: widget.child,
              ),
            ),
          ),
        ],
      ),
    );
  }
}
