<?php

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Server-side resolution of agent navigation requests for the Niwa frontend
 * agent `navigate_to_page` tool.
 *
 * Resolves a navigation target (checkout, cart, shop, account, home, blog),
 * a specific post_id, a free-text page query, or an explicit URL into a safe
 * internal URL the chat client can redirect the visitor's browser to.
 *
 * Only same-site URLs are ever returned — external URLs are rejected.
 */
class Niwa_AI_Navigation_Tool_Service {

	/**
	 * Resolve tool arguments into a navigation result.
	 *
	 * @param array $args Tool arguments: target|post_id|page_query|url.
	 * @return array
	 */
	public function resolve( $args = array() ) {
		$args = is_array( $args ) ? $args : array();

		$target     = isset( $args['target'] ) ? sanitize_key( (string) $args['target'] ) : '';
		$post_id    = isset( $args['post_id'] ) ? absint( $args['post_id'] ) : 0;
		$page_query = isset( $args['page_query'] ) ? trim( sanitize_text_field( (string) $args['page_query'] ) ) : '';
		$url        = isset( $args['url'] ) ? trim( (string) $args['url'] ) : '';

		if ( '' !== $target ) {
			$known = $this->resolve_known_target( $target );
			if ( ! empty( $known ) ) {
				return $known;
			}
		}

		if ( $post_id > 0 ) {
			return $this->resolve_post_id( $post_id );
		}

		if ( '' !== $url ) {
			return $this->resolve_explicit_url( $url );
		}

		if ( '' !== $page_query ) {
			return $this->resolve_page_query( $page_query );
		}

		return $this->error(
			'A navigation destination is required. Pass target, post_id, page_query, or url.',
			'navigation_target_required'
		);
	}

	/**
	 * Map well-known destinations to their store URLs.
	 *
	 * @param string $target Sanitized target keyword.
	 * @return array Navigation result, or empty array when the keyword is unknown.
	 */
	private function resolve_known_target( $target ) {
		$aliases = array(
			'checkout'   => 'checkout',
			'cart'       => 'cart',
			'basket'     => 'cart',
			'korpa'      => 'cart',
			'shop'       => 'shop',
			'store'      => 'shop',
			'prodavnica' => 'shop',
			'account'    => 'account',
			'my_account' => 'account',
			'myaccount'  => 'account',
			'nalog'      => 'account',
			'home'       => 'home',
			'pocetna'    => 'home',
			'blog'       => 'blog',
		);

		if ( ! isset( $aliases[ $target ] ) ) {
			return array();
		}

		$canonical = $aliases[ $target ];
		$url       = '';
		$title     = '';

		switch ( $canonical ) {
			case 'checkout':
				if ( function_exists( 'wc_get_checkout_url' ) ) {
					$url   = (string) wc_get_checkout_url();
					$title = 'Checkout';
				}
				break;
			case 'cart':
				if ( function_exists( 'wc_get_cart_url' ) ) {
					$url   = (string) wc_get_cart_url();
					$title = 'Cart';
				}
				break;
			case 'shop':
				if ( function_exists( 'wc_get_page_permalink' ) ) {
					$url   = (string) wc_get_page_permalink( 'shop' );
					$title = 'Shop';
				}
				break;
			case 'account':
				if ( function_exists( 'wc_get_page_permalink' ) ) {
					$url   = (string) wc_get_page_permalink( 'myaccount' );
					$title = 'My Account';
				}
				break;
			case 'home':
				$url   = home_url( '/' );
				$title = 'Home';
				break;
			case 'blog':
				$url   = $this->resolve_blog_url();
				$title = 'Blog';
				break;
		}

		if ( '' === $url ) {
			return $this->error(
				'That destination is not available on this store. Use page_query to find the page instead.',
				'navigation_target_unavailable'
			);
		}

		return $this->success( $url, $title, $canonical );
	}

	/**
	 * Blog URL: posts page when configured, otherwise empty.
	 *
	 * @return string
	 */
	private function resolve_blog_url() {
		if ( ! function_exists( 'get_option' ) || ! function_exists( 'get_permalink' ) ) {
			return '';
		}
		$posts_page = intval( get_option( 'page_for_posts' ) );
		if ( $posts_page > 0 ) {
			$permalink = get_permalink( $posts_page );
			if ( is_string( $permalink ) && '' !== $permalink ) {
				return $permalink;
			}
		}
		return '';
	}

	/**
	 * Permalink for a specific published post/page/product.
	 *
	 * @param int $post_id Post ID.
	 * @return array
	 */
	private function resolve_post_id( $post_id ) {
		if ( ! function_exists( 'get_post' ) || ! function_exists( 'get_permalink' ) ) {
			return $this->error( 'Page lookup is not available.', 'navigation_lookup_unavailable' );
		}

		$post = get_post( $post_id );
		if ( ! $post || 'publish' !== $post->post_status ) {
			return $this->error( 'That page was not found.', 'navigation_page_not_found' );
		}

		$permalink = get_permalink( $post );
		if ( ! is_string( $permalink ) || '' === $permalink ) {
			return $this->error( 'That page has no public URL.', 'navigation_page_not_found' );
		}

		return $this->success( $permalink, (string) $post->post_title, 'page' );
	}

	/**
	 * Validate an explicit URL as internal (same host as this site).
	 *
	 * @param string $url Raw URL from the tool call.
	 * @return array
	 */
	private function resolve_explicit_url( $url ) {
		$internal = $this->to_internal_url( $url );
		if ( '' === $internal ) {
			return $this->error(
				'Only pages on this store website can be opened. Use page_query to find the right page on this site.',
				'navigation_external_url_blocked'
			);
		}

		return $this->success( $internal, '', 'url' );
	}

	/**
	 * Normalize a URL and verify it points at this site.
	 *
	 * Accepts absolute same-host URLs and site-relative paths ("/checkout/").
	 * Rejects everything else (other hosts, javascript:, data:, …).
	 *
	 * @param string $url Raw URL.
	 * @return string Validated absolute URL, or '' when rejected.
	 */
	public function to_internal_url( $url ) {
		$url = trim( (string) $url );
		if ( '' === $url ) {
			return '';
		}

		$home       = home_url( '/' );
		$parse      = function_exists( 'wp_parse_url' ) ? 'wp_parse_url' : 'parse_url';
		$home_parts = $parse( $home );
		$home_host  = isset( $home_parts['host'] ) ? strtolower( (string) $home_parts['host'] ) : '';

		// Site-relative path.
		if ( 0 === strpos( $url, '/' ) && 0 !== strpos( $url, '//' ) ) {
			$url = rtrim( $home, '/' ) . $url;
		}

		$parts = $parse( $url );
		if ( ! is_array( $parts ) || empty( $parts['host'] ) ) {
			return '';
		}

		$scheme = isset( $parts['scheme'] ) ? strtolower( (string) $parts['scheme'] ) : '';
		if ( ! in_array( $scheme, array( 'http', 'https' ), true ) ) {
			return '';
		}

		if ( '' === $home_host || strtolower( (string) $parts['host'] ) !== $home_host ) {
			return '';
		}

		return function_exists( 'esc_url_raw' ) ? esc_url_raw( $url ) : $url;
	}

	/**
	 * Find a published page/post/product by free-text title query.
	 *
	 * @param string $page_query Visitor-language page name.
	 * @return array
	 */
	private function resolve_page_query( $page_query ) {
		if ( ! class_exists( 'WP_Query' ) ) {
			return $this->error( 'Page search is not available.', 'navigation_search_unavailable' );
		}

		$post_types = array( 'page', 'post' );
		if ( class_exists( 'WooCommerce' ) ) {
			$post_types[] = 'product';
		}

		$query = new WP_Query(
			array(
				'post_type'           => $post_types,
				'post_status'         => 'publish',
				'posts_per_page'      => 8,
				's'                   => $page_query,
				'no_found_rows'       => true,
				'ignore_sticky_posts' => true,
			)
		);

		$posts = isset( $query->posts ) && is_array( $query->posts ) ? $query->posts : array();
		if ( empty( $posts ) ) {
			return $this->error(
				'No page on this site matches that name. Tell the customer you could not find that page.',
				'navigation_page_not_found'
			);
		}

		$query_norm = $this->normalize_text( $page_query );
		$scored     = array();
		foreach ( $posts as $post ) {
			$title_norm = $this->normalize_text( (string) $post->post_title );
			if ( '' === $title_norm ) {
				continue;
			}

			if ( $title_norm === $query_norm ) {
				return $this->success_from_post( $post );
			}

			$score = 0;
			if ( false !== strpos( $title_norm, $query_norm ) || false !== strpos( $query_norm, $title_norm ) ) {
				$score = 50;
			} else {
				foreach ( preg_split( '/\s+/', $query_norm ) as $token ) {
					if ( mb_strlen( $token ) >= 3 && false !== strpos( ' ' . $title_norm . ' ', ' ' . $token . ' ' ) ) {
						$score += 10;
					}
				}
			}

			if ( $score > 0 ) {
				$scored[] = array(
					'post'  => $post,
					'score' => $score,
				);
			}
		}

		if ( empty( $scored ) ) {
			// Fall back to the first search hit — WP relevance order.
			return $this->success_from_post( $posts[0] );
		}

		usort(
			$scored,
			function ( $a, $b ) {
				return $b['score'] - $a['score'];
			}
		);

		$top        = $scored[0];
		$runner_up  = isset( $scored[1] ) ? $scored[1] : null;
		$ambiguous  = $runner_up && $runner_up['score'] === $top['score'] && $top['score'] < 50;

		if ( $ambiguous ) {
			$matches = array();
			foreach ( array_slice( $scored, 0, 5 ) as $entry ) {
				$matches[] = array(
					'post_id' => intval( $entry['post']->ID ),
					'title'   => (string) $entry['post']->post_title,
				);
			}

			return array(
				'success' => false,
				'status'  => 'needs_selection',
				'code'    => 'ambiguous_page',
				'message' => 'More than one page matches. Ask the customer which one they meant, then call navigate_to_page again with its post_id.',
				'matches' => $matches,
			);
		}

		return $this->success_from_post( $top['post'] );
	}

	private function success_from_post( $post ) {
		$permalink = function_exists( 'get_permalink' ) ? get_permalink( $post ) : '';
		if ( ! is_string( $permalink ) || '' === $permalink ) {
			return $this->error( 'That page has no public URL.', 'navigation_page_not_found' );
		}
		return $this->success( $permalink, (string) $post->post_title, 'page' );
	}

	private function success( $url, $title, $target ) {
		return array(
			'success'    => true,
			'status'     => 'navigate',
			'url'        => (string) $url,
			'page_title' => (string) $title,
			'target'     => (string) $target,
			'message'    => 'The customer browser will be redirected to this page. Reply with ONE short sentence in the customer\'s language telling them you are taking them there. Do not add a page link button for the same destination.',
		);
	}

	private function normalize_text( $value ) {
		$value = strtolower( trim( (string) $value ) );
		if ( function_exists( 'remove_accents' ) ) {
			$value = remove_accents( $value );
		}
		$value = str_replace( array( '-', '_' ), ' ', $value );
		$value = preg_replace( '/[^\p{L}\p{N}\s]/u', ' ', $value );
		$value = preg_replace( '/\s+/', ' ', $value );
		return trim( $value );
	}

	private function error( $message, $code ) {
		return array(
			'success' => false,
			'status'  => 'error',
			'code'    => (string) $code,
			'message' => (string) $message,
		);
	}
}
