/*
 * Copyright 2021-2025 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 org.opentest4j.reporting.events.core;

import org.apiguardian.api.API;
import org.opentest4j.reporting.events.api.Element;
import org.opentest4j.reporting.events.api.Factory;

import java.net.URI;
import java.time.LocalDateTime;
import java.util.Optional;

import static org.apiguardian.api.API.Status.EXPERIMENTAL;

/**
 * Factory for elements of the core namespace.
 *
 * @since 0.1.0
 */
@API(status = EXPERIMENTAL, since = "0.1.0")
public class CoreFactory {

	private CoreFactory() {
	}

	/**
	 * Create a factory for {@link Infrastructure} elements.
	 *
	 * @return Infrastructure factory
	 */
	public static Factory<Infrastructure> infrastructure() {
		return Infrastructure::new;
	}

	/**
	 * Create a factory for {@link HostName} elements.
	 *
	 * @param hostName the host name
	 * @return HostName factory
	 */
	public static Factory<HostName> hostName(String hostName) {
		return context -> new HostName(context, hostName);
	}

	/**
	 * Create a factory for {@link UserName} elements.
	 *
	 * @param userName the user name
	 * @return UserName factory
	 */
	public static Factory<UserName> userName(String userName) {
		return context -> new UserName(context, userName);
	}

	/**
	 * Create a factory for {@link OperatingSystem} elements.
	 *
	 * @param osName the name of the operating system
	 * @return OperatingSystem factory
	 */
	public static Factory<OperatingSystem> operatingSystem(String osName) {
		return context -> new OperatingSystem(context, osName);
	}

	/**
	 * Create a factory for {@link CpuCores} elements.
	 *
	 * @param cpuCores the number of CPU cores
	 * @return CpuCores factory
	 */
	public static Factory<CpuCores> cpuCores(int cpuCores) {
		return context -> new CpuCores(context, cpuCores);
	}

	/**
	 * Create a factory for {@link Sources} elements.
	 *
	 * @return Sources factory
	 */
	public static Factory<Sources> sources() {
		return Sources::new;
	}

	/**
	 * Create a factory for {@link Metadata} elements.
	 *
	 * @return Metadata factory
	 */
	public static Factory<Metadata> metadata() {
		return Metadata::new;
	}

	/**
	 * Create a factory for {@link Tags} elements.
	 *
	 * @return Tags factory
	 */
	public static Factory<Tags> tags() {
		return Tags::new;
	}

	/**
	 * Create a factory for {@link Tag} elements.
	 *
	 * @param value the value of the tag
	 * @return Tag factory
	 */
	public static Factory<Tag> tag(String value) {
		return context -> new Tag(context, value);
	}

	/**
	 * Create a factory for {@link Attachments} elements.
	 *
	 * @return Attachments factory
	 */
	public static Factory<Attachments> attachments() {
		return Attachments::new;
	}

	/**
	 * Create a factory for {@link Data} elements.
	 *
	 * @param timestamp the timestamp of the data
	 * @return Data factory
	 */
	public static Factory<Data> data(LocalDateTime timestamp) {
		return context -> new Data(context).withTime(timestamp);
	}

	/**
	 * Create a factory for {@link File} elements.
	 *
	 * @param timestamp the timestamp of creating the file
	 * @return File factory
	 * @since 0.2.0
	 */
	public static Factory<File> file(LocalDateTime timestamp) {
		return context -> new File(context).withTime(timestamp);
	}

	/**
	 * Create a factory for {@link Output} elements.
	 *
	 * @param timestamp the timestamp the output was created
	 * @return Output factory
	 * @since 0.2.0
	 */
	public static Factory<Output> output(LocalDateTime timestamp) {
		return context -> new Output(context).withTime(timestamp);
	}

	/**
	 * Create a factory for {@link Result} elements.
	 *
	 * @param status the status of the result
	 * @return Result factory
	 */
	public static Factory<Result> result(Result.Status status) {
		return context -> new Result(context).withStatus(status);
	}

	/**
	 * Create a factory for {@link Reason} elements.
	 *
	 * @param reason the reason
	 * @return Reason factory
	 */
	public static Factory<Reason> reason(String reason) {
		return context -> new Reason(context, reason);
	}

	/**
	 * Create a factory for {@link FileSource} elements.
	 *
	 * @param file the source file
	 * @return FileSource factory
	 */
	public static Factory<FileSource> fileSource(java.io.File file) {
		return context -> new FileSource(context).withPath(file);
	}

	/**
	 * Create a factory for {@link DirectorySource} elements.
	 *
	 * @param dir the source directory
	 * @return DirectorySource factory
	 */
	public static Factory<DirectorySource> directorySource(java.io.File dir) {
		return context -> new DirectorySource(context).withPath(dir);
	}

	/**
	 * Create a factory for {@link UriSource} elements.
	 *
	 * @param uri the source URI
	 * @return UriSource factory
	 */
	public static Factory<UriSource> uriSource(URI uri) {
		return context -> new UriSource(context).withUri(uri);
	}

	/**
	 * Create a factory for {@link FilePosition} elements.
	 *
	 * @param line   the line number
	 * @param column the column number
	 * @param <P>    parent element type
	 * @return FilePosition factory
	 */
	@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
	public static <P extends Element<P>> Factory<FilePosition<P>> filePosition(int line, Optional<Integer> column) {
		return context -> {
			FilePosition<P> filePosition = new FilePosition<>(context);
			filePosition.withAttribute(FilePosition.LINE, String.valueOf(line));
			column.ifPresent(value -> filePosition.withAttribute(FilePosition.COLUMN, value.toString()));
			return filePosition;
		};
	}
}
