<?php
/**
 * Shortcodes for Local SEO: address, map, opening hours, all locations, logo.
 *
 * @package SEOvaultSEO\LocalSEO
 */

namespace SEOvaultSEO\LocalSEO;

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

class Shortcodes {

	public static function init() {
		add_shortcode( 'seovault_address', array( __CLASS__, 'shortcode_address' ) );
		add_shortcode( 'seovault_locations', array( __CLASS__, 'shortcode_locations' ) );
		add_shortcode( 'seovault_map', array( __CLASS__, 'shortcode_map' ) );
		add_shortcode( 'seovault_opening_hours', array( __CLASS__, 'shortcode_opening_hours' ) );
		add_shortcode( 'seovault_logo', array( __CLASS__, 'shortcode_logo' ) );
		add_shortcode( 'seovault_store_locator', array( __CLASS__, 'shortcode_store_locator' ) );
		add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ) );
	}

	public static function enqueue_assets() {
		wp_register_style( 'seovault-local-seo', SEOVAULT_SEO_URL . 'assets/css/local-seo.css', array(), SEOVAULT_SEO_VERSION );

		if ( self::should_enqueue_assets() ) {
			wp_enqueue_style( 'seovault-local-seo' );
		}
	}

	/**
	 * Whether Local SEO CSS is needed for the current request.
	 *
	 * @return bool
	 */
	private static function should_enqueue_assets() {
		if ( is_singular( PostType::get_post_type() ) ) {
			return true;
		}

		$post = get_post();
		if ( ! $post instanceof \WP_Post ) {
			return false;
		}

		$tags = array(
			'seovault_address',
			'seovault_locations',
			'seovault_map',
			'seovault_opening_hours',
			'seovault_logo',
			'seovault_store_locator',
		);

		foreach ( $tags as $tag ) {
			if ( has_shortcode( $post->post_content, $tag ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Ensure Local SEO CSS is enqueued when a shortcode actually renders.
	 *
	 * @return void
	 */
	private static function ensure_style() {
		wp_enqueue_style( 'seovault-local-seo' );
	}

	protected static $address_defaults = array(
		'id'                 => 0,
		'show_state'         => false,
		'show_country'       => false,
		'show_phone'         => false,
		'show_phone_2'       => false,
		'show_fax'           => false,
		'show_email'         => false,
		'show_url'           => false,
		'show_opening_hours' => false,
		'hide_closed'        => false,
		'oneline'            => false,
		'hide_json_ld'       => false,
	);

	public static function shortcode_address( $atts ) {
		self::ensure_style();
		$atts = shortcode_atts( self::$address_defaults, $atts, 'seovault_address' );
		$atts = self::cast_bools( $atts );

		$post_id = absint( $atts['id'] );
		if ( ! $post_id ) {
			$pt = PostType::get_post_type();
			if ( is_singular( $pt ) ) {
				$post_id = get_queried_object_id();
			} else {
				$post_id = LocationMeta::get_primary_location();
			}
		}

		if ( ! $post_id ) {
			return '';
		}

		$location = LocationMeta::get_location_data_object( $post_id );
		if ( ! $location ) {
			return '';
		}

		return self::render_address( $location, $atts );
	}

	public static function render_address( $location, $atts ) {
		$lines = array();

		if ( ! empty( $location->business_address ) ) {
			$lines[] = esc_html( $location->business_address );
		}
		if ( ! empty( $location->business_address_2 ) ) {
			$lines[] = esc_html( $location->business_address_2 );
		}

		$city_state_zip = array();
		if ( ! empty( $location->business_zipcode ) ) {
			$city_state_zip[] = esc_html( $location->business_zipcode );
		}
		if ( ! empty( $location->business_city ) ) {
			$city_state_zip[] = esc_html( $location->business_city );
		}
		if ( $atts['show_state'] && ! empty( $location->business_state ) ) {
			$city_state_zip[] = esc_html( $location->business_state );
		}
		if ( $atts['show_country'] && ! empty( $location->business_country ) ) {
			$name             = Countries::get_name( $location->business_country );
			$city_state_zip[] = $name ? esc_html( $name ) : esc_html( $location->business_country );
		}
		if ( ! empty( $city_state_zip ) ) {
			$lines[] = implode( ', ', $city_state_zip );
		}

		if ( $atts['show_phone'] && ! empty( $location->business_phone ) ) {
			$lines[] = esc_html( $location->business_phone );
		}
		if ( $atts['show_phone_2'] && ! empty( $location->business_phone_2nd ) ) {
			$lines[] = esc_html( $location->business_phone_2nd );
		}
		if ( $atts['show_fax'] && ! empty( $location->business_fax ) ) {
			$lines[] = esc_html( $location->business_fax );
		}
		if ( $atts['show_email'] && ! empty( $location->business_email ) ) {
			$lines[] = '<a href="mailto:' . esc_attr( $location->business_email ) . '">' . esc_html( $location->business_email ) . '</a>';
		}
		if ( $atts['show_url'] && ! empty( $location->business_url ) ) {
			$lines[] = '<a href="' . esc_url( $location->business_url ) . '">' . esc_html( $location->business_url ) . '</a>';
		}

		$separator = $atts['oneline'] ? ', ' : '<br>';
		$html      = '<div class="seovault-address">' . implode( $separator, $lines ) . '</div>';

		if ( $atts['show_opening_hours'] ) {
			$oh    = new OpeningHours( $location->post_id );
			$html .= $oh->render_html( $atts['hide_closed'] );
		}

		if ( ! $atts['hide_json_ld'] ) {
			$sg     = new SchemaGenerator();
			$schema = $sg->get_schema_for_location( $location->post_id );
			if ( ! empty( $schema ) ) {
				$html .= '<script type="application/ld+json">' . wp_json_encode( $schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ) . '</script>';
			}
		}

		return $html;
	}

	public static function shortcode_locations( $atts ) {
		self::ensure_style();
		$atts = shortcode_atts(
			array(
				'show_state'         => false,
				'show_country'       => false,
				'show_phone'         => false,
				'show_phone_2'       => false,
				'show_fax'           => false,
				'show_email'         => false,
				'show_url'           => false,
				'show_opening_hours' => false,
				'hide_closed'        => false,
				'oneline'            => false,
				'hide_json_ld'       => true,
				'limit'              => -1,
				'orderby'            => 'date',
				'order'              => 'ASC',
			),
			$atts,
			'seovault_locations'
		);
		$atts = self::cast_bools( $atts );

		$pt    = PostType::get_post_type();
		$query = new \WP_Query(
			array(
				'post_type'      => $pt,
				'post_status'    => 'publish',
				'posts_per_page' => absint( $atts['limit'] ),
				'orderby'        => sanitize_text_field( $atts['orderby'] ),
				'order'          => ( 'DESC' === strtoupper( $atts['order'] ) ) ? 'DESC' : 'ASC',
			)
		);

		if ( ! $query->have_posts() ) {
			return '<p>' . esc_html__( 'No locations found.', 'seovault-ai' ) . '</p>';
		}

		$output = '<div class="seovault-locations-list">';
		while ( $query->have_posts() ) {
			$query->the_post();
			$post_id  = get_the_ID();
			$location = LocationMeta::get_location_data_object( $post_id );
			if ( $location ) {
				$output .= '<div class="seovault-location-item">';
				$output .= '<h3><a href="' . esc_url( get_permalink( $post_id ) ) . '">' . esc_html( get_the_title( $post_id ) ) . '</a></h3>';
				$output .= self::render_address( $location, $atts );
				$output .= '</div>';
			}
		}
		wp_reset_postdata();
		$output .= '</div>';

		return $output;
	}

	public static function shortcode_map( $atts ) {
		self::ensure_style();
		$atts = shortcode_atts(
			array(
				'id'                   => 0,
				'width'                => '100%',
				'height'               => '300',
				'zoom'                 => -1,
				'map_style'            => 'ROADMAP',
				'scrollable'           => true,
				'draggable'            => true,
				'show_route'           => false,
				'marker_clustering'    => false,
				'show_category_filter' => false,
			),
			$atts,
			'seovault_map'
		);
		$atts = self::cast_bools( $atts );

		$api_key = self::get_maps_api_key();
		if ( '' === $api_key ) {
			return '<div class="seovault-map-error">' . esc_html__( 'Google Maps API key is not configured.', 'seovault-ai' ) . '</div>';
		}

		$location_ids = array();
		if ( 'all' === $atts['id'] ) {
			$pt           = PostType::get_post_type();
			$query        = new \WP_Query(
				array(
					'post_type'      => $pt,
					'post_status'    => 'publish',
					'posts_per_page' => -1,
					'fields'         => 'ids',
					// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Map shortcode needs all geocoded locations.
					'meta_key'       => LocationMeta::META_PREFIX . 'coordinates_lat',
					'meta_compare'   => 'EXISTS',
				)
			);
			$location_ids = $query->posts;
		} else {
			$post_id = absint( $atts['id'] );
			if ( ! $post_id ) {
				$pt = PostType::get_post_type();
				if ( is_singular( $pt ) ) {
					$post_id = get_queried_object_id();
				} else {
					$post_id = LocationMeta::get_primary_location();
				}
			}
			if ( $post_id ) {
				$location_ids = array( $post_id );
			}
		}

		if ( empty( $location_ids ) ) {
			return '';
		}

		$markers = array();
		foreach ( $location_ids as $pid ) {
			$lat = LocationMeta::get( $pid, 'coordinates_lat' );
			$lng = LocationMeta::get( $pid, 'coordinates_long' );
			if ( '' !== $lat && '' !== $lng ) {
				$markers[] = array(
					'id'    => $pid,
					'lat'   => (float) $lat,
					'lng'   => (float) $lng,
					'title' => get_the_title( $pid ),
					'url'   => get_permalink( $pid ),
				);
			}
		}

		if ( empty( $markers ) ) {
			return '';
		}

		$map_id = 'seovault-map-' . wp_rand( 1000, 9999 );

		$maps_url = add_query_arg(
			array(
				'key' => $api_key,
				'v'   => 'weekly',
			),
			'https://maps.googleapis.com/maps/api/js'
		);
		wp_enqueue_script( 'seovault-google-maps', esc_url_raw( $maps_url ), array(), defined( 'SEOVAULT_SEO_VERSION' ) ? SEOVAULT_SEO_VERSION : '1.0', true );

		$config = array(
			'mapId'      => $map_id,
			'zoom'       => (int) $atts['zoom'],
			'mapStyle'   => $atts['map_style'],
			'scrollable' => (bool) $atts['scrollable'],
			'draggable'  => (bool) $atts['draggable'],
			'markers'    => $markers,
		);

		wp_enqueue_script( 'seovault-local-map', SEOVAULT_SEO_URL . 'assets/js/local-map.js', array( 'seovault-google-maps' ), SEOVAULT_SEO_VERSION, true );
		wp_add_inline_script(
			'seovault-local-map',
			'window.seovaultseoLocalMaps = window.seovaultseoLocalMaps || []; window.seovaultseoLocalMaps.push(' . wp_json_encode( $config, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ) . ');',
			'before'
		);

		$html = '<div id="' . esc_attr( $map_id ) . '" class="seovault-map" style="width:' . esc_attr( $atts['width'] ) . ';height:' . esc_attr( $atts['height'] ) . 'px;"></div>';

		return $html;
	}

	public static function shortcode_opening_hours( $atts ) {
		self::ensure_style();
		$atts = shortcode_atts(
			array(
				'id'          => 0,
				'hide_closed' => false,
			),
			$atts,
			'seovault_opening_hours'
		);
		$atts = self::cast_bools( $atts );

		$post_id = absint( $atts['id'] );
		if ( ! $post_id ) {
			$pt = PostType::get_post_type();
			if ( is_singular( $pt ) ) {
				$post_id = get_queried_object_id();
			} else {
				$post_id = LocationMeta::get_primary_location();
			}
		}

		if ( ! $post_id ) {
			return '';
		}

		$oh = new OpeningHours( $post_id );
		return $oh->render_html( $atts['hide_closed'] );
	}

	public static function shortcode_logo( $atts ) {
		self::ensure_style();
		$atts = shortcode_atts(
			array(
				'id'     => 0,
				'width'  => '',
				'height' => '',
			),
			$atts,
			'seovault_logo'
		);

		$post_id = absint( $atts['id'] );
		if ( ! $post_id ) {
			$pt = PostType::get_post_type();
			if ( is_singular( $pt ) ) {
				$post_id = get_queried_object_id();
			} else {
				$post_id = LocationMeta::get_primary_location();
			}
		}

		if ( ! $post_id ) {
			return '';
		}

		$logo_id = absint( LocationMeta::get( $post_id, 'business_logo_id' ) );
		if ( ! $logo_id ) {
			return '';
		}

		$size = 'full';
		if ( $atts['width'] || $atts['height'] ) {
			$size = array( (int) $atts['width'] ?: 9999, (int) $atts['height'] ?: 9999 );
		}

		return wp_get_attachment_image( $logo_id, $size, false, array( 'class' => 'seovault-location-logo' ) );
	}

	public static function shortcode_store_locator( $atts ) {
		self::ensure_style();
		return StoreLocator::render( $atts );
	}

	public static function get_maps_api_key() {
		$options = get_option( 'seovaultseo_local_settings', array() );
		if ( ! is_array( $options ) ) {
			return '';
		}
		return $options['maps_api_key'] ?? '';
	}

	protected static function cast_bools( $atts ) {
		foreach ( $atts as $key => $value ) {
			if ( is_bool( $value ) ) {
				continue;
			}
			if ( 'true' === $value || '1' === $value || 'yes' === $value || 'on' === $value ) {
				$atts[ $key ] = true;
			} elseif ( 'false' === $value || '0' === $value || 'no' === $value || '' === $value ) {
				$atts[ $key ] = false;
			}
		}
		return $atts;
	}
}
