/// The outcome of a conflict resolution decision.
enum ConflictResolution {
  /// Use the local version of the record.
  useLocal,

  /// Use the remote version of the record.
  useRemote,

  /// Merge both versions into a new record (implementation-defined).
  merge,

  /// Escalate to the operator — neither version is automatically chosen.
  requiresOperatorReview,
}

/// The result produced by a [ConflictResolver] for a single conflicting record.
class ConflictResult<T> {
  /// Which version was chosen (or how the conflict was handled).
  final ConflictResolution resolution;

  /// The resolved record, or `null` when [resolution] is
  /// [ConflictResolution.requiresOperatorReview].
  final T? resolvedValue;

  /// Human-readable explanation of why this resolution was chosen.
  final String? reason;

  const ConflictResult({required this.resolution, this.resolvedValue, this.reason});

  /// Convenience constructor: local version wins.
  const ConflictResult.useLocal({required T local, this.reason})
    : resolution = ConflictResolution.useLocal,
      resolvedValue = local;

  /// Convenience constructor: remote version wins.
  const ConflictResult.useRemote({required T remote, this.reason})
    : resolution = ConflictResolution.useRemote,
      resolvedValue = remote;

  /// Convenience constructor: merged value produced.
  const ConflictResult.merged({required T merged, this.reason})
    : resolution = ConflictResolution.merge,
      resolvedValue = merged;

  /// Convenience constructor: operator review required.
  const ConflictResult.requiresReview({this.reason})
    : resolution = ConflictResolution.requiresOperatorReview,
      resolvedValue = null;

  @override
  String toString() => 'ConflictResult(resolution: $resolution, reason: $reason)';
}

/// Contract for resolving conflicts between a locally-modified record and a
/// remotely-modified record of the same entity.
///
/// [T] is the domain type being reconciled (e.g. `InventoryItem`, `Order`).
///
/// Implementations encode the business rules for which version wins when both
/// the local app and a remote channel (WooCommerce, Square) have modified the
/// same record since the last sync.
abstract class ConflictResolver<T> {
  /// Resolves the conflict between [local] and [remote] versions of a record.
  ///
  /// [localModifiedAt] and [remoteModifiedAt] are the last-modified timestamps
  /// for each version, used by time-based strategies.
  ConflictResult<T> resolve({
    required T local,
    required T remote,
    required DateTime localModifiedAt,
    required DateTime remoteModifiedAt,
  });
}

/// A [ConflictResolver] that always prefers the most recently modified version.
///
/// When timestamps are equal the remote version is preferred, on the assumption
/// that the remote is the authoritative source of truth.
class LastWriteWinsResolver<T> implements ConflictResolver<T> {
  const LastWriteWinsResolver();

  @override
  ConflictResult<T> resolve({
    required T local,
    required T remote,
    required DateTime localModifiedAt,
    required DateTime remoteModifiedAt,
  }) {
    if (localModifiedAt.isAfter(remoteModifiedAt)) {
      return ConflictResult.useLocal(
        local: local,
        reason: 'Local version is newer (last-write-wins)',
      );
    }
    return ConflictResult.useRemote(
      remote: remote,
      reason: 'Remote version is newer or equal (last-write-wins)',
    );
  }
}

/// A [ConflictResolver] that always prefers the local version.
///
/// Useful when the app is the authoritative source of truth and remote changes
/// should never overwrite local edits.
class LocalAlwaysWinsResolver<T> implements ConflictResolver<T> {
  const LocalAlwaysWinsResolver();

  @override
  ConflictResult<T> resolve({
    required T local,
    required T remote,
    required DateTime localModifiedAt,
    required DateTime remoteModifiedAt,
  }) {
    return ConflictResult.useLocal(local: local, reason: 'Local always wins policy');
  }
}

/// A [ConflictResolver] that always prefers the remote version.
///
/// Useful when the remote channel is the authoritative source of truth and
/// local edits should be discarded on conflict.
class RemoteAlwaysWinsResolver<T> implements ConflictResolver<T> {
  const RemoteAlwaysWinsResolver();

  @override
  ConflictResult<T> resolve({
    required T local,
    required T remote,
    required DateTime localModifiedAt,
    required DateTime remoteModifiedAt,
  }) {
    return ConflictResult.useRemote(remote: remote, reason: 'Remote always wins policy');
  }
}
