// FlowOutput — typed representation of one entry in
// `SubmitResponse.outputs`. Lets Studio render text vs. JSON vs.
// bytes vs. file with the right widget instead of stringifying
// everything into `<bytes: …>` placeholders.

import 'dart:convert';
import 'dart:typed_data';

import 'package:fai_client_sdk/fai_client_sdk.dart' show Payload;

/// One named output of a flow run. Sealed via factory constructors
/// — callers pattern-match with `switch` on the runtime type to
/// pick a renderer. Variants:
///
///   - [FlowOutputText]    : plain text. `mimeType` may be set
///                           (e.g. `text/markdown`) so the
///                           renderer can pick a Markdown widget.
///   - [FlowOutputJson]    : a proto Struct, already pretty-
///                           printed as JSON so the UI can render
///                           it directly.
///   - [FlowOutputBytes]   : raw bytes + MIME. The renderer
///                           decides whether to inline a preview
///                           (e.g. images) and exposes a
///                           Save-As affordance otherwise.
///   - [FlowOutputFile]    : a URI the host already wrote to. The
///                           renderer offers an Open action.
///   - [FlowOutputUnknown] : oneof was empty / unset. Never
///                           expected in practice but kept so
///                           the UI never panics on a future
///                           schema bump.
sealed class FlowOutput {
  const FlowOutput();

  factory FlowOutput.fromPayload(Payload p) {
    if (p.hasText()) {
      return FlowOutputText(text: p.text);
    }
    if (p.hasJson()) {
      // proto Struct → toProto3Json gives a Dart-side
      // Map/List/primitive tree. We re-encode with an indent so
      // the UI can show the operator-friendly form directly.
      final tree = p.json.toProto3Json();
      const encoder = JsonEncoder.withIndent('  ');
      return FlowOutputJson(pretty: encoder.convert(tree));
    }
    if (p.hasBytes()) {
      return FlowOutputBytes(
        bytes: Uint8List.fromList(p.bytes.data),
        mimeType: p.bytes.mimeType,
      );
    }
    if (p.hasFile()) {
      return FlowOutputFile(uri: p.file.uri, mimeType: p.file.mimeType);
    }
    return const FlowOutputUnknown();
  }
}

class FlowOutputText extends FlowOutput {
  final String text;
  const FlowOutputText({required this.text});
}

class FlowOutputJson extends FlowOutput {
  /// Already indented JSON, ready for SelectableText / Markdown
  /// code-block rendering.
  final String pretty;
  const FlowOutputJson({required this.pretty});
}

class FlowOutputBytes extends FlowOutput {
  final Uint8List bytes;
  final String mimeType;
  const FlowOutputBytes({required this.bytes, required this.mimeType});
}

class FlowOutputFile extends FlowOutput {
  final String uri;
  final String mimeType;
  const FlowOutputFile({required this.uri, required this.mimeType});
}

class FlowOutputUnknown extends FlowOutput {
  const FlowOutputUnknown();
}
