import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/foundation.dart';


import 'auth_client.dart';
import 'remote_models.dart';


class RemoteHttpApi {
  RemoteHttpApi({
    required this.baseUrl,
    required this.auth,
    this.timeout = const Duration(seconds: 20),
  }) : _base = Uri.parse(baseUrl.endsWith('/') ? baseUrl : '$baseUrl/');

  final String baseUrl;
  final Uri _base;
  final RemoteAuth auth;
  final Duration timeout;

/// Recupera i photo items per una lista di ids.
Future<List<RemotePhotoItem>> getPhotosByIds(List<String> ids) async {
  if (ids.isEmpty) return <RemotePhotoItem>[];

  const int chunkSize = 100;
  final List<RemotePhotoItem> out = [];

  for (var i = 0; i < ids.length; i += chunkSize) {
    final end = (i + chunkSize < ids.length) ? i + chunkSize : ids.length;
    final chunk = ids.sublist(i, end);

    // prova batch endpoint
    try {
      final idsParam = chunk.map(Uri.encodeComponent).join(',');
      final uri = _base.resolve('photos?ids=$idsParam');
      final r = await _getWithAuth(uri);
      final list = _decodeList(r, label: 'getPhotosByIds');
      out.addAll(list.map(RemotePhotoItem.fromJson));
      continue;
    } catch (e) {
      debugPrint('[remote][api] batch getPhotosByIds failed for chunk starting at $i: $e');
      // fallback: prova richieste singole per questo chunk
    }

    // fallback per chunk: richieste singole
    for (final id in chunk) {
      try {
        final map = await getPhotoById(id);
        if (map != null) out.add(RemotePhotoItem.fromJson(map));
      } catch (e) {
        debugPrint('[remote][api] getPhotosByIds fallback error for id=$id: $e');
      }
    }
  }

  return out;
}


  // -------------------------
  // Helpers
  // -------------------------

  Future<http.Response> _getWithAuth(Uri uri) async {
    var headers = await auth.authHeaders();

    http.Response res;
    try {
      res = await http.get(uri, headers: headers).timeout(timeout);
    } catch (e) {
      throw Exception('GET fallita: errore di rete verso $uri: $e');
    }

    // Follow redirect 307/308 se presenti (raro ma possibile)
    if ({307, 308}.contains(res.statusCode) && res.headers['location'] != null) {
      final redirectUri = uri.resolve(res.headers['location']!);
      try {
        res = await http.get(redirectUri, headers: headers).timeout(timeout);
      } catch (e) {
        throw Exception('GET fallita: errore di rete verso $redirectUri: $e');
      }
    }

    // Se token scaduto -> refresh e retry una sola volta
    if (res.statusCode == 401) {
      headers = await auth.refreshAndHeaders();
      try {
        res = await http.get(uri, headers: headers).timeout(timeout);
      } catch (e) {
        throw Exception('GET fallita dopo refresh token verso $uri: $e');
      }
    }

    // Gestione errori HTTP
    if (res.statusCode < 200 || res.statusCode >= 300) {
      final snippet = utf8.decode(res.bodyBytes.take(300).toList());
      throw Exception('HTTP ${res.statusCode} ${res.reasonPhrase} su $uri – $snippet');
    }

    return res;
  }

  List<Map<String, dynamic>> _decodeList(http.Response r, {required String label}) {
    final decoded = jsonDecode(utf8.decode(r.bodyBytes));
    if (decoded is! List) {
      throw Exception('$label: risposta non è una lista JSON');
    }
    return decoded.cast<Map<String, dynamic>>();
  }

  // -------------------------
  // API
  // -------------------------
Future<void> sendSyncDone({
  required String sessionId,
  required String deviceId,
  required int lastSyncMs,
}) async {
  final uri = _base.resolve('photos/sync_done');

  final headers = await auth.authHeaders();
  headers['Content-Type'] = 'application/json';

  final body = jsonEncode({
    'session_id': sessionId,
    'device_id': deviceId,
    'last_sync': lastSyncMs,
  });

  final res = await http
      .post(uri, headers: headers, body: body)
      .timeout(timeout);

  if (res.statusCode < 200 || res.statusCode >= 300) {
    final snippet = utf8.decode(res.bodyBytes.take(300).toList());
    throw Exception('sync_done: HTTP ${res.statusCode} – $snippet');
  }
}


  Future<List<Map<String, dynamic>>> getAllPhotos() async {
    final uri = _base.resolve('photos');
    final r = await _getWithAuth(uri);
    return _decodeList(r, label: 'getAllPhotos');
  }

  Future<List<Map<String, dynamic>>> getChanges(String sinceIso) async {
    final uri = _base.resolve('photos/changes?since=${Uri.encodeComponent(sinceIso)}');
    final r = await _getWithAuth(uri);
    return _decodeList(r, label: 'getChanges');
  }

  Future<List<Map<String, dynamic>>> getDeletedHard(String sinceIso) async {
    final uri = _base.resolve('photos/deleted_hard?since=${Uri.encodeComponent(sinceIso)}');
    final r = await _getWithAuth(uri);

    final decoded = jsonDecode(utf8.decode(r.bodyBytes));
    if (decoded is! Map<String, dynamic>) {
      throw Exception('getDeletedHard: risposta non è un oggetto JSON');
    }

    final deleted = (decoded['deleted'] ?? []) as List;
    return deleted.cast<Map<String, dynamic>>();
  }

  Future<Map<String, dynamic>?> getPhotoById(String id) async {
    final uri = _base.resolve('photos/$id');
    final r = await _getWithAuth(uri);

    final decoded = jsonDecode(utf8.decode(r.bodyBytes));
    if (decoded is List && decoded.isNotEmpty) {
      final first = decoded.first;
      if (first is Map<String, dynamic>) return first;
      throw Exception('getPhotoById: primo elemento non è un oggetto JSON');
    }
    return null;
  }
}
