import { AppError } from '../errors/app-error';
import type { ErrorContext } from '../errors/app-error';
import { ErrorCode } from '../types/error-codes';
import { isAppError } from '../guards/type-guards';

/**
 * Wrap an error with context.
 * Similar to Go's `fmt.Errorf("context: %w", err)`.
 *
 * @param error - The error to wrap (can be any type)
 * @param context - Description of the operation that failed
 * @param additionalContext - Extra context data to include
 * @returns An AppError with the original as its cause
 *
 * @example
 * ```typescript
 * try {
 *   await fetchData();
 * } catch (error) {
 *   return err(wrap(error, 'fetching user data'));
 * }
 * ```
 */
export function wrap(error: unknown, context: string, additionalContext?: ErrorContext): AppError {
	if (isAppError(error)) {
		return error.wrap(context, additionalContext);
	}

	const message = error instanceof Error ? error.message : String(error);
	return new AppError({
		code: ErrorCode.UNKNOWN_ERROR,
		message: `${context}: ${message}`,
		cause: error instanceof Error ? error : undefined,
		context: additionalContext,
	});
}

/**
 * Convert any error to AppError.
 * If already an AppError, returns it unchanged.
 *
 * @example
 * ```typescript
 * try {
 *   await riskyOperation();
 * } catch (error) {
 *   return err(toAppError(error));
 * }
 * ```
 */
export function toAppError(error: unknown): AppError {
	if (isAppError(error)) {
		return error;
	}

	if (error instanceof Error) {
		return new AppError({
			code: ErrorCode.UNKNOWN_ERROR,
			message: error.message,
			cause: error,
		});
	}

	return new AppError({
		code: ErrorCode.UNKNOWN_ERROR,
		message: String(error),
	});
}

/**
 * Get the cause of an error.
 *
 * @example
 * ```typescript
 * const originalError = cause(wrappedError);
 * ```
 */
export function cause(error: AppError): Error | undefined {
	return error.cause;
}

/**
 * Get the root cause of an error chain.
 * Traverses all causes to find the original error.
 *
 * @example
 * ```typescript
 * const original = rootCause(deeplyWrappedError);
 * ```
 */
export function rootCause(error: AppError): Error {
	return error.rootCause();
}
