<?php
// DESCRIPTION: Classifies Google API exceptions as transient (retryable) or permanent
// to determine whether a failed sync should be retried or dead-lettered.

namespace SheetWise\Sync;

use SheetWise\Vendor\Google\Service\Exception as GoogleServiceException;

class ErrorClassifier {

	/**
	 * HTTP status codes that indicate transient (retryable) failures
	 *
	 * @since 2.0.0
	 *
	 * @var array
	 */
	const TRANSIENT_HTTP_CODES = [ 429, 500, 502, 503, 504 ];

	/**
	 * HTTP status codes that indicate permanent (non-retryable) failures
	 *
	 * @since 2.0.0
	 *
	 * @var array
	 */
	const PERMANENT_HTTP_CODES = [ 400, 401, 403, 404 ];

	/**
	 * Check if an exception represents a transient (retryable) failure
	 *
	 * @since 2.0.0
	 *
	 * @param \Exception $e
	 *
	 * @return bool
	 */
	public static function is_transient( \Exception $e ) {
		$code = self::get_http_code( $e );

		$is_transient = in_array( $code, self::TRANSIENT_HTTP_CODES, true )
			|| self::is_connection_error( $e );

		/**
		 * Override whether an exception is classified as transient
		 *
		 * @since 2.0.0
		 *
		 * @param bool       $is_transient Whether the error is transient
		 * @param \Exception $e            The exception
		 */
		return apply_filters( 'swise_error_is_transient', $is_transient, $e );
	}

	/**
	 * Check if an exception represents a permanent (non-retryable) failure
	 *
	 * @since 2.0.0
	 *
	 * @param \Exception $e
	 *
	 * @return bool
	 */
	public static function is_permanent( \Exception $e ) {
		$code = self::get_http_code( $e );

		return in_array( $code, self::PERMANENT_HTTP_CODES, true );
	}

	/**
	 * Extract structured error details from an exception
	 *
	 * @since 2.0.0
	 *
	 * @param \Exception $e
	 *
	 * @return array
	 */
	public static function get_error_details( \Exception $e ) {
		$details = [
			'code'    => $e->getCode(),
			'message' => $e->getMessage(),
			'type'    => 'unknown',
		];

		if ( self::is_transient( $e ) ) {
			$details['type'] = 'transient';
		} elseif ( self::is_permanent( $e ) ) {
			$details['type'] = 'permanent';
		}

		if ( $e instanceof GoogleServiceException ) {
			$errors = $e->getErrors();
			if ( ! empty( $errors[0] ) ) {
				$details['reason']  = $errors[0]['reason'] ?? '';
				$details['domain']  = $errors[0]['domain'] ?? '';
				$details['message'] = $errors[0]['message'] ?? $details['message'];
			}
		}

		return $details;
	}

	/**
	 * Get the HTTP status code from an exception
	 *
	 * @since 2.0.0
	 *
	 * @param \Exception $e
	 *
	 * @return int
	 */
	private static function get_http_code( \Exception $e ) {
		if ( $e instanceof GoogleServiceException ) {
			return (int) $e->getCode();
		}

		return (int) $e->getCode();
	}

	/**
	 * Check if an exception represents a connection-level error (timeout, DNS, etc.)
	 *
	 * @since 2.0.0
	 *
	 * @param \Exception $e
	 *
	 * @return bool
	 */
	private static function is_connection_error( \Exception $e ) {
		$message = strtolower( $e->getMessage() );

		$connection_patterns = [
			'curl error',
			'connection timed out',
			'connection refused',
			'could not resolve host',
			'ssl connection',
			'operation timed out',
		];

		foreach ( $connection_patterns as $pattern ) {
			if ( strpos( $message, $pattern ) !== false ) {
				return true;
			}
		}

		return false;
	}
}
