/// Synor Database SDK Client for Flutter
///
/// Multi-model database with Key-Value, Document, Vector, and Time Series stores.
library synor_database;

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'types.dart';

export 'types.dart';

/// Synor Database Client
class SynorDatabase {
  final DatabaseConfig config;
  final http.Client _client;
  bool _closed = false;

  late final KeyValueStore kv;
  late final DocumentStore documents;
  late final VectorStore vectors;
  late final TimeSeriesStore timeseries;

  SynorDatabase(this.config) : _client = http.Client() {
    kv = KeyValueStore(this);
    documents = DocumentStore(this);
    vectors = VectorStore(this);
    timeseries = TimeSeriesStore(this);
  }

  /// Close the client
  void close() {
    _closed = true;
    _client.close();
  }

  /// Check if closed
  bool get isClosed => _closed;

  /// Health check
  Future<bool> healthCheck() async {
    try {
      final resp = await _request('GET', '/health');
      return resp['status'] == 'healthy';
    } catch (_) {
      return false;
    }
  }

  Future<Map<String, dynamic>> _request(
    String method,
    String path, [
    Map<String, dynamic>? body,
  ]) async {
    if (_closed) throw const DatabaseException('Client has been closed');

    Exception? lastError;
    for (var attempt = 0; attempt < config.retries; attempt++) {
      try {
        return await _doRequest(method, path, body);
      } catch (e) {
        lastError = e as Exception;
        if (attempt < config.retries - 1) {
          await Future.delayed(Duration(seconds: 1 << attempt));
        }
      }
    }
    throw lastError ?? const DatabaseException('Unknown error');
  }

  Future<Map<String, dynamic>> _doRequest(
    String method,
    String path,
    Map<String, dynamic>? body,
  ) async {
    final uri = Uri.parse('${config.endpoint}$path');
    final headers = {
      'Authorization': 'Bearer ${config.apiKey}',
      'Content-Type': 'application/json',
      'X-SDK-Version': 'flutter/0.1.0',
    };

    http.Response response;
    switch (method) {
      case 'GET':
        response = await _client.get(uri, headers: headers);
        break;
      case 'POST':
        response = await _client.post(uri, headers: headers, body: jsonEncode(body));
        break;
      case 'PUT':
        response = await _client.put(uri, headers: headers, body: jsonEncode(body));
        break;
      case 'PATCH':
        response = await _client.patch(uri, headers: headers, body: jsonEncode(body));
        break;
      case 'DELETE':
        response = await _client.delete(uri, headers: headers, body: body != null ? jsonEncode(body) : null);
        break;
      default:
        throw DatabaseException('Unknown method: $method');
    }

    if (response.statusCode >= 400) {
      final error = jsonDecode(response.body) as Map<String, dynamic>;
      throw DatabaseException(
        error['message'] as String? ?? 'HTTP ${response.statusCode}',
        code: error['code'] as String?,
        statusCode: response.statusCode,
      );
    }

    return jsonDecode(response.body) as Map<String, dynamic>;
  }
}

/// Key-Value Store
class KeyValueStore {
  final SynorDatabase _db;
  KeyValueStore(this._db);

  Future<dynamic> get(String key) async {
    final resp = await _db._request('GET', '/kv/${Uri.encodeComponent(key)}');
    return resp['value'];
  }

  Future<void> set(String key, dynamic value, {int? ttl}) async {
    await _db._request('PUT', '/kv/${Uri.encodeComponent(key)}', {
      'key': key,
      'value': value,
      if (ttl != null) 'ttl': ttl,
    });
  }

  Future<void> delete(String key) async {
    await _db._request('DELETE', '/kv/${Uri.encodeComponent(key)}');
  }

  Future<List<KeyValue>> list(String prefix) async {
    final resp = await _db._request('GET', '/kv?prefix=${Uri.encodeComponent(prefix)}');
    return (resp['items'] as List).map((e) => KeyValue.fromJson(e)).toList();
  }
}

/// Document Store
class DocumentStore {
  final SynorDatabase _db;
  DocumentStore(this._db);

  Future<String> create(String collection, Map<String, dynamic> document) async {
    final resp = await _db._request(
      'POST',
      '/collections/${Uri.encodeComponent(collection)}/documents',
      document,
    );
    return resp['id'] as String;
  }

  Future<Document> get(String collection, String id) async {
    final resp = await _db._request(
      'GET',
      '/collections/${Uri.encodeComponent(collection)}/documents/${Uri.encodeComponent(id)}',
    );
    return Document.fromJson(resp);
  }

  Future<void> update(String collection, String id, Map<String, dynamic> update) async {
    await _db._request(
      'PATCH',
      '/collections/${Uri.encodeComponent(collection)}/documents/${Uri.encodeComponent(id)}',
      update,
    );
  }

  Future<void> delete(String collection, String id) async {
    await _db._request(
      'DELETE',
      '/collections/${Uri.encodeComponent(collection)}/documents/${Uri.encodeComponent(id)}',
    );
  }

  Future<List<Document>> query(String collection, Query query) async {
    final resp = await _db._request(
      'POST',
      '/collections/${Uri.encodeComponent(collection)}/query',
      query.toJson(),
    );
    return (resp['documents'] as List).map((e) => Document.fromJson(e)).toList();
  }
}

/// Vector Store
class VectorStore {
  final SynorDatabase _db;
  VectorStore(this._db);

  Future<void> upsert(String collection, List<VectorEntry> vectors) async {
    await _db._request(
      'POST',
      '/vectors/${Uri.encodeComponent(collection)}/upsert',
      {'vectors': vectors.map((v) => v.toJson()).toList()},
    );
  }

  Future<List<SearchResult>> search(String collection, List<double> vector, int k) async {
    final resp = await _db._request(
      'POST',
      '/vectors/${Uri.encodeComponent(collection)}/search',
      {'vector': vector, 'k': k},
    );
    return (resp['results'] as List).map((e) => SearchResult.fromJson(e)).toList();
  }

  Future<void> delete(String collection, List<String> ids) async {
    await _db._request(
      'DELETE',
      '/vectors/${Uri.encodeComponent(collection)}',
      {'ids': ids},
    );
  }
}

/// Time Series Store
class TimeSeriesStore {
  final SynorDatabase _db;
  TimeSeriesStore(this._db);

  Future<void> write(String series, List<DataPoint> points) async {
    await _db._request(
      'POST',
      '/timeseries/${Uri.encodeComponent(series)}/write',
      {'points': points.map((p) => p.toJson()).toList()},
    );
  }

  Future<List<DataPoint>> query(String series, TimeRange range, {Aggregation? aggregation}) async {
    final body = <String, dynamic>{'range': range.toJson()};
    if (aggregation != null) body['aggregation'] = aggregation.toJson();

    final resp = await _db._request(
      'POST',
      '/timeseries/${Uri.encodeComponent(series)}/query',
      body,
    );
    return (resp['points'] as List).map((e) => DataPoint.fromJson(e)).toList();
  }
}
