import 'dart:async';

import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../core/domain/errors.dart';
import '../shared/utils/app_logger.dart';
import 'danmaku_settings_provider.dart';
import 'di_providers.dart';
import 'server_provider.dart';
import 'session_provider.dart';
import 'sm_account_provider.dart';


enum ServerSyncStatus { success, info, failure, needsLogin, needsVip }


class ServerSyncOutcome {
  final ServerSyncStatus status;
  final String message;

  const ServerSyncOutcome(this.status, this.message);

  bool get isError =>
      status == ServerSyncStatus.failure ||
      status == ServerSyncStatus.needsLogin ||
      status == ServerSyncStatus.needsVip;
}


class ServerSyncState {
  final bool busy;
  final DateTime? lastSyncAt;
  final String? lastSyncError;
  const ServerSyncState({
    this.busy = false,
    this.lastSyncAt,
    this.lastSyncError,
  });
}


class ServerSyncNotifier extends Notifier<ServerSyncState> {
  static const _tag = 'ServerSync';

  @override
  ServerSyncState build() => const ServerSyncState();


  Future<ServerSyncOutcome> upload() async {
    if (state.busy) {
      return const ServerSyncOutcome(ServerSyncStatus.info, '正在同步，请稍候');
    }
    final access = await _resolveAccess();
    if (access.outcome != null) return access.outcome!;
    final creds = access.creds!;

    state = const ServerSyncState(busy: true);
    try {
      final uc = await ref.read(uploadServerSyncUseCaseProvider.future);
      final r = await uc.execute(
        baseUrl: creds.baseUrl,
        accessToken: creds.accessToken,
      );
      final outcome = ServerSyncOutcome(
        ServerSyncStatus.success,
        '已上传 ${r.serverCount} 个服务器、${r.sessionCount} 个登录态、'
        '${r.danmakuSourceCount} 个弹幕源、'
        '${r.scriptWidgetCount} 个模块、${r.scriptWidgetSubscriptionCount} 个订阅到云端',
      );
      state = ServerSyncState(lastSyncAt: DateTime.now());
      return outcome;
    } on DomainError catch (e) {
      state = ServerSyncState(lastSyncError: '上传失败：${e.message}');
      return ServerSyncOutcome(ServerSyncStatus.failure, '上传失败：${e.message}');
    } catch (e) {
      AppLogger.warn(_tag, 'upload failed', e);
      state = const ServerSyncState(lastSyncError: '上传失败，请稍后重试');
      return const ServerSyncOutcome(ServerSyncStatus.failure, '上传失败，请稍后重试');
    }
  }


  Future<ServerSyncOutcome> pull() async {
    if (state.busy) {
      return const ServerSyncOutcome(ServerSyncStatus.info, '正在同步，请稍候');
    }
    final access = await _resolveAccess();
    if (access.outcome != null) return access.outcome!;
    final creds = access.creds!;

    state = const ServerSyncState(busy: true);
    try {
      final uc = await ref.read(pullServerSyncUseCaseProvider.future);
      final r = await uc.execute(
        baseUrl: creds.baseUrl,
        accessToken: creds.accessToken,
      );
      if (r.empty) {
        return const ServerSyncOutcome(ServerSyncStatus.info, '云端还没有备份');
      }

      await ref.read(serverListProvider.notifier).refresh();
      await ref.read(sessionProvider.notifier).refresh();
      ref.invalidate(danmakuSettingsProvider);
      ref.invalidate(installedScriptWidgetsProvider);
      ref.invalidate(scriptWidgetSubscriptionsProvider);

      unawaited(_repairInBackground());

      final outcome = ServerSyncOutcome(
        ServerSyncStatus.success,
        '已从云端恢复 ${r.serverCount} 个服务器、${r.sessionCount} 个登录态、'
        '${r.danmakuSourceCount} 个弹幕源、'
        '${r.scriptWidgetCount} 个模块、${r.scriptWidgetSubscriptionCount} 个订阅',
      );
      state = ServerSyncState(lastSyncAt: DateTime.now());
      return outcome;
    } on DomainError catch (e) {
      state = ServerSyncState(lastSyncError: '拉取失败：${e.message}');
      return ServerSyncOutcome(ServerSyncStatus.failure, '拉取失败：${e.message}');
    } catch (e) {
      AppLogger.warn(_tag, 'pull failed', e);
      state = const ServerSyncState(lastSyncError: '拉取失败，请稍后重试');
      return const ServerSyncOutcome(ServerSyncStatus.failure, '拉取失败，请稍后重试');
    }
  }

  Future<void> _repairInBackground() async {
    try {
      final uc = await ref.read(repairSessionsUseCaseProvider.future);
      final repaired = await uc.execute();
      if (repaired > 0) {
        await ref.read(sessionProvider.notifier).refresh();
        AppLogger.info(_tag, 'repaired $repaired session(s) after pull');
      }
    } catch (e) {
      AppLogger.warn(_tag, 'background repair failed', e);
    }
  }


  Future<({_SyncCredentials? creds, ServerSyncOutcome? outcome})>
  _resolveAccess() async {
    const needsLogin = (
      creds: null,
      outcome: ServerSyncOutcome(ServerSyncStatus.needsLogin, '请先登录 sm 账号后再同步'),
    );

    var account = await ref.read(smAccountProvider.future);
    if (!account.canUseDataPlane) return needsLogin;

    var token = await ref.read(smAccountProvider.notifier).getAccessToken();
    if (token == null || token.isEmpty) return needsLogin;
    account = await ref.read(smAccountProvider.future);

    if (!account.isVip) {
      token = await ref
          .read(smAccountProvider.notifier)
          .getAccessToken(force: true);
      if (token == null || token.isEmpty) return needsLogin;
      account = await ref.read(smAccountProvider.future);
    }

    if (!account.isVip) {
      return (
        creds: null,
        outcome: const ServerSyncOutcome(
          ServerSyncStatus.needsVip,
          '服务器云同步为 VIP 功能，请开通 VIP 后使用',
        ),
      );
    }

    return (
      creds: _SyncCredentials(baseUrl: account.baseUrl, accessToken: token),
      outcome: null,
    );
  }
}

class _SyncCredentials {
  final String baseUrl;
  final String accessToken;
  const _SyncCredentials({required this.baseUrl, required this.accessToken});
}

final serverSyncProvider =
    NotifierProvider<ServerSyncNotifier, ServerSyncState>(
      ServerSyncNotifier.new,
    );
