<?php
/**
 * IP ASN lookup for Cloud Firewall (premium).
 *
 * Uses transient caching and on-demand HTTP fallback (ip-api.com) when local ASN DB is not bundled.
 * Lookup runs only when the firewall needs ASN for softening decisions—not on every page view.
 *
 * @package WPSecurityNinja
 */

namespace WPSecurityNinja\Plugin;

defined( 'ABSPATH' ) || exit;

/**
 * Minimal ASN resolution with caching.
 */
class SN_Ip_Asn {

	const CACHE_PREFIX = 'sn_cf_asn_v1_';

	const CACHE_TTL_HIT = WEEK_IN_SECONDS;

	const CACHE_TTL_MISS = HOUR_IN_SECONDS;

	/**
	 * Resolve ASN for an IP address.
	 *
	 * @param string $ip IPv4 or IPv6.
	 * @return array|null Array with keys 'asn' (int), 'label' (string), or null if unknown/error.
	 */
	public static function get_asn_for_ip( $ip ) {
		if ( ! is_string( $ip ) || '' === $ip ) {
			return null;
		}

		if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
			return null;
		}

		$cache_key = self::CACHE_PREFIX . md5( $ip );
		$cached    = get_transient( $cache_key );

		if ( false !== $cached && is_array( $cached ) ) {
			if ( array_key_exists( 'asn', $cached ) && null === $cached['asn'] ) {
				return null;
			}
			if ( isset( $cached['asn'] ) && is_int( $cached['asn'] ) ) {
				return $cached;
			}
		}

		$fetched = self::fetch_asn_via_api( $ip );

		if ( null === $fetched ) {
			set_transient(
				$cache_key,
				array( 'asn' => null ),
				self::CACHE_TTL_MISS
			);
			return null;
		}

		set_transient( $cache_key, $fetched, self::CACHE_TTL_HIT );

		return $fetched;
	}

	/**
	 * Query ip-api.com (HTTPS, minimal fields). Cached per IP elsewhere.
	 *
	 * @param string $ip IP address.
	 * @return array|null
	 */
	private static function fetch_asn_via_api( $ip ) {
		$url = sprintf(
			'https://ip-api.com/json/%s?fields=status,message,as',
			rawurlencode( $ip )
		);

		$response = wp_safe_remote_get(
			$url,
			array(
				'timeout' => 3,
				'headers' => array(
					'Accept' => 'application/json',
				),
			)
		);

		if ( is_wp_error( $response ) ) {
			return null;
		}

		$code = wp_remote_retrieve_response_code( $response );
		if ( 200 !== (int) $code ) {
			return null;
		}

		$body = wp_remote_retrieve_body( $response );
		if ( '' === $body ) {
			return null;
		}

		$data = json_decode( $body, true );
		if ( ! is_array( $data ) ) {
			return null;
		}

		if ( isset( $data['status'] ) && 'fail' === $data['status'] ) {
			return null;
		}

		if ( empty( $data['as'] ) || ! is_string( $data['as'] ) ) {
			return null;
		}

		// Typical format: "AS14593 Space Exploration Technologies INC".
		if ( ! preg_match( '/AS(\d+)/i', $data['as'], $m ) ) {
			return null;
		}

		$asn = (int) $m[1];
		if ( $asn < 1 ) {
			return null;
		}

		return array(
			'asn'   => $asn,
			'label' => sanitize_text_field( $data['as'] ),
		);
	}
}
