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

// ==================== Config ====================

/// Database configuration
class DatabaseConfig {
  final String apiKey;
  final String endpoint;
  final Duration timeout;
  final int retries;
  final bool debug;

  const DatabaseConfig({
    required this.apiKey,
    this.endpoint = 'https://db.synor.io/v1',
    this.timeout = const Duration(seconds: 60),
    this.retries = 3,
    this.debug = false,
  });
}

// ==================== Key-Value Types ====================

/// Key-Value entry
class KeyValue {
  final String key;
  final dynamic value;
  final int? ttl;
  final DateTime? createdAt;
  final DateTime? updatedAt;

  const KeyValue({
    required this.key,
    required this.value,
    this.ttl,
    this.createdAt,
    this.updatedAt,
  });

  factory KeyValue.fromJson(Map<String, dynamic> json) => KeyValue(
        key: json['key'] as String,
        value: json['value'],
        ttl: json['ttl'] as int?,
        createdAt: json['createdAt'] != null
            ? DateTime.fromMillisecondsSinceEpoch(json['createdAt'] as int)
            : null,
        updatedAt: json['updatedAt'] != null
            ? DateTime.fromMillisecondsSinceEpoch(json['updatedAt'] as int)
            : null,
      );
}

// ==================== Document Types ====================

/// Document record
class Document {
  final String id;
  final String collection;
  final Map<String, dynamic> data;
  final DateTime createdAt;
  final DateTime updatedAt;

  const Document({
    required this.id,
    required this.collection,
    required this.data,
    required this.createdAt,
    required this.updatedAt,
  });

  factory Document.fromJson(Map<String, dynamic> json) => Document(
        id: json['id'] as String,
        collection: json['collection'] as String,
        data: Map<String, dynamic>.from(json['data'] as Map),
        createdAt: DateTime.fromMillisecondsSinceEpoch(json['createdAt'] as int),
        updatedAt: DateTime.fromMillisecondsSinceEpoch(json['updatedAt'] as int),
      );
}

/// Query builder
class Query {
  final Map<String, dynamic>? filter;
  final Map<String, int>? sort;
  final int? limit;
  final int? offset;
  final List<String>? projection;

  const Query({
    this.filter,
    this.sort,
    this.limit,
    this.offset,
    this.projection,
  });

  Map<String, dynamic> toJson() {
    final json = <String, dynamic>{};
    if (filter != null) json['filter'] = filter;
    if (sort != null) json['sort'] = sort;
    if (limit != null) json['limit'] = limit;
    if (offset != null) json['offset'] = offset;
    if (projection != null) json['projection'] = projection;
    return json;
  }
}

// ==================== Vector Types ====================

/// Vector entry for upsert
class VectorEntry {
  final String id;
  final List<double> vector;
  final Map<String, dynamic>? metadata;

  const VectorEntry({
    required this.id,
    required this.vector,
    this.metadata,
  });

  Map<String, dynamic> toJson() => {
        'id': id,
        'vector': vector,
        if (metadata != null) 'metadata': metadata,
      };
}

/// Vector search result
class SearchResult {
  final String id;
  final double score;
  final List<double>? vector;
  final Map<String, dynamic>? metadata;

  const SearchResult({
    required this.id,
    required this.score,
    this.vector,
    this.metadata,
  });

  factory SearchResult.fromJson(Map<String, dynamic> json) => SearchResult(
        id: json['id'] as String,
        score: (json['score'] as num).toDouble(),
        vector: json['vector'] != null
            ? List<double>.from((json['vector'] as List).map((e) => (e as num).toDouble()))
            : null,
        metadata: json['metadata'] != null
            ? Map<String, dynamic>.from(json['metadata'] as Map)
            : null,
      );
}

// ==================== Time Series Types ====================

/// Data point
class DataPoint {
  final DateTime timestamp;
  final double value;
  final Map<String, String>? tags;

  const DataPoint({
    required this.timestamp,
    required this.value,
    this.tags,
  });

  factory DataPoint.fromJson(Map<String, dynamic> json) => DataPoint(
        timestamp: DateTime.fromMillisecondsSinceEpoch(json['timestamp'] as int),
        value: (json['value'] as num).toDouble(),
        tags: json['tags'] != null
            ? Map<String, String>.from(json['tags'] as Map)
            : null,
      );

  Map<String, dynamic> toJson() => {
        'timestamp': timestamp.millisecondsSinceEpoch,
        'value': value,
        if (tags != null) 'tags': tags,
      };
}

/// Time range
class TimeRange {
  final DateTime start;
  final DateTime end;

  const TimeRange({required this.start, required this.end});

  Map<String, dynamic> toJson() => {
        'start': start.millisecondsSinceEpoch,
        'end': end.millisecondsSinceEpoch,
      };
}

/// Aggregation options
class Aggregation {
  final String function; // sum, avg, min, max, count
  final String interval; // 1m, 5m, 1h, 1d

  const Aggregation({required this.function, required this.interval});

  Map<String, dynamic> toJson() => {
        'function': function,
        'interval': interval,
      };
}

// ==================== Error Types ====================

/// Database error
class DatabaseException implements Exception {
  final String message;
  final String? code;
  final int? statusCode;

  const DatabaseException(this.message, {this.code, this.statusCode});

  @override
  String toString() => 'DatabaseException: $message';
}
