<?php

namespace Limb_Chatbot\Includes\Services;

use Limb_Chatbot\Includes\Data_Objects\Wp_Object;
use Limb_Chatbot\Includes\Services\Data_Object_Collection;

/**
 * Service for fetching WordPress objects by their identifiers.
 *
 * Handles parsing and fetching objects based on various identifier formats:
 * - page:front (front page)
 * - page:home (posts page)
 * - singular:{post_type}:{id} (specific post/page)
 * - singular:{post_type} (generic post type)
 * - term:{taxonomy}:{id} (specific term)
 * - term:{taxonomy} (generic taxonomy)
 * - archive:{post_type} (post type archive)
 *
 * @package Limb_Chatbot\Includes\Services
 * @since 1.0.0
 */
class Wp_Object_By_Identifier_Service {

	/**
	 * Fetch multiple objects by their identifiers.
	 *
	 * @param array $identifiers Array of identifier strings.
	 *
	 * @return Data_Object_Collection Collection of Wp_Object instances.
	 */
	public function get_objects_by_identifiers( array $identifiers ): Data_Object_Collection {
		$objects = [];

		foreach ( $identifiers as $identifier ) {
			$object = $this->fetch_object_by_identifier( $identifier );
			if ( $object ) {
				$objects[] = $object;
			}
		}

		return new Data_Object_Collection( $objects );
	}

	/**
	 * Fetch a single object by its identifier.
	 *
	 * @param string $identifier The identifier string.
	 *
	 * @return Wp_Object|null The object or null if not found.
	 */
	private function fetch_object_by_identifier( string $identifier ): ?Wp_Object {
		$identifier = sanitize_text_field( $identifier );
		$parts      = explode( ':', $identifier );

		if ( empty( $parts ) ) {
			return null;
		}

		$type = $parts[0];

		switch ( $type ) {
			case 'page':
				return $this->fetch_special_page( $parts );

			case 'singular':
				return $this->fetch_singular_object( $parts );

			case 'term':
				return $this->fetch_term_object( $parts );

			case 'archive':
				return $this->fetch_archive_object( $parts );

			default:
				return null;
		}
	}

	/**
	 * Fetch special page objects (front, home).
	 *
	 * @param array $parts Identifier parts.
	 *
	 * @return Wp_Object|null
	 */
	private function fetch_special_page( array $parts ): ?Wp_Object {
		if ( count( $parts ) < 2 ) {
			return null;
		}

		$subtype       = $parts[1];
		$show_on_front = get_option( 'show_on_front', 'posts' );
		$front_page_id = (int) get_option( 'page_on_front' );
		$blog_page_id  = (int) get_option( 'page_for_posts' );

		if ( $subtype === 'front' ) {
			if ( $show_on_front === 'posts' ) {
				return Wp_Object::make( [
					'id'          => null,
					'title'       => __( 'Homepage - Latest posts', 'limb-chatbot' ),
					'link'        => home_url( '/' ),
					'identifier'  => 'page:front',
					'object_type' => 'special_page',
				] );
			} else {
				$page_title = $front_page_id ? get_the_title( $front_page_id ) : __( '(no page selected)', 'limb-chatbot' );

				return Wp_Object::make( [
					'id'          => $front_page_id ?: null,
					'title'       => sprintf( __( 'Homepage - %s', 'limb-chatbot' ), $page_title ),
					'link'        => home_url( '/' ),
					'identifier'  => 'page:front',
					'object_type' => 'special_page',
				] );
			}
		}

		if ( $subtype === 'home' ) {
			$page_title = $blog_page_id ? get_the_title( $blog_page_id ) : __( '(no page selected)', 'limb-chatbot' );

			return Wp_Object::make( [
				'id'          => $blog_page_id ?: null,
				'title'       => sprintf( __( 'Posts Page - %s', 'limb-chatbot' ), $page_title ),
				'link'        => get_post_type_archive_link( 'post' ),
				'identifier'  => 'page:home',
				'object_type' => 'special_page',
			] );
		}

		return null;
	}

	/**
	 * Fetch singular post/page objects.
	 *
	 * @param array $parts Identifier parts.
	 *
	 * @return Wp_Object|null
	 */
	private function fetch_singular_object( array $parts ): ?Wp_Object {
		if ( count( $parts ) < 2 ) {
			return null;
		}

		$post_type = $parts[1];

		// Check if it's a specific post or generic post type
		if ( count( $parts ) === 3 ) {
			// Specific post: singular:post:123
			$post_id = (int) $parts[2];

			return $this->fetch_post_by_id( $post_id, $post_type );
		} else {
			// Generic post type: singular:post
			$post_type_obj = get_post_type_object( $post_type );
			if ( ! $post_type_obj ) {
				return null;
			}

			return Wp_Object::make( [
				'id'          => null,
				'title'       => sprintf( __( 'All %s', 'limb-chatbot' ), $post_type_obj->labels->name ),
				'link'        => null,
				'identifier'  => "singular:{$post_type}",
				'object_type' => $post_type,
			] );
		}
	}

	/**
	 * Fetch a specific post by ID.
	 *
	 * @param int    $post_id   Post ID.
	 * @param string $post_type Post type.
	 *
	 * @return Wp_Object|null
	 */
	private function fetch_post_by_id( int $post_id, string $post_type ): ?Wp_Object {
		$post = get_post( $post_id );

		if ( ! $post || $post->post_status !== 'publish' || $post->post_type !== $post_type ) {
			return null;
		}

		return Wp_Object::make( [
			'id'          => $post->ID,
			'title'       => ! empty( $post->post_title ) ? $post->post_title : __( '(no title)', 'limb-chatbot' ),
			'link'        => get_permalink( $post->ID ),
			'identifier'  => "singular:{$post_type}:{$post->ID}",
			'object_type' => $post_type,
		] );
	}

	/**
	 * Fetch term objects.
	 *
	 * @param array $parts Identifier parts.
	 *
	 * @return Wp_Object|null
	 */
	private function fetch_term_object( array $parts ): ?Wp_Object {
		if ( count( $parts ) < 2 ) {
			return null;
		}

		$taxonomy = $parts[1];

		// Check if it's a specific term or generic taxonomy
		if ( count( $parts ) === 3 ) {
			// Specific term: term:category:5
			$term_id = (int) $parts[2];

			return $this->fetch_term_by_id( $term_id, $taxonomy );
		} else {
			// Generic taxonomy: term:category
			$taxonomy_obj = get_taxonomy( $taxonomy );
			if ( ! $taxonomy_obj ) {
				return null;
			}

			return Wp_Object::make( [
				'id'          => null,
				'title'       => sprintf( __( 'All %s', 'limb-chatbot' ), $taxonomy_obj->labels->name ),
				'link'        => null,
				'identifier'  => "term:{$taxonomy}",
				'object_type' => $taxonomy,
			] );
		}
	}

	/**
	 * Fetch a specific term by ID.
	 *
	 * @param int    $term_id  Term ID.
	 * @param string $taxonomy Taxonomy name.
	 *
	 * @return Wp_Object|null
	 */
	private function fetch_term_by_id( int $term_id, string $taxonomy ): ?Wp_Object {
		$term = get_term( $term_id, $taxonomy );

		if ( ! $term || is_wp_error( $term ) ) {
			return null;
		}

		return Wp_Object::make( [
			'id'          => $term->term_id,
			'title'       => $term->name,
			'link'        => get_term_link( $term ),
			'identifier'  => "term:{$taxonomy}:{$term->term_id}",
			'object_type' => $taxonomy,
		] );
	}

	/**
	 * Fetch archive objects.
	 *
	 * @param array $parts Identifier parts.
	 *
	 * @return Wp_Object|null
	 */
	private function fetch_archive_object( array $parts ): ?Wp_Object {
		if ( count( $parts ) < 2 ) {
			return null;
		}

		$post_type     = $parts[1];
		$post_type_obj = get_post_type_object( $post_type );

		if ( ! $post_type_obj || ! $post_type_obj->has_archive ) {
			return null;
		}

		return Wp_Object::make( [
			'id'          => null,
			'title'       => sprintf( __( '%s Archive', 'limb-chatbot' ), $post_type_obj->labels->name ),
			'link'        => get_post_type_archive_link( $post_type ),
			'identifier'  => "archive:{$post_type}",
			'object_type' => $post_type,
		] );
	}
}
