<?php

namespace Limb_Chatbot\Includes\Services\Knowledge;

use Limb_Chatbot\Includes\Data_Objects\Dataset;

/**
 * Persists source attachment meta from source content.
 *
 * @since 1.2.0
 */
class Source_Attachment_Meta_Service {

	/**
	 * Update dataset attachment metas from content and extra URLs.
	 *
	 * @param  Dataset  $dataset
	 * @param  string  $source_content
	 * @param  array  $extra_urls
	 *
	 * @return void
	 */
	public function update_dataset_attachment_meta( Dataset $dataset, string $source_content, array $extra_urls = [] ): void {
		$base_url     = (string) ( $dataset->source_url() ?? '' );
		$from_content = $this->extract_urls_from_content( $source_content, $base_url );

		$image_urls = $from_content['image_urls'];
		$file_urls  = $from_content['file_urls'];

		foreach ( $extra_urls as $url ) {
			if ( ! is_string( $url ) || '' === trim( $url ) ) {
				continue;
			}

			$resolved_url = $this->resolve_url( (string) $url, $base_url );
			$type         = $this->classify_url( $resolved_url );
			if ( 'image' === $type ) {
				$image_urls[] = $resolved_url;
				continue;
			}

			if ( 'file' === $type ) {
				$file_urls[] = $resolved_url;
			}
		}

		$image_urls = $this->normalize_url_list( $image_urls, $base_url );
		$file_urls  = $this->normalize_url_list( $file_urls, $base_url );
		$all_urls   = $this->normalize_url_list( array_merge( $image_urls, $file_urls ) );

		$dataset->update_meta( 'source_image_urls', $image_urls );
		$dataset->update_meta( 'source_file_urls', $file_urls );
		$dataset->update_meta( 'source_attachment_urls', $all_urls );
	}

	/**
	 * Extract image and file URLs from HTML/markdown content.
	 *
	 * @param  string  $content
	 *
	 * @return array<string,array>
	 */
	private function extract_urls_from_content( string $content, string $base_url = '' ): array {
		$image_urls = [];
		$file_urls  = [];

		if ( preg_match_all( '/<img[^>]+src=["\']([^"\']+)["\']/i', $content, $img_matches ) ) {
			$image_urls = array_merge( $image_urls, $img_matches[1] );
		}

		if ( preg_match_all( '/!\[[^\]]*\]\(([^)]+)\)/', $content, $md_img_matches ) ) {
			$image_urls = array_merge( $image_urls, $md_img_matches[1] );
		}

		$link_urls = [];
		if ( preg_match_all( '/<a[^>]+href=["\']([^"\']+)["\']/i', $content, $link_matches ) ) {
			$link_urls = array_merge( $link_urls, $link_matches[1] );
		}

		if ( preg_match_all( '/(?<!!)\[[^\]]*\]\(([^)]+)\)/', $content, $md_link_matches ) ) {
			$link_urls = array_merge( $link_urls, $md_link_matches[1] );
		}

		foreach ( $link_urls as $url ) {
			$resolved_url = $this->resolve_url( (string) $url, $base_url );
			$type         = $this->classify_url( $resolved_url );
			if ( 'image' === $type ) {
				$image_urls[] = $resolved_url;
				continue;
			}
			if ( 'file' === $type ) {
				$file_urls[] = $resolved_url;
			}
		}

		return [
			'image_urls' => $this->normalize_url_list( $image_urls, $base_url ),
			'file_urls'  => $this->normalize_url_list( $file_urls, $base_url ),
		];
	}

	/**
	 * Classify URL as image/file/unknown.
	 *
	 * @param  string  $url
	 *
	 * @return string
	 */
	private function classify_url( string $url ): string {
		$path      = (string) wp_parse_url( $url, PHP_URL_PATH );
		$extension = strtolower( pathinfo( $path, PATHINFO_EXTENSION ) );

		if ( '' === $extension ) {
			return '';
		}

		$image_extensions = [ 'jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'avif' ];
		if ( in_array( $extension, $image_extensions, true ) ) {
			return 'image';
		}

		$file_extensions = [
			'pdf',
			'txt',
			'csv',
			'doc',
			'docx',
			'xls',
			'xlsx',
			'ppt',
			'pptx',
			'rtf',
			'odt',
			'ods',
			'odp',
			'zip',
			'rar',
			'7z',
			'json',
			'xml',
			'mp3',
			'wav',
			'mp4',
			'mov',
			'avi',
			'webm',
		];

		return in_array( $extension, $file_extensions, true ) ? 'file' : '';
	}

	/**
	 * Normalize URL list to unique valid URLs.
	 *
	 * @param  array  $urls
	 *
	 * @return array
	 */
	private function normalize_url_list( array $urls, string $base_url = '' ): array {
		$urls = array_map(
			function ( $url ) use ( $base_url ) {
				return $this->resolve_url( (string) $url, $base_url );
			},
			$urls
		);
		$urls = array_values(
			array_filter(
				$urls,
				static function ( $url ) {
					return is_string( $url ) && '' !== $url && false !== filter_var( $url, FILTER_VALIDATE_URL );
				}
			)
		);

		return array_values( array_unique( $urls ) );
	}

	/**
	 * Resolve protocol-relative or relative URLs into absolute URLs.
	 *
	 * @param  string  $url
	 * @param  string  $base_url
	 *
	 * @return string
	 */
	private function resolve_url( string $url, string $base_url = '' ): string {
		$url = trim( html_entity_decode( $url, ENT_QUOTES | ENT_HTML5, 'UTF-8' ) );
		if ( '' === $url ) {
			return '';
		}

		// Already absolute URL.
		if ( false !== filter_var( $url, FILTER_VALIDATE_URL ) ) {
			return $url;
		}

		$base_scheme = (string) wp_parse_url( $base_url, PHP_URL_SCHEME );
		$base_host   = (string) wp_parse_url( $base_url, PHP_URL_HOST );

		// Protocol-relative URL: //cdn.example.com/image.jpg
		if ( 0 === strpos( $url, '//' ) ) {
			$scheme = '' !== $base_scheme ? $base_scheme : 'https';
			return $scheme . ':' . $url;
		}

		// Root-relative URL: /wiki/File:Something.jpg
		if ( '/' === substr( $url, 0, 1 ) && '' !== $base_host ) {
			$scheme = '' !== $base_scheme ? $base_scheme : 'https';
			return $scheme . '://' . $base_host . $url;
		}

		return $url;
	}
}
