import 'dart:convert';
import 'dart:io';

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';

import '../core/contracts/danmaku.dart';
import 'shared/settings_persistence.dart';

enum ChineseConvertMode { none, toSimplified, toTraditional }

DanmakuPanelSettings defaultDanmakuPanelSettings({bool? isAndroid}) {
  final android = isAndroid ?? Platform.isAndroid;
  return DanmakuPanelSettings(
    opacity: android ? 0.9 : 1.0,
    fontSize: android ? 18 : 20,
    strokeWidth: android ? 1.2 : 1.5,
    lineHeight: android ? 1.4 : 1.6,
    maxRows: android ? 3 : 0,
  );
}

class DanmakuSettingsState {
  final bool enabled;
  final ChineseConvertMode chineseConvert;
  final List<DanmakuSource> sources;
  final List<String> blockedKeywords;
  final bool mergeDuplicates;
  final DanmakuPanelSettings panelSettings;

  const DanmakuSettingsState({
    this.enabled = false,
    this.chineseConvert = ChineseConvertMode.none,
    this.sources = const [],
    this.blockedKeywords = const [],
    this.mergeDuplicates = false,
    this.panelSettings = const DanmakuPanelSettings(),
  });

  List<DanmakuSource> get enabledSources =>
      sources.where((s) => s.enabled && s.url.trim().isNotEmpty).toList();

  String get sourceUrl =>
      enabledSources.isNotEmpty ? enabledSources.first.url : '';

  DanmakuSettingsState copyWith({
    bool? enabled,
    ChineseConvertMode? chineseConvert,
    List<DanmakuSource>? sources,
    List<String>? blockedKeywords,
    bool? mergeDuplicates,
    DanmakuPanelSettings? panelSettings,
  }) => DanmakuSettingsState(
    enabled: enabled ?? this.enabled,
    chineseConvert: chineseConvert ?? this.chineseConvert,
    sources: sources ?? this.sources,
    blockedKeywords: blockedKeywords ?? this.blockedKeywords,
    mergeDuplicates: mergeDuplicates ?? this.mergeDuplicates,
    panelSettings: panelSettings ?? this.panelSettings,
  );
}

class DanmakuSettingsNotifier extends AsyncNotifier<DanmakuSettingsState>
    with SettingsPersistence {
  static const _keyEnabled = 'smplayer.danmaku_enabled';
  static const _keyChineseConvert = 'smplayer.danmaku_chinese_convert';
  static const _keySourceUrl = 'smplayer.danmaku_source_url';
  static const _keySources = 'smplayer.danmaku_sources';
  static const _keyBlockedKeywords = 'smplayer.danmaku_blocked_keywords';
  static const _keyMergeDuplicates = 'smplayer.danmaku_merge_duplicates';

  static const _keyPanelEnabled = 'smplayer.danmaku_panel_enabled';
  static const _keyPanelOpacity = 'smplayer.danmaku_panel_opacity';
  static const _keyPanelArea = 'smplayer.danmaku_panel_area';
  static const _keyPanelFontSize = 'smplayer.danmaku_panel_font_size';
  static const _keyPanelSpeed = 'smplayer.danmaku_panel_speed';
  static const _keyPanelOffset = 'smplayer.danmaku_panel_offset';
  static const _keyPanelShowTop = 'smplayer.danmaku_panel_show_top';
  static const _keyPanelShowScroll = 'smplayer.danmaku_panel_show_scroll';
  static const _keyPanelShowBottom = 'smplayer.danmaku_panel_show_bottom';
  static const _keyPanelColored = 'smplayer.danmaku_panel_colored';
  static const _keyPanelStrokeLegacy = 'smplayer.danmaku_panel_stroke';
  static const _keyPanelStrokeWidth = 'smplayer.danmaku_panel_stroke_width';
  static const _keyPanelBold = 'smplayer.danmaku_panel_bold';
  static const _keyPanelFontFamily = 'smplayer.danmaku_panel_font_family';
  static const _keyPanelLineHeight = 'smplayer.danmaku_panel_line_height';
  static const _keyPanelFixedToScroll =
      'smplayer.danmaku_panel_fixed_to_scroll';
  static const _keyPanelMaxRows = 'smplayer.danmaku_panel_max_rows';
  static const _keyPanelHeatmapEnabled =
      'smplayer.danmaku_panel_heatmap_enabled';

  @override
  Future<DanmakuSettingsState> build() async {
    final prefs = await getPrefs();
    return DanmakuSettingsState(
      enabled: prefs.getBool(_keyEnabled) ?? false,
      chineseConvert: _parseConvert(prefs.getString(_keyChineseConvert)),
      sources: _parseSources(
        prefs.getStringList(_keySources),
        legacyUrl: prefs.getString(_keySourceUrl),
      ),
      blockedKeywords: prefs.getStringList(_keyBlockedKeywords) ?? [],
      mergeDuplicates: prefs.getBool(_keyMergeDuplicates) ?? false,
      panelSettings: _loadPanelSettings(prefs),
    );
  }

  DanmakuPanelSettings _loadPanelSettings(SharedPreferences prefs) {
    final defaults = defaultDanmakuPanelSettings();
    return DanmakuPanelSettings(
      enabled: prefs.getBool(_keyPanelEnabled) ?? defaults.enabled,
      opacity: prefs.getDouble(_keyPanelOpacity) ?? defaults.opacity,
      area: prefs.getDouble(_keyPanelArea) ?? defaults.area,
      fontSize: prefs.getDouble(_keyPanelFontSize) ?? defaults.fontSize,
      speed: prefs.getDouble(_keyPanelSpeed) ?? defaults.speed,
      offset: prefs.getDouble(_keyPanelOffset) ?? defaults.offset,
      showTop: prefs.getBool(_keyPanelShowTop) ?? defaults.showTop,
      showScroll: prefs.getBool(_keyPanelShowScroll) ?? defaults.showScroll,
      showBottom: prefs.getBool(_keyPanelShowBottom) ?? defaults.showBottom,
      colored: prefs.getBool(_keyPanelColored) ?? defaults.colored,
      strokeWidth:
          prefs.getDouble(_keyPanelStrokeWidth) ??
          ((prefs.getBool(_keyPanelStrokeLegacy) ?? true)
              ? defaults.strokeWidth
              : 0.0),
      bold: prefs.getBool(_keyPanelBold) ?? defaults.bold,
      fontFamily: prefs.getString(_keyPanelFontFamily) ?? defaults.fontFamily,
      lineHeight: prefs.getDouble(_keyPanelLineHeight) ?? defaults.lineHeight,
      fixedToScroll:
          prefs.getBool(_keyPanelFixedToScroll) ?? defaults.fixedToScroll,
      maxRows: prefs.getInt(_keyPanelMaxRows) ?? defaults.maxRows,
      heatmapEnabled:
          prefs.getBool(_keyPanelHeatmapEnabled) ?? defaults.heatmapEnabled,
    );
  }

  static ChineseConvertMode _parseConvert(String? s) => switch (s) {
    'simplified' => ChineseConvertMode.toSimplified,
    'traditional' => ChineseConvertMode.toTraditional,
    _ => ChineseConvertMode.none,
  };

  static String _convertToStr(ChineseConvertMode m) => switch (m) {
    ChineseConvertMode.toSimplified => 'simplified',
    ChineseConvertMode.toTraditional => 'traditional',
    ChineseConvertMode.none => 'none',
  };

  static List<DanmakuSource> _parseSources(
    List<String>? raw, {
    required String? legacyUrl,
  }) {
    final parsed = <DanmakuSource>[];
    for (final item in raw ?? const <String>[]) {
      try {
        final data = jsonDecode(item);
        if (data is Map<String, dynamic>) {
          final source = DanmakuSource.fromJson(data);
          if (source.url.isNotEmpty) parsed.add(source);
        }
      } catch (_) {
        final url = item.trim();
        if (url.isNotEmpty) {
          parsed.add(
            DanmakuSource(id: _sourceIdFromUrl(url), name: '', url: url),
          );
        }
      }
    }
    if (parsed.isEmpty) {
      final url = legacyUrl?.trim() ?? '';
      if (url.isNotEmpty) {
        parsed.add(
          DanmakuSource(id: _sourceIdFromUrl(url), name: '默认源', url: url),
        );
      } else {
        parsed.add(
          DanmakuSource(
            id: _sourceIdFromUrl('https://api.dandanplay.net'),
            name: '弹弹play',
            url: 'https://api.dandanplay.net',
          ),
        );
      }
    }
    return _normalizeSources(parsed);
  }

  static List<DanmakuSource> _normalizeSources(List<DanmakuSource> sources) {
    final seen = <String>{};
    final result = <DanmakuSource>[];
    for (final source in sources) {
      final url = source.url.trim();
      if (url.isEmpty || seen.contains(url)) continue;
      seen.add(url);
      result.add(
        source.copyWith(
          id: source.id.trim().isEmpty ? _sourceIdFromUrl(url) : source.id,
          url: url,
        ),
      );
    }
    return result;
  }

  static String _sourceIdFromUrl(String url) =>
      'src_${url.hashCode.toUnsigned(32).toRadixString(16)}';

  Future<void> setEnabled(bool v) async {
    final current = state.value ?? const DanmakuSettingsState();
    state = AsyncValue.data(current.copyWith(enabled: v));
    await persistField(_keyEnabled, v);
  }

  Future<void> setChineseConvert(ChineseConvertMode mode) async {
    final current = state.value ?? const DanmakuSettingsState();
    state = AsyncValue.data(current.copyWith(chineseConvert: mode));
    await persistField(_keyChineseConvert, _convertToStr(mode));
  }


  Future<List<DanmakuSource>> setSources(List<DanmakuSource> sources) async {
    final current = state.value ?? const DanmakuSettingsState();
    final normalized = _normalizeSources(sources);
    state = AsyncValue.data(current.copyWith(sources: normalized));
    final enabled = normalized.where((s) => s.enabled).toList();
    final enabledUrl = enabled.isEmpty ? null : enabled.first.url;
    await persistFields({
      _keySources: normalized.map((s) => jsonEncode(s.toJson())).toList(),
      _keySourceUrl: enabledUrl,
    });
    return normalized;
  }

  Future<void> addSource({required String name, required String url}) async {
    final current = state.value ?? const DanmakuSettingsState();
    final trimmed = url.trim();
    if (trimmed.isEmpty) return;
    final source = DanmakuSource(
      id: _sourceIdFromUrl(trimmed),
      name: name.trim(),
      url: trimmed,
    );
    final existing = current.sources.indexWhere((s) => s.url == trimmed);
    final next = [...current.sources];
    if (existing >= 0) {
      next[existing] = source.copyWith(id: next[existing].id);
    } else {
      next.add(source);
    }
    await setSources(next);
  }

  Future<void> removeSource(String id) async {
    final current = state.value ?? const DanmakuSettingsState();
    await setSources(current.sources.where((s) => s.id != id).toList());
  }

  Future<void> addBlockedKeyword(String keyword) async {
    final current = state.value ?? const DanmakuSettingsState();
    if (current.blockedKeywords.contains(keyword)) return;
    final updated = [...current.blockedKeywords, keyword];
    state = AsyncValue.data(current.copyWith(blockedKeywords: updated));
    await persistField(_keyBlockedKeywords, updated);
  }

  Future<void> removeBlockedKeyword(String keyword) async {
    final current = state.value ?? const DanmakuSettingsState();
    final updated = current.blockedKeywords.where((k) => k != keyword).toList();
    state = AsyncValue.data(current.copyWith(blockedKeywords: updated));
    await persistField(_keyBlockedKeywords, updated);
  }

  Future<void> setMergeDuplicates(bool v) async {
    final current = state.value ?? const DanmakuSettingsState();
    state = AsyncValue.data(current.copyWith(mergeDuplicates: v));
    await persistField(_keyMergeDuplicates, v);
  }

  Future<void> updatePanelSettings(DanmakuPanelSettings panel) async {
    setPanelSettings(panel);
    await persistPanelSettings(panel);
  }

  void setPanelSettings(DanmakuPanelSettings panel) {
    final current = state.value ?? const DanmakuSettingsState();
    state = AsyncValue.data(current.copyWith(panelSettings: panel));
  }

  Future<void> persistPanelSettings(DanmakuPanelSettings panel) async {
    await persistFields({
      _keyPanelEnabled: panel.enabled,
      _keyPanelOpacity: panel.opacity,
      _keyPanelArea: panel.area,
      _keyPanelFontSize: panel.fontSize,
      _keyPanelSpeed: panel.speed,
      _keyPanelOffset: panel.offset,
      _keyPanelShowTop: panel.showTop,
      _keyPanelShowScroll: panel.showScroll,
      _keyPanelShowBottom: panel.showBottom,
      _keyPanelColored: panel.colored,
      _keyPanelStrokeWidth: panel.strokeWidth,
      _keyPanelBold: panel.bold,
      _keyPanelFontFamily: panel.fontFamily,
      _keyPanelLineHeight: panel.lineHeight,
      _keyPanelFixedToScroll: panel.fixedToScroll,
      _keyPanelMaxRows: panel.maxRows,
      _keyPanelHeatmapEnabled: panel.heatmapEnabled,
    });
  }
}

final danmakuSettingsProvider =
    AsyncNotifierProvider<DanmakuSettingsNotifier, DanmakuSettingsState>(
      DanmakuSettingsNotifier.new,
    );
