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

part of '../../locked_time.dart';

/// Scheduling constraint that reserves time without becoming a task card.
///
/// Locked blocks represent time the flexible scheduler should avoid: work hours,
/// appointments, sleep boundaries, streams, relationship blocks, or any other
/// commitment that should not be automatically rearranged. They are modeled
/// separately from normal tasks because the UI may show them as subtle overlays
/// rather than actionable task cards.
///
/// A block is either:
/// - one-off, with [date] set and [recurrence] null; or
/// - recurring, with [recurrence] set and [date] usually null.
class LockedBlock {
  /// Creates a `LockedBlock` instance with the values required by its invariants.
  /// Callers should pass already-normalized scheduler data because validation and defaulting belong at the model or service boundary, not in arbitrary UI code.
  LockedBlock({
    required String id,
    required String name,
    required this.startTime,
    required this.endTime,
    required this.createdAt,
    required this.updatedAt,
    this.date,
    this.recurrence,
    this.hiddenByDefault = true,
    this.projectId,
    this.archivedAt,
  })  : id =
            _requiredLockedString(id, 'id', DomainValidationCode.blankStableId),
        name = _requiredLockedString(
            name, 'name', DomainValidationCode.blankName) {
    if (recurrence == null && date == null) {
      throw DomainValidationException(
        code: DomainValidationCode.invalidLockedBlock,
        invalidValue: null,
        name: 'date',
        message: 'One-off locked blocks need a date.',
      );
    }
    if (!_clockTimeIsBefore(startTime, endTime)) {
      throw DomainValidationException(
        code: DomainValidationCode.invalidLockedBlock,
        invalidValue: {'startTime': startTime, 'endTime': endTime},
        name: 'startTime/endTime',
        message: 'Locked block end time must be after start time.',
      );
    }
  }

  /// Stable id for persistence and one-day overrides.
  final String id;

  /// User-facing label, such as `Work`, `Stream`, or `Relationship block`.
  final String name;

  /// Start time-of-day. Combined with a date during expansion.
  final ClockTime startTime;

  /// End time-of-day. Combined with a date during expansion.
  final ClockTime endTime;

  /// Calendar date for one-off locked blocks. Recurring blocks leave this null.
  final CivilDate? date;

  /// Optional weekly recurrence. Null means this is a one-off block.
  final LockedBlockRecurrence? recurrence;

  /// Whether UI should keep this block visually quiet by default. Scheduling still
  /// treats hidden blocks as blocked time.
  final bool hiddenByDefault;

  /// Optional project/category association for UI colors or reports.
  final String? projectId;

  /// Creation timestamp for persistence/auditing.
  final DateTime createdAt;

  /// Last update timestamp for persistence/auditing.
  final DateTime updatedAt;

  /// When present, this block no longer creates future occurrences.
  ///
  /// One-day overrides are intentionally stored separately and are not deleted
  /// when a block is archived.
  final DateTime? archivedAt;

  /// Convenience check for whether this block expands through recurrence.
  bool get isRecurring => recurrence != null;

  /// Whether this block has been archived.
  bool get isArchived => archivedAt != null;

  /// Return a copy with selected locked-block details changed.
  LockedBlock copyWith({
    String? id,
    String? name,
    ClockTime? startTime,
    ClockTime? endTime,
    CivilDate? date,
    LockedBlockRecurrence? recurrence,
    bool? hiddenByDefault,
    String? projectId,
    DateTime? createdAt,
    DateTime? updatedAt,
    DateTime? archivedAt,
    bool clearArchivedAt = false,
  }) {
    return LockedBlock(
      id: id ?? this.id,
      name: name ?? this.name,
      startTime: startTime ?? this.startTime,
      endTime: endTime ?? this.endTime,
      date: date ?? this.date,
      recurrence: recurrence ?? this.recurrence,
      hiddenByDefault: hiddenByDefault ?? this.hiddenByDefault,
      projectId: projectId ?? this.projectId,
      createdAt: createdAt ?? this.createdAt,
      updatedAt: updatedAt ?? this.updatedAt,
      archivedAt: clearArchivedAt ? null : (archivedAt ?? this.archivedAt),
    );
  }
}
