<?php
/**
 * Base variant engine for The Studio Player.
 *
 * @package The Studio Player
 */

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

if ( ! class_exists( 'THESPL_Variant_Engine' ) ) {
	/**
	 * Base variant engine.
	 */
	class THESPL_Variant_Engine {

		/**
		 * Get the number of editable variants in the current engine.
		 *
		 * @return int
		 */
		public function get_editor_variant_count() {
			return 2;
		}

		/**
		 * Get default variant labels for the current engine.
		 *
		 * @return array
		 */
		public function get_default_variant_labels() {
			$labels = array();
			$count  = $this->get_editor_variant_count();

			for ( $index = 0; $index < $count; $index++ ) {
				$labels[] = sprintf(
					/* translators: %d: Variant number. */
					__( 'Variant %d', 'the-studio-player' ),
					$index + 1
				);
			}

			return $labels;
		}

		/**
		 * Check whether raw playlist JSON contains more variants than the editor can modify.
		 *
		 * @param string $raw_json Raw playlist JSON.
		 * @return bool
		 */
		public function playlist_json_contains_extra_variants( $raw_json ) {
			$raw_json = is_string( $raw_json ) ? trim( $raw_json ) : '';

			if ( '' === $raw_json ) {
				return false;
			}

			$decoded = json_decode( $raw_json, true );
			if ( ! is_array( $decoded ) ) {
				return false;
			}

			$variant_limit = $this->get_editor_variant_count();

			if ( isset( $decoded['variants'] ) && is_array( $decoded['variants'] ) && count( $decoded['variants'] ) > $variant_limit ) {
				return true;
			}

			if ( ! isset( $decoded['tracks'] ) || ! is_array( $decoded['tracks'] ) ) {
				return false;
			}

			foreach ( $decoded['tracks'] as $track ) {
				if ( ! is_array( $track ) ) {
					continue;
				}

				if ( isset( $track['variants'] ) && is_array( $track['variants'] ) && count( $track['variants'] ) > $variant_limit ) {
					return true;
				}

				if ( isset( $track['variant_info'] ) && is_array( $track['variant_info'] ) && count( $track['variant_info'] ) > $variant_limit ) {
					return true;
				}
			}

			return false;
		}

		/**
		 * Normalize playlist payload for storage without truncating existing extra variant data.
		 *
		 * @param array $decoded Decoded playlist payload.
		 * @return array
		 */
		public function normalize_playlist_payload( $decoded ) {
			$decoded = is_array( $decoded ) ? $decoded : array();

			$fallback_variants = $this->get_storage_fallback_variants( $decoded );
			$tracks            = array();

			if ( isset( $decoded['tracks'] ) && is_array( $decoded['tracks'] ) ) {
				foreach ( $decoded['tracks'] as $track ) {
					if ( ! is_array( $track ) ) {
						continue;
					}

					$track_variants = $this->get_storage_track_variants( $track, $fallback_variants );
					$variant_info   = $this->get_storage_track_variant_info( $track, count( $track_variants ) );

					$audio_attachment_id = isset( $track['audio_attachment_id'] ) ? absint( $track['audio_attachment_id'] ) : 0;
					if ( $audio_attachment_id > 0 ) {
						$audio_file_path = get_attached_file( $audio_attachment_id );
						$audio_ext       = is_string( $audio_file_path ) ? strtolower( pathinfo( $audio_file_path, PATHINFO_EXTENSION ) ) : '';
						$audio_mime      = get_post_mime_type( $audio_attachment_id );
						$audio_mime      = is_string( $audio_mime ) ? strtolower( $audio_mime ) : '';
						$allowed_ext     = array( 'flac', 'wav' );
						$allowed_mime    = array( 'audio/flac', 'audio/x-flac', 'audio/wav', 'audio/x-wav', 'audio/wave', 'audio/vnd.wave' );
						if ( ! in_array( $audio_ext, $allowed_ext, true ) && ! in_array( $audio_mime, $allowed_mime, true ) ) {
							$audio_attachment_id = 0;
						}
					}

					$tracks[] = array(
						'title'               => isset( $track['title'] ) && is_string( $track['title'] ) ? sanitize_text_field( $track['title'] ) : '',
						'artist'              => isset( $track['artist'] ) && is_string( $track['artist'] ) ? sanitize_text_field( $track['artist'] ) : '',
						'credits'             => isset( $track['credits'] ) && is_string( $track['credits'] ) ? sanitize_text_field( $track['credits'] ) : '',
						'variants'            => $track_variants,
						'variant_info'        => $variant_info,
						'social'              => isset( $track['social'] ) && is_string( $track['social'] ) ? esc_url_raw( $track['social'] ) : '',
						'audio_attachment_id' => $audio_attachment_id,
						'cover_attachment_id' => isset( $track['cover_attachment_id'] ) ? absint( $track['cover_attachment_id'] ) : 0,
					);
				}
			}

			return array(
				'variants' => $fallback_variants,
				'tracks'   => $tracks,
			);
		}

		/**
		 * Get fallback variants for storage.
		 *
		 * @param array $decoded Decoded playlist payload.
		 * @return array
		 */
		public function get_storage_fallback_variants( $decoded ) {
			$decoded  = is_array( $decoded ) ? $decoded : array();
			$variants = array();

			if ( isset( $decoded['variants'] ) && is_array( $decoded['variants'] ) ) {
				foreach ( $decoded['variants'] as $index => $variant_label ) {
					$variants[] = $this->sanitize_variant_label( $variant_label, $index );
				}
			}

			if ( empty( $variants ) ) {
				$variants = $this->get_default_variant_labels();
			}

			return $variants;
		}

		/**
		 * Get fallback variants for editor and frontend display.
		 *
		 * @param array $decoded Decoded playlist payload.
		 * @return array
		 */
		public function get_display_fallback_variants( $decoded ) {
			$variants = $this->get_storage_fallback_variants( $decoded );
			$limit    = $this->get_editor_variant_count();

			if ( count( $variants ) > $limit ) {
				$variants = array_slice( $variants, 0, $limit );
			}

			if ( count( $variants ) < $limit ) {
				$defaults = $this->get_default_variant_labels();
				$variants = array_slice( array_pad( $variants, $limit, '' ), 0, $limit );

				foreach ( $variants as $index => $value ) {
					if ( '' === $value ) {
						$variants[ $index ] = isset( $defaults[ $index ] ) ? $defaults[ $index ] : $this->build_default_variant_label( $index );
					}
				}
			}

			return $variants;
		}

		/**
		 * Get track variants for storage.
		 *
		 * @param array $track             Track payload.
		 * @param array $fallback_variants Fallback variant labels.
		 * @return array
		 */
		public function get_storage_track_variants( $track, $fallback_variants ) {
			$track             = is_array( $track ) ? $track : array();
			$fallback_variants = is_array( $fallback_variants ) && ! empty( $fallback_variants ) ? array_values( $fallback_variants ) : $this->get_default_variant_labels();
			$track_variants    = array();

			if ( isset( $track['variants'] ) && is_array( $track['variants'] ) ) {
				foreach ( $track['variants'] as $index => $variant_label ) {
					$track_variants[] = $this->sanitize_variant_label(
						$variant_label,
						$index,
						isset( $fallback_variants[ $index ] ) ? $fallback_variants[ $index ] : null
					);
				}
			}

			if ( empty( $track_variants ) ) {
				$track_variants = $fallback_variants;
			}

			return $track_variants;
		}

		/**
		 * Get track variants for editor and frontend display.
		 *
		 * @param array $track             Track payload.
		 * @param array $fallback_variants Fallback variant labels.
		 * @return array
		 */
		public function get_display_track_variants( $track, $fallback_variants ) {
			$track_variants = $this->get_storage_track_variants( $track, $fallback_variants );
			$limit          = $this->get_editor_variant_count();

			if ( count( $track_variants ) > $limit ) {
				$track_variants = array_slice( $track_variants, 0, $limit );
			}

			if ( count( $track_variants ) < $limit ) {
				$track_variants = array_slice( array_pad( $track_variants, $limit, '' ), 0, $limit );

				foreach ( $track_variants as $index => $variant_label ) {
					if ( '' === $variant_label ) {
						$track_variants[ $index ] = isset( $fallback_variants[ $index ] ) ? $fallback_variants[ $index ] : $this->build_default_variant_label( $index );
					}
				}
			}

			return $track_variants;
		}

		/**
		 * Get track variant info for storage.
		 *
		 * @param array $track         Track payload.
		 * @param int   $variant_count Number of variant slots to align with.
		 * @return array
		 */
		public function get_storage_track_variant_info( $track, $variant_count ) {
			$track         = is_array( $track ) ? $track : array();
			$variant_count = max( 0, (int) $variant_count );
			$variant_info  = array();

			if ( isset( $track['variant_info'] ) && is_array( $track['variant_info'] ) ) {
				foreach ( $track['variant_info'] as $info ) {
					$variant_info[] = is_string( $info ) ? sanitize_text_field( $info ) : '';
				}
			}

			if ( count( $variant_info ) < $variant_count ) {
				$variant_info = array_slice( array_pad( $variant_info, $variant_count, '' ), 0, $variant_count );
			}

			return $variant_info;
		}

		/**
		 * Get track variant info for editor and frontend display.
		 *
		 * @param array $track         Track payload.
		 * @param int   $variant_count Number of displayed variant slots.
		 * @return array
		 */
		public function get_display_track_variant_info( $track, $variant_count ) {
			$variant_count = max( 0, (int) $variant_count );
			$variant_info  = $this->get_storage_track_variant_info( $track, $variant_count );

			if ( count( $variant_info ) > $variant_count ) {
				$variant_info = array_slice( $variant_info, 0, $variant_count );
			}

			return $variant_info;
		}

		/**
		 * Build a default variant label for an index.
		 *
		 * @param int $index Variant index.
		 * @return string
		 */
		protected function build_default_variant_label( $index ) {
			return sprintf(
				/* translators: %d: Variant number. */
				__( 'Variant %d', 'the-studio-player' ),
				(int) $index + 1
			);
		}

		/**
		 * Sanitize a variant label and provide a fallback when empty.
		 *
		 * @param mixed       $variant_label   Variant label to sanitize.
		 * @param int         $index           Variant index.
		 * @param string|null $fallback_label  Optional fallback label.
		 * @return string
		 */
		protected function sanitize_variant_label( $variant_label, $index, $fallback_label = null ) {
			$variant_label = is_string( $variant_label ) ? sanitize_text_field( $variant_label ) : '';

			if ( '' !== $variant_label ) {
				return $variant_label;
			}

			if ( is_string( $fallback_label ) && '' !== $fallback_label ) {
				return $fallback_label;
			}

			return $this->build_default_variant_label( $index );
		}
	}
}
