import 'package:flut/flut/runtime.dart';

/// Base class for immutable objects passed between Dart and Python.
/// These are simple data carriers - no bidirectional sync needed.
///
/// Subclasses must implement:
/// - flutEncode(): Convert to JSON for Python
/// - static flutDecode(dynamic data, FlutRuntime runtime): Decode from JSON
abstract class FlutImmutableObject {
  final String flutType;

  const FlutImmutableObject(this.flutType);

  Map<String, dynamic> flutBaseProps() => {'_flut_type': flutType};

  /// Encode this object to JSON for Python.
  Map<String, dynamic> flutEncode();
}

mixin FlutAbstractObject {
  FlutRuntime get runtime;
}

/// Base class for enum-like types that map string values to Dart enum values.
///
/// Subclasses provide [flutType] and [flutValueMap], and get generic
/// decode/encode for free.
///
/// Standard JSON format: {"_flut_type": "SomeEnum", "_flut_value": "someValue"}
abstract class FlutEnumObject<T> {
  final String flutType;
  final Map<String, T> flutValueMap;

  const FlutEnumObject(this.flutType, this.flutValueMap);

  T? flutDecode(Map<String, dynamic> data) {
    return flutValueMap[data['_flut_value'] as String?];
  }

  Map<String, dynamic>? flutEncode(T value) {
    for (final entry in flutValueMap.entries) {
      if (entry.value == value) {
        return {'_flut_type': flutType, '_flut_value': entry.key};
      }
    }
    return null;
  }
}

/// Base class for objects that support realtime property sync with Python.
/// Wraps a Flutter object of type [T] and provides get/set/call protocol.
///
/// Two constructors:
/// - FlutRealtimeObject(): for Python-created objects decoded from JSON
/// - FlutRealtimeObject.wrap(): for Dart-created objects exposed to Python
///
/// Subclasses must implement:
/// - getRawProperty(String property)
/// - setProperty(String property, dynamic value)
/// - callMethod(String method, Map<String, dynamic> args)
///
/// flutEncode() is provided by the base class — do NOT override it.
abstract class FlutRealtimeObject<T extends Object> {
  final FlutRuntime flutRuntime;
  final int flutOid;
  final String flutType;
  final T flutTarget;

  FlutRealtimeObject({
    required FlutRuntime runtime,
    required Map<String, dynamic> json,
    required String type,
    required T target,
  }) : flutRuntime = runtime,
       flutOid = json['_flut_oid'] as int,
       flutType = type,
       flutTarget = target;

  FlutRealtimeObject.wrap({
    required FlutRuntime runtime,
    required int oid,
    required String type,
    required T target,
  }) : flutRuntime = runtime,
       flutOid = oid,
       flutType = type,
       flutTarget = target;

  Map<String, dynamic> flutBaseProps() {
    return {'_flut_type': flutType, '_flut_oid': flutOid};
  }

  Map<String, dynamic> flutEncode() => flutBaseProps();

  /// Override to return raw Dart value for a property.
  dynamic getRawProperty(String property);

  /// Generic implementation - calls getRawProperty then encodes.
  dynamic getProperty(String property) {
    return flutRuntime.encodeValue(getRawProperty(property));
  }

  bool setProperty(String property, dynamic value);
  dynamic callMethod(String method, Map<String, dynamic> args);
}
