/*
 * Copyright 2024-2026 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.cloud.ai.graph.checkpoint;

import com.alibaba.cloud.ai.graph.RunnableConfig;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

import static java.util.Optional.ofNullable;

public interface BaseCheckpointSaver {
	String THREAD_ID_DEFAULT = "$default";
	String CHECKPOINTS_NUM_RETAINED = "checkpoints.numRetained";

	default Optional<Checkpoint> getLast(LinkedList<Checkpoint> checkpoints, RunnableConfig config) {
		return (checkpoints.isEmpty()) ? Optional.empty() : ofNullable(checkpoints.peek());
	}

	default Optional<Integer> checkpointsNumRetained(RunnableConfig config) {
		return config.metadata(CHECKPOINTS_NUM_RETAINED).map(value -> {
			if (value instanceof Number number) {
				return number.intValue();
			}
			if (value instanceof String text) {
				return Integer.parseInt(text);
			}
			throw new IllegalArgumentException(
					"checkpoints.numRetained must be a number or numeric string, got: " + value.getClass().getName());
		}).filter(value -> value > 0);
	}

	default void retainLatestCheckpoints(LinkedList<Checkpoint> checkpoints, RunnableConfig config) {
		checkpointsNumRetained(config).ifPresent(numRetained -> {
			while (checkpoints.size() > numRetained) {
				checkpoints.removeLast();
			}
		});
	}

	Collection<Checkpoint> list(RunnableConfig config);

	Optional<Checkpoint> get(RunnableConfig config);

	RunnableConfig put(RunnableConfig config, Checkpoint checkpoint) throws Exception;

	Tag release(RunnableConfig config) throws Exception;

	record Tag(String threadId, Collection<Checkpoint> checkpoints) {
		public Tag(String threadId, Collection<Checkpoint> checkpoints) {
			this.threadId = threadId;
			this.checkpoints = ofNullable(checkpoints).map(List::copyOf).orElseGet(List::of);
		}
	}

}
