import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;

import '../config.dart';
import '../models/class_cookie_entry.dart';

const _cookieKey = AppSharedPreferencesKey.cookieKey;

final cookieProvider = StateNotifierProvider<CookieNotifier, Map<String, CookieEntry>>((ref) {
  return CookieNotifier();
});

final cookieHeaderProvider = Provider<String>((ref) {
  final cookies = ref.watch(cookieProvider);
  final now = DateTime.now();

  return cookies.entries
  .where((e) => e.value.expires == null || e.value.expires!.isAfter(now))
  .map((e) => e.value.toCookieString(e.key))
  .join('; ');
});


final apiClientProvider = Provider<ApiClient>((ref) {
  final cookieHeader = ref.watch(cookieHeaderProvider);
  final client = ApiClient(ref: ref);
  ref.onDispose(client.dispose);
  return client;
});

class ApiClient {
  final Ref ref;
  final http.Client _client;

  static const String baseUrl = AppConfig.apiBaseUrl;

  ApiClient({required this.ref, http.Client? client})
  : _client = client ?? http.Client(); // 复用或新建

  Map<String, String> _buildHeaders({Map<String, String>? extra}) {
    final cookieHeader = ref.read(cookieHeaderProvider);
    return {
      'Content-Type': 'application/json',
      if (cookieHeader.isNotEmpty) 'Cookie': cookieHeader,
        ...?extra,
    };
  }

  Future<http.Response> _send(Future<http.Response> Function(http.Client client) request) async {
    final response = await request(_client);

    final setCookie = response.headers['set-cookie'];
    if (setCookie != null) {
      await ref.read(cookieProvider.notifier).updateFromSetCookie(setCookie.split(','));
    }

    return response;
  }

  Future<http.Response> get(String path, {Map<String, String>? headers}) {
    return _send((client) => client.get(
      Uri.parse('$baseUrl/$path'),
      headers: _buildHeaders(extra: headers),
    ));
  }

  Future<http.Response> post(String path, {Map<String, String>? headers, String? body}) {
    return _send((client) => client.post(
      Uri.parse('$baseUrl/$path'),
      headers: _buildHeaders(extra: headers),
      body: body,
    ));
  }

  void _checkResponse(http.Response res) {
    if (res.statusCode >= 300) {
      throw Exception('HTTP ${res.statusCode}: ${res.body}');
    }
  }

  void dispose() {
    _client.close(); // 记得释放资源
  }
}

class CookieNotifier extends StateNotifier<Map<String, CookieEntry>> {
  CookieNotifier() : super({}) {
    _load();
  }

  Future<void> _load() async {
    final prefs = await SharedPreferences.getInstance();
    final list = prefs.getStringList(_cookieKey) ?? [];
    final map = <String, CookieEntry>{};

    for (final item in list) {
      final split = item.split('|');
      if (split.length == 2) {
        final key = split[0];
        final valueMap = Map<String, dynamic>.from(jsonDecode(split[1]));
        map[key] = CookieEntry.fromJson(valueMap);
      }
    }

    state = map;
  }

  Future<void> _persist() async {
    final prefs = await SharedPreferences.getInstance();
    final list = state.entries.map((e) => '${e.key}|${jsonEncode(e.value.toJson())}').toList();
    await prefs.setStringList(_cookieKey, list);
  }

  Future<void> setAll(Map<String, CookieEntry> map) async {
    state = Map<String, CookieEntry>.from(map);
    await _persist();
  }

  Future<void> clear() async {
    state = {};
    final prefs = await SharedPreferences.getInstance();
    await prefs.remove(_cookieKey);
  }

  Future<void> updateFromSetCookie(List<String> setCookies) async {
    final updated = Map<String, CookieEntry>.from(state);

    for (final raw in setCookies) {
      final parts = raw.split(';');
      final kv = parts[0].split('=');
      if (kv.length < 2) continue;
      final name = kv[0].trim();
      final entry = CookieEntry.fromSetCookie(raw);
      updated[name] = entry;
    }

    state = updated;
    await _persist();
  }

  bool get isLoggedIn =>
  state.containsKey('auth') || state.containsKey('sessionid');
}
