import 'dart:convert';

import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../core/contracts/icon_library.dart';
import '../core/infra/icon_library_api/icon_library_client.dart';
import 'shared/settings_persistence.dart';


enum IconLibraryStatus { idle, loading, loaded, error }

class IconLibraryEntryState {
  final String url;
  final IconLibrary? library;
  final IconLibraryStatus status;
  final String? error;

  const IconLibraryEntryState({
    required this.url,
    this.library,
    this.status = IconLibraryStatus.idle,
    this.error,
  });

  IconLibraryEntryState copyWith({
    IconLibrary? library,
    IconLibraryStatus? status,
    String? error,
    bool clearError = false,
  }) => IconLibraryEntryState(
    url: url,
    library: library ?? this.library,
    status: status ?? this.status,
    error: clearError ? null : (error ?? this.error),
  );

  int get iconCount => library?.icons.length ?? 0;
}

class IconLibrarySettingsState {

  final List<IconLibraryEntryState> entries;

  const IconLibrarySettingsState({this.entries = const []});

  IconLibrarySettingsState copyWith({List<IconLibraryEntryState>? entries}) =>
      IconLibrarySettingsState(entries: entries ?? this.entries);

  int get totalIcons => entries.fold<int>(0, (acc, e) => acc + e.iconCount);
}

class IconLibrarySettingsNotifier
    extends AsyncNotifier<IconLibrarySettingsState>
    with SettingsPersistence {
  static const _keyUrls = 'smplayer.icon_library_urls';
  static const _keyCache = 'smplayer.icon_library_cache';
  static const _keyInitialized = 'smplayer.icon_library_initialized';


  static const defaultUrls = <String>[
    'https://emby-icon.vercel.app/TFEL-Emby.json',
    'https://raw.githubusercontent.com/xiyuliu509/Player-Icon/refs/heads/master/invisible.json',
    'https://raw.githubusercontent.com/ginibond/ginibond/main/Icons/Forward/tubiao.json',
  ];

  late final IconLibraryClient _client;

  @override
  Future<IconLibrarySettingsState> build() async {
    _client = IconLibraryClient();

    final prefs = await getPrefs();
    final initialized = prefs.getBool(_keyInitialized) ?? false;
    List<String> urls = prefs.getStringList(_keyUrls) ?? const [];

    if (!initialized && urls.isEmpty) {
      urls = List.of(defaultUrls);
      await persistFields({_keyUrls: urls, _keyInitialized: true});
    } else if (!initialized) {
      await persistField(_keyInitialized, true);
    }

    final cacheRaw = prefs.getString(_keyCache);
    final cache = <String, IconLibrary>{};
    if (cacheRaw != null && cacheRaw.isNotEmpty) {
      try {
        final decoded = jsonDecode(cacheRaw);
        if (decoded is Map<String, dynamic>) {
          for (final e in decoded.entries) {
            final value = e.value;
            if (value is Map<String, dynamic>) {
              cache[e.key] = IconLibrary.parse(e.key, value);
            }
          }
        }
      } catch (err) {
        debugPrint('[IconLibrary] failed to decode cache: $err');
      }
    }

    final entries = urls
        .map(
          (u) => IconLibraryEntryState(
            url: u,
            library: cache[u],
            status: cache[u] != null
                ? IconLibraryStatus.loaded
                : IconLibraryStatus.idle,
          ),
        )
        .toList();

    final initialState = IconLibrarySettingsState(entries: entries);

    Future.microtask(() async {
      for (final e in entries) {
        if (e.library == null) {
          await refresh(e.url);
        }
      }
    });

    return initialState;
  }


  Future<void> addUrl(String url) async {
    final trimmed = url.trim();
    if (trimmed.isEmpty) return;

    final current = state.value ?? const IconLibrarySettingsState();
    if (current.entries.any((e) => e.url == trimmed)) return;

    final next = [
      ...current.entries,
      IconLibraryEntryState(url: trimmed, status: IconLibraryStatus.loading),
    ];
    state = AsyncValue.data(current.copyWith(entries: next));
    await _persistUrls(next);
    await refresh(trimmed);
  }


  Future<void> removeUrl(String url) async {
    final current = state.value ?? const IconLibrarySettingsState();
    final next = current.entries.where((e) => e.url != url).toList();
    if (next.length == current.entries.length) return;
    state = AsyncValue.data(current.copyWith(entries: next));
    await _persistUrls(next);
    await _persistCache(next);
  }


  Future<void> refresh(String url) async {
    final current = state.value;
    if (current == null) return;
    final idx = current.entries.indexWhere((e) => e.url == url);
    if (idx < 0) return;

    var entries = List<IconLibraryEntryState>.of(current.entries);
    entries[idx] = entries[idx].copyWith(
      status: IconLibraryStatus.loading,
      clearError: true,
    );
    state = AsyncValue.data(current.copyWith(entries: entries));

    try {
      final lib = await _client.fetch(url);
      final after = state.value;
      if (after == null) return;
      final i = after.entries.indexWhere((e) => e.url == url);
      if (i < 0) return;
      entries = List<IconLibraryEntryState>.of(after.entries);
      entries[i] = entries[i].copyWith(
        library: lib,
        status: IconLibraryStatus.loaded,
        clearError: true,
      );
      state = AsyncValue.data(after.copyWith(entries: entries));
      await _persistCache(entries);
    } catch (err) {
      debugPrint('[IconLibrary] refresh failed for $url: $err');
      final after = state.value;
      if (after == null) return;
      final i = after.entries.indexWhere((e) => e.url == url);
      if (i < 0) return;
      entries = List<IconLibraryEntryState>.of(after.entries);
      entries[i] = entries[i].copyWith(
        status: IconLibraryStatus.error,
        error: err.toString(),
      );
      state = AsyncValue.data(after.copyWith(entries: entries));
    }
  }


  Future<void> refreshAll() async {
    final current = state.value;
    if (current == null) return;
    for (final e in current.entries) {
      await refresh(e.url);
    }
  }

  Future<void> _persistUrls(List<IconLibraryEntryState> entries) async {
    await persistField(_keyUrls, entries.map((e) => e.url).toList());
  }

  Future<void> _persistCache(List<IconLibraryEntryState> entries) async {
    final map = <String, dynamic>{};
    for (final e in entries) {
      final lib = e.library;
      if (lib != null) map[e.url] = lib.toJson();
    }
    await persistField(_keyCache, jsonEncode(map));
  }
}

final iconLibraryProvider =
    AsyncNotifierProvider<
      IconLibrarySettingsNotifier,
      IconLibrarySettingsState
    >(IconLibrarySettingsNotifier.new);
