// SPDX-FileCopyrightText: 2026 FocusFlow contributors
// SPDX-License-Identifier: AGPL-3.0-only

part of '../json_csv_writers.dart';

/// JSON export writer for scheduler tasks.
final class JsonExportWriter implements ExportWriter {
  /// Creates a JSON writer that writes to the provided string sink.
  JsonExportWriter(this._sink);

  /// Private state stored as `_sink` for this implementation.
  /// The field is intentionally scoped to this library so only the owning type can change the related scheduler or UI behavior.
  final StringSink _sink;

  /// Private state stored as `_context` for this implementation.
  /// The field is intentionally scoped to this library so only the owning type can change the related scheduler or UI behavior.
  ExportContext? _context;

  /// Private state stored as `_hasTask` for this implementation.
  /// The field is intentionally scoped to this library so only the owning type can change the related scheduler or UI behavior.
  bool _hasTask = false;

  /// Private state stored as `_ended` for this implementation.
  /// The field is intentionally scoped to this library so only the owning type can change the related scheduler or UI behavior.
  bool _ended = false;

  /// Runs the `begin` operation and completes after its asynchronous work finishes.
  /// Callers should await the returned future so repository writes, UI refreshes, and failure handling happen in a predictable order.
  @override
  Future<void> begin(ExportContext context) async {
    _assertNotEnded();
    _context = context;
    _sink.write(
      '{"context":${jsonEncode(_contextToJson(context))},"tasks":[',
    );
  }

  /// Runs the `writeTask` operation and completes after its asynchronous work finishes.
  /// Callers should await the returned future so repository writes, UI refreshes, and failure handling happen in a predictable order.
  @override
  Future<void> writeTask(core.Task task) async {
    final context = _requireContext();
    _assertNotEnded();
    if (_hasTask) {
      _sink.write(',');
    }
    _sink.write(jsonEncode(core.TaskDocumentMapping.toDocument(
      task,
      ownerId: context.ownerId.value,
    )));
    _hasTask = true;
  }

  /// Runs the `end` operation and completes after its asynchronous work finishes.
  /// Callers should await the returned future so repository writes, UI refreshes, and failure handling happen in a predictable order.
  @override
  Future<void> end() async {
    _requireContext();
    _assertNotEnded();
    _sink.write(']}');
    _ended = true;
  }

  /// Runs the `_requireContext` helper used inside this library.
  /// The helper centralizes a small piece of transformation or validation so higher-level scheduler flow remains easier to read.
  ExportContext _requireContext() {
    final context = _context;
    if (context == null) {
      throw const ExportException('Writer has not started.');
    }
    return context;
  }

  /// Runs the `_assertNotEnded` helper used inside this library.
  /// The helper centralizes a small piece of transformation or validation so higher-level scheduler flow remains easier to read.
  void _assertNotEnded() {
    if (_ended) {
      throw const ExportException('Writer has already ended.');
    }
  }
}
