<?php
/**
 * FontAwesomeSVG
 *
 * @description Prints FontAwesome's SVG markup instead of using the JS-With-SVG library or font files.
 *
 * @link https://github.com/husseinalhammad/FontAwesomeSVG-PHP
 * @package StrategyBlocks
 */

namespace StrategyBlocks;

/**
 * FontAwesomeSVG
 *
 * @description Prints FontAwesome's SVG markup instead of using the JS-With-SVG library or font files.
 *
 * @param $svg_dir The directory of the Font Awesome .svg files.
 *
 * @package StrategyBlocks
 */
class FontAwesomeSVG {

	/**
	 * The directory of the Font Awesome .svg files.
	 *
	 * @var string $svg_dir The directory of the Font Awesome .svg files.
	 */
	public $svg_dir;

	/**
	 * Constructor ensures we have a directory set.
	 *
	 * @param string $svg_dir The directory of the Font Awesome .svg files.
	 * @throws \Exception When there the svg directory does not exist.
	 */
	public function __construct( $svg_dir ) {
		if ( ! is_dir( $svg_dir ) ) {
			throw new \Exception( 'Directory' . esc_html( $svg_dir ) . 'does not exist' );
		}

		$this->svg_dir = $svg_dir;
	}

	/**
	 * Get SVG icon
	 *
	 * @param string $id    icon ID e.g. fas fa-house
	 * @param array  $opts   options array
	 * @return string|boolean
	 */
	public function get_svg( $id, $opts = [] ) {
		try {
			$normalized_id = $this->normalize_icon_identifier( $id );
			$icon = $this->get_icon_details( $normalized_id );
		} catch ( \Exception $e ) {
			return false;
		}

		$doc = new \DOMDocument();
		$doc->load( $icon['filepath'] );

		$default_opts = [
			'title'         => false,
			'class'         => false,
			'default_class' => true,
			'inline_style'  => true,
			'role'          => 'img',
			'fill'          => 'currentColor',
		];

		$opts = array_merge( $default_opts, $opts );

		$classes = '';
		if ( $opts['default_class'] ) {
			$classes .= 'svg-inline--fa';
		}
		if ( $opts['class'] ) {
			$classes .= ' ' . $opts['class'];
		}

		// $opts[aria-*]
		// strlen('aria-') = 5
		$aria_opts = array_filter(
			$opts,
			function ( $item, $key ) {
				if ( 'aria-' == substr( $key, 0, 5 ) ) {
					return $item;
				}
			},
			ARRAY_FILTER_USE_BOTH
		);

		foreach ( $doc->getElementsByTagName( 'svg' ) as $item ) {
			if ( '' != $classes ) {
				$item->setAttribute( 'class', $classes );
			}
			if ( $opts['role'] ) {
				$item->setAttribute( 'role', $opts['role'] );
			}

			foreach ( $aria_opts as $key => $val ) {
				$item->setAttribute( $key, $val );
			}

			if ( $opts['title'] ) {
				$title = $doc->createElement( 'title' );
                // @codingStandardsIgnoreStart
				$title->nodeValue = $opts['title'];
                // @codingStandardsIgnoreEnd
				$title_node = $item->appendChild( $title );

				// <title> id attribute has to be set to add aria-labelledby
				if ( isset( $opts['title_id'] ) ) {
					$title_id = $opts['title_id'];
					$title_node->setAttribute( 'id', $title_id );
					$item->setAttribute( 'aria-labelledby', $title_id );
				}
			}

			if ( ! isset( $aria_opts['aria-label'] ) && ! isset( $opts['title_id'] ) ) {
				$item->setAttribute( 'aria-hidden', 'true' );
			}
		}

		foreach ( $doc->getElementsByTagName( 'path' ) as $item ) {
			$fill = $opts['fill'];
			$opacity = false;

			// duotone
			switch ( $item->getAttribute( 'class' ) ) {
				case 'fa-primary':
					if ( isset( $opts['primary']['fill'] ) ) { $fill = $opts['primary']['fill'];
					}
					if ( isset( $opts['primary']['opacity'] ) ) { $opacity = $opts['primary']['opacity'];
					}
					break;

				case 'fa-secondary':
					if ( isset( $opts['secondary']['fill'] ) ) { $fill = $opts['secondary']['fill'];
					}
					if ( isset( $opts['secondary']['opacity'] ) ) { $opacity = $opts['secondary']['opacity'];
					}
					break;
			}

			$item->setAttribute( 'fill', $fill );
			if ( $opacity ) {
				$item->setAttribute( 'opacity', $opacity );
			}
		}

		// duotone styles
		if ( $opts['inline_style'] ) {
			$styles = [];

			if ( isset( $opts['primary']['fill'] ) ) {
				$styles[] = '--fa-primary-color:' . $opts['primary']['fill'];
			}

			if ( isset( $opts['primary']['opacity'] ) ) {
				$styles[] = '--fa-primary-opacity:' . $opts['primary']['opacity'];
			}

			if ( isset( $opts['secondary']['fill'] ) ) {
				$styles[] = '--fa-secondary-color:' . $opts['secondary']['fill'];
			}

			if ( isset( $opts['secondary']['opacity'] ) ) {
				$styles[] = '--fa-secondary-opacity:' . $opts['secondary']['opacity'];
			}

			if ( empty( $styles ) || ! isset( $opts['primary']['fill'], $opts['secondary']['fill'] ) ) {
				$styles[] = 'color:' . $opts['fill'];
			}

			if ( $styles ) {
				foreach ( $doc->getElementsByTagName( 'svg' ) as $svg ) {
					$svg->setAttribute( 'style', implode( ';', $styles ) );
				}
			}
		}

		return $doc->saveHTML();
	}

	/**
	 * Normalizes various Font Awesome icon identifier formats into a standard class string.
	 *
	 * Accepts a wide range of input formats such as:
	 * - "fab fa-yammer" (valid and returned as-is)
	 * - "fontawesome/brands yammer"
	 * - "fab yammer"
	 * - "fa-yammer"
	 * - "yammer"
	 *
	 * The method maps style aliases (e.g., "fontawesome/brands") to their Font Awesome
	 * prefix equivalents (e.g., "fab") and ensures the icon class is prefixed with "fa-".
	 *
	 * Examples:
	 *   "fa-user"            => "fas fa-user"
	 *   "fontawesome/brands yammer" => "fab fa-yammer"
	 *   "far envelope"       => "far fa-envelope"
	 *   "yammer"             => "fas fa-yammer"
	 *
	 * @param string $input Raw icon identifier input.
	 * @return string Normalized Font Awesome class string (e.g., "fab fa-yammer").
	 * @throws \Exception If the input cannot be parsed into a valid format.
	 */
	private function normalize_icon_identifier( $input ): string {
		$input = trim( $input );

		$style_aliases = [
			'fontawesome/solid'   => 'fas',
			'fontawesome/regular' => 'far',
			'fontawesome/brands'  => 'fab',
			'fas'        => 'fas',
			'fa-solid'   => 'fas',
			'far'        => 'far',
			'fa-regular' => 'far',
			'fab'        => 'fab',
			'fa-brands'  => 'fab',
		];

		// Case 1: Already correct format: "fab fa-yammer"
		if ( preg_match( '/^fa[a-z\-]*\s+fa-[a-z0-9\-]+$/i', $input ) ) {
			return $input;
		}

		// Case 2: "fontawesome/brands yammer" or "fab yammer"
		if ( preg_match( '/^([a-z\-\/]+)\s+([a-z0-9\-]+)$/i', $input, $matches ) ) {
			$raw_prefix = strtolower( $matches[1] );
			$icon_name  = strtolower( $matches[2] );

			$prefix = $style_aliases[ $raw_prefix ] ?? 'fas';
			$icon   = strpos( $icon_name, 'fa-' ) === 0 ? $icon_name : "fa-{$icon_name}";

			return "{$prefix} {$icon}";
		}

		// Case 3: Single class name like "fa-home"
		if ( preg_match( '/^fa-[a-z0-9\-]+$/i', $input ) ) {
			return "fas {$input}";
		}

		// Case 4: Raw icon name like "home"
		if ( preg_match( '/^[a-z0-9\-]+$/i', $input ) ) {
			return "fas fa-{$input}";
		}

		throw new \Exception( esc_html( 'Invalid icon identifier: ' . $input ) );
	}

	/**
	 * Get an icon's details from icon ID
	 *
	 * @param string $id    icon ID e.g. fas fa-house
	 * @throws \Exception When the svg file does not exist.
	 * @return array
	 */
	public function get_icon_details( $id ) {
		$icon = array();

		$id = explode( ' ', $id );
		$dir = $this->get_icon_dir( $id[0] );
		$filename = $this->get_icon_filename( $id[1] );

		$icon['dir'] = $dir;
		$icon['filename'] = $filename;
		$icon['filepath'] = str_replace( '/', DIRECTORY_SEPARATOR, "$this->svg_dir/$dir/$filename.svg" );

		if ( ! is_file( $icon['filepath'] ) ) {
			// throw new \Exception( 'File ' . $icon['filepath'] . ' does not exist.' );
			$icon['dir'] = 'solid';
			$icon['filename'] = 'triangle-exclamation';
			$icon['filepath'] = str_replace( '/', DIRECTORY_SEPARATOR, "$this->svg_dir/solid/triangle-exclamation.svg" );
		}

		return $icon;
	}

	/**
	 * Get the directory that contains the SVG icon file
	 *
	 * @param string $style The font awesome style that determines which folder is searched.
	 * @return string
	 */
	public function get_icon_dir( $style ) {
		switch ( $style ) {
			case 'far':
				$dir = 'regular';
				break;

			case 'fal':
				$dir = 'light';
				break;

			case 'fab':
				$dir = 'brands';
				break;

			case 'fad':
				$dir = 'duotone';
				break;

			case 'fas':
			default:
				$dir = 'solid';
		}

		return $dir;
	}

	/**
	 * Get the icon's SVG file name
	 *
	 * @param string $icon_name The Font Awesome icon name.
	 * @return string
	 */
	public function get_icon_filename( $icon_name ) {
		return str_replace( 'fa-', '', $icon_name );
	}
}
