// lib/remote/auth_client.dart
import 'dart:convert';
import 'package:http/http.dart' as http;

/// Gestisce autenticazione remota e caching del Bearer token.
/// - [baseUrl]: URL base del server (con o senza '/')
/// - [email]/[password]: credenziali
/// - [loginPath]: path dell'endpoint di login (default 'auth/login')
/// - [timeout]: timeout per le richieste (default 20s)
class RemoteAuth {
  final Uri base;
  final String email;
  final String password;
  final String loginPath;
  final Duration timeout;

  String? _token;

  RemoteAuth({
    required String baseUrl,
    required this.email,
    required this.password,
    this.loginPath = 'auth/login',
    this.timeout = const Duration(seconds: 20),
  }) : base = Uri.parse(baseUrl.endsWith('/') ? baseUrl : '$baseUrl/');

  Uri get _loginUri => base.resolve(loginPath);

  /// Esegue il login e memorizza il token.
  /// Lancia eccezione con messaggio chiaro in caso di errore HTTP, rete o JSON.
  Future<String> login() async {
    final uri = _loginUri;
    final headers = {'Content-Type': 'application/json'};
    final bodyStr = json.encode({'email': email, 'password': password});

    http.Response res;
    try {
      res = await http
          .post(uri, headers: headers, body: bodyStr)
          .timeout(timeout);
    } catch (e) {
      throw Exception('Login fallito: errore di rete verso $uri: $e');
    }

    // Follow esplicito per redirect POST moderni (307/308) mantenendo metodo e body
    if ({307, 308}.contains(res.statusCode) && res.headers['location'] != null) {
      final redirectUri = uri.resolve(res.headers['location']!);
      try {
        res = await http
            .post(redirectUri, headers: headers, body: bodyStr)
            .timeout(timeout);
      } catch (e) {
        throw Exception('Login fallito: errore di rete verso $redirectUri: $e');
      }
    }

    if (res.statusCode != 200) {
      final snippet = utf8.decode(res.bodyBytes.take(200).toList());
      throw Exception(
        'Login fallito: HTTP ${res.statusCode} ${res.reasonPhrase} – $snippet',
      );
    }

    // Parsing JSON robusto
    Map<String, dynamic> map;
    try {
      map = json.decode(utf8.decode(res.bodyBytes)) as Map<String, dynamic>;
    } catch (_) {
      throw Exception('Login fallito: risposta non è un JSON valido');
    }

    // Supporto sia 'token' sia 'access_token'
    final token = (map['token'] ?? map['access_token']) as String?;
    if (token == null || token.isEmpty) {
      throw Exception('Login fallito: token assente nella risposta');
    }

    _token = token;
    return token;
  }

  /// Ritorna gli header con Bearer; se non hai token, esegue login.
  Future<Map<String, String>> authHeaders() async {
    _token ??= await login();
    return {'Authorization': 'Bearer $_token'};
  }

  /// Forza il rinnovo del token (es. dopo 401) e ritorna i nuovi header.
  Future<Map<String, String>> refreshAndHeaders() async {
    _token = null;
    return await authHeaders();
  }

  /// Accesso in sola lettura al token corrente (può essere null).
  String? get token => _token;
}