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

/// Tests Persistence Index Contract behavior for the Scheduler Core package.
library;

import 'package:scheduler_core/scheduler_core.dart';
import 'package:test/test.dart';

/// Entry point for this executable Dart file.
/// It wires the local command, test, or application behavior needed by this repository without exposing additional public API.
void main() {
  group('Persistence index contract', () {
    test('index names and key fields are stable and adapter-neutral', () {
      final names = <String>{};

      for (final spec in PersistenceIndexCatalog.all) {
        expect(spec.name, isNotEmpty);
        expect(spec.name.trim(), spec.name);
        expect(spec.name.contains(' '), isFalse);
        expect(names.add(spec.name), isTrue, reason: spec.name);
        expect(spec.collection, isNotEmpty);
        expect(spec.keys, isNotEmpty);
        expect(spec.keys.first.fieldName, DocumentFields.ownerId);
        for (final key in spec.keys) {
          expect(key.fieldName, isNotEmpty);
          expect(key.fieldName.contains('.'), isFalse);
        }
      }
    });

    test('every repository query has an index-backed access path', () {
      const requiredQueries = {
        RepositoryQueryNames.taskById,
        RepositoryQueryNames.taskByOwner,
        RepositoryQueryNames.taskByStatus,
        RepositoryQueryNames.taskByProject,
        RepositoryQueryNames.taskByParent,
        RepositoryQueryNames.taskBacklogCandidates,
        RepositoryQueryNames.taskScheduledWindow,
        RepositoryQueryNames.taskLocalDay,
        RepositoryQueryNames.projectByOwner,
        RepositoryQueryNames.projectById,
        RepositoryQueryNames.lockedBlockByOwner,
        RepositoryQueryNames.lockedBlockById,
        RepositoryQueryNames.lockedOverrideByDate,
        RepositoryQueryNames.taskActivityByOwner,
        RepositoryQueryNames.taskActivityByOperation,
        RepositoryQueryNames.taskActivityByTask,
        RepositoryQueryNames.taskActivityByProject,
        RepositoryQueryNames.projectStatisticsByProject,
        RepositoryQueryNames.ownerSettingsByOwner,
        RepositoryQueryNames.noticeAcknowledgementByOwner,
        RepositoryQueryNames.noticeAcknowledgementById,
        RepositoryQueryNames.operationByIdempotencyKey,
        RepositoryQueryNames.schedulingSnapshotById,
        RepositoryQueryNames.schedulingSnapshotWindow,
      };

      expect(PersistenceIndexCatalog.supportedQueries,
          containsAll(requiredQueries));
    });

    test('uniqueness and archive/partial index requirements are explicit', () {
      expect(
        uniqueIndexNames,
        containsAll({
          'tasks_owner_id_unique',
          'projects_owner_id_unique',
          'project_stats_owner_project_unique',
          'locked_blocks_owner_id_unique',
          'locked_overrides_owner_id_unique',
          'task_activities_owner_id_unique',
          'owner_settings_owner_unique',
          'notice_ack_owner_notice_unique',
          'operations_owner_operation_unique',
          'snapshots_owner_id_unique',
        }),
      );

      expect(
          indexByName('projects_owner_archived_name').keys.map(
                (key) => key.fieldName,
              ),
          contains(ProjectDocumentFields.archivedAt));
      expect(
          indexByName('locked_blocks_owner_archived_name').keys.map(
                (key) => key.fieldName,
              ),
          contains(LockedBlockDocumentFields.archivedAt));
      expect(
        indexByName('tasks_owner_scheduled_window').partialFilterFields,
        containsAll({
          TaskDocumentFields.scheduledStart,
          TaskDocumentFields.scheduledEnd,
        }),
      );
      expect(
        indexByName('tasks_owner_parent_status').partialFilterFields,
        contains(TaskDocumentFields.parentTaskId),
      );
    });

    test('collection retention does not target authoritative state', () {
      expect(
        PersistenceCollections.authoritative.intersection(
          PersistenceCollections.internalRetained,
        ),
        isEmpty,
      );
      expect(PersistencePayloadLimits.taskActivityRetention.inDays, 365);
      expect(PersistencePayloadLimits.applicationOperationRetention.inDays, 90);
      expect(PersistencePayloadLimits.schedulingSnapshotRetention.inDays, 30);
    });

    test('payload guards reject unbounded embedded growth', () {
      final metadata = {
        for (var index = 0;
            index <= PersistencePayloadLimits.maxTaskActivityMetadataEntries;
            index++)
          'key-$index': index,
      };
      final taskIds = List<Object?>.generate(
        PersistencePayloadLimits.maxSchedulingSnapshotTasks + 1,
        (index) => 'task-$index',
      );
      final appliedActivityIds = List<Object?>.generate(
        PersistencePayloadLimits.maxAppliedActivityIds + 1,
        (index) => 'activity-$index',
      );

      final metadataResult = PersistencePayloadGuard.mapEntries(
        fieldName: TaskActivityDocumentFields.metadata,
        value: metadata,
        limit: PersistencePayloadLimits.maxTaskActivityMetadataEntries,
      );
      final snapshotResult = PersistencePayloadGuard.listLength(
        fieldName: SchedulingSnapshotDocumentFields.tasks,
        value: taskIds,
        limit: PersistencePayloadLimits.maxSchedulingSnapshotTasks,
      );
      final activityIdsResult = PersistencePayloadGuard.listLength(
        fieldName: ProjectStatisticsDocumentFields.appliedActivityIds,
        value: appliedActivityIds,
        limit: PersistencePayloadLimits.maxAppliedActivityIds,
      );

      expect(metadataResult.isValid, isFalse);
      expect(metadataResult.actual, metadata.length);
      expect(snapshotResult.isValid, isFalse);
      expect(snapshotResult.fieldName, SchedulingSnapshotDocumentFields.tasks);
      expect(activityIdsResult.isValid, isFalse);
      expect(activityIdsResult.limit,
          PersistencePayloadLimits.maxAppliedActivityIds);
      expect(
        PersistencePayloadGuard.listLength(
          fieldName: SchedulingSnapshotDocumentFields.notices,
          value: const <Object?>[],
          limit: PersistencePayloadLimits.maxSchedulingSnapshotNotices,
        ).isValid,
        isTrue,
      );
    });
  });
}

/// Runs the `uniqueIndexNames` top-level operation.
/// This function is part of the file-level API and gives callers a direct way to use the associated scheduler behavior.
Set<String> get uniqueIndexNames {
  return {
    for (final spec in PersistenceIndexCatalog.all)
      if (spec.unique) spec.name,
  };
}

/// Runs the `indexByName` top-level operation.
/// This function is part of the file-level API and gives callers a direct way to use the associated scheduler behavior.
PersistenceIndexSpec indexByName(String name) {
  return PersistenceIndexCatalog.all.singleWhere((spec) => spec.name == name);
}
