import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../api/endpoints/admin_invites.dart';
import '../api/endpoints/admin_quarantine.dart';
import '../api/endpoints/admin_requests.dart';
import '../api/endpoints/admin_users.dart';
import '../library/library_providers.dart' show dioProvider;
import '../models/admin_quarantine_item.dart';
import '../models/admin_request.dart';
import '../models/admin_user.dart';
import '../models/invite.dart';

final adminRequestsApiProvider = FutureProvider<AdminRequestsApi>((ref) async {
  return AdminRequestsApi(await ref.watch(dioProvider.future));
});

final adminQuarantineApiProvider =
    FutureProvider<AdminQuarantineApi>((ref) async {
  return AdminQuarantineApi(await ref.watch(dioProvider.future));
});

final adminUsersApiProvider = FutureProvider<AdminUsersApi>((ref) async {
  return AdminUsersApi(await ref.watch(dioProvider.future));
});

final adminInvitesApiProvider = FutureProvider<AdminInvitesApi>((ref) async {
  return AdminInvitesApi(await ref.watch(dioProvider.future));
});

class AdminCounts {
  const AdminCounts({
    required this.requests,
    required this.quarantine,
    required this.users,
  });
  final int requests;
  final int quarantine;
  final int users;
}

/// Fans out to the three list endpoints in parallel and rolls them up
/// into `{requests, quarantine, users}` for the landing screen badges.
final adminCountsProvider = FutureProvider<AdminCounts>((ref) async {
  final requestsApi = await ref.watch(adminRequestsApiProvider.future);
  final quarantineApi = await ref.watch(adminQuarantineApiProvider.future);
  final usersApi = await ref.watch(adminUsersApiProvider.future);
  final results = await Future.wait([
    requestsApi.list(),
    quarantineApi.list(),
    usersApi.list(),
  ]);
  return AdminCounts(
    requests: (results[0] as List).length,
    quarantine: (results[1] as List).length,
    users: (results[2] as List).length,
  );
});

class AdminRequestsController extends AsyncNotifier<List<AdminRequest>> {
  @override
  Future<List<AdminRequest>> build() async {
    final api = await ref.watch(adminRequestsApiProvider.future);
    return api.list();
  }

  Future<void> approve(String id) async {
    await _decide(id, (api) => api.approve(id));
  }

  Future<void> reject(String id) async {
    await _decide(id, (api) => api.reject(id));
  }

  Future<void> _decide(
    String id,
    Future<void> Function(AdminRequestsApi api) action,
  ) async {
    final api = await ref.read(adminRequestsApiProvider.future);
    final current = state.value ?? const <AdminRequest>[];
    state = AsyncData(current.where((r) => r.id != id).toList());
    try {
      await action(api);
      // Refresh landing badge.
      ref.invalidate(adminCountsProvider);
    } catch (e, st) {
      state = AsyncData(current);
      Error.throwWithStackTrace(e, st);
    }
  }
}

final adminRequestsProvider =
    AsyncNotifierProvider<AdminRequestsController, List<AdminRequest>>(
        AdminRequestsController.new);

/// Read side originally defined for the Requests screen's
/// requester-UUID → username join. Mutation methods (setAdmin,
/// setAutoApprove, resetPassword, delete) added alongside the Users
/// screen in Slice 3. resetPassword is admin-supplies; the server
/// has no auto-generation mode.
class AdminUsersController extends AsyncNotifier<List<AdminUser>> {
  @override
  Future<List<AdminUser>> build() async {
    final api = await ref.watch(adminUsersApiProvider.future);
    return api.list();
  }

  /// Optimistic flip of `is_admin` on the user row; rolls back on
  /// server error (including the last-admin guard 4xx).
  Future<void> setAdmin(String id, bool isAdmin) =>
      _patch(id, (u) => _copy(u, isAdmin: isAdmin),
          (api) => api.setAdmin(id, isAdmin));

  Future<void> setAutoApprove(String id, bool autoApprove) =>
      _patch(id, (u) => _copy(u, autoApproveRequests: autoApprove),
          (api) => api.setAutoApprove(id, autoApprove));

  Future<void> delete(String id) async {
    final api = await ref.read(adminUsersApiProvider.future);
    final current = state.value ?? const <AdminUser>[];
    state = AsyncData(current.where((u) => u.id != id).toList());
    try {
      await api.delete(id);
      ref.invalidate(adminCountsProvider);
    } catch (e, st) {
      state = AsyncData(current);
      Error.throwWithStackTrace(e, st);
    }
  }

  Future<void> resetPassword(String id, String newPassword) async {
    final api = await ref.read(adminUsersApiProvider.future);
    await api.resetPassword(id, newPassword);
  }

  Future<void> _patch(
    String id,
    AdminUser Function(AdminUser) mutate,
    Future<void> Function(AdminUsersApi api) action,
  ) async {
    final api = await ref.read(adminUsersApiProvider.future);
    final current = state.value ?? const <AdminUser>[];
    state = AsyncData([
      for (final u in current) u.id == id ? mutate(u) : u,
    ]);
    try {
      await action(api);
    } catch (e, st) {
      state = AsyncData(current);
      Error.throwWithStackTrace(e, st);
    }
  }

  AdminUser _copy(AdminUser u, {bool? isAdmin, bool? autoApproveRequests}) =>
      AdminUser(
        id: u.id,
        username: u.username,
        displayName: u.displayName,
        isAdmin: isAdmin ?? u.isAdmin,
        autoApproveRequests: autoApproveRequests ?? u.autoApproveRequests,
        createdAt: u.createdAt,
      );
}

final adminUsersProvider =
    AsyncNotifierProvider<AdminUsersController, List<AdminUser>>(
        AdminUsersController.new);

class AdminQuarantineController
    extends AsyncNotifier<List<AdminQuarantineItem>> {
  @override
  Future<List<AdminQuarantineItem>> build() async {
    final api = await ref.watch(adminQuarantineApiProvider.future);
    return api.list();
  }

  Future<void> resolve(String trackId) =>
      _act(trackId, (api) => api.resolve(trackId));
  Future<void> deleteFile(String trackId) =>
      _act(trackId, (api) => api.deleteFile(trackId));
  Future<void> deleteViaLidarr(String trackId) =>
      _act(trackId, (api) => api.deleteViaLidarr(trackId));

  Future<void> _act(
    String trackId,
    Future<void> Function(AdminQuarantineApi api) action,
  ) async {
    final api = await ref.read(adminQuarantineApiProvider.future);
    final current = state.value ?? const <AdminQuarantineItem>[];
    state = AsyncData(current.where((q) => q.trackId != trackId).toList());
    try {
      await action(api);
      ref.invalidate(adminCountsProvider);
    } catch (e, st) {
      state = AsyncData(current);
      Error.throwWithStackTrace(e, st);
    }
  }
}

final adminQuarantineProvider = AsyncNotifierProvider<AdminQuarantineController,
    List<AdminQuarantineItem>>(AdminQuarantineController.new);

class AdminInvitesController extends AsyncNotifier<List<Invite>> {
  @override
  Future<List<Invite>> build() async {
    final api = await ref.watch(adminInvitesApiProvider.future);
    return api.list();
  }

  /// Returns the freshly-minted invite so the screen can show the
  /// token in a copy-once dialog. The new invite is also prepended
  /// to the local list so it shows up without a refresh.
  Future<Invite> create({String? note}) async {
    final api = await ref.read(adminInvitesApiProvider.future);
    final invite = await api.create(note: note);
    final current = state.value ?? const <Invite>[];
    state = AsyncData([invite, ...current]);
    return invite;
  }

  Future<void> revoke(String token) async {
    final api = await ref.read(adminInvitesApiProvider.future);
    final current = state.value ?? const <Invite>[];
    state = AsyncData(current.where((i) => i.token != token).toList());
    try {
      await api.revoke(token);
    } catch (e, st) {
      state = AsyncData(current);
      Error.throwWithStackTrace(e, st);
    }
  }
}

final adminInvitesProvider =
    AsyncNotifierProvider<AdminInvitesController, List<Invite>>(
        AdminInvitesController.new);
