<?php

namespace Gigneo;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Class Taxonomies
 *
 * Plugin taxonomies class for Gigneo.
 *
 * @since 0.0.1
 */
class Taxonomies {

	/**
	 * The single instance of the class.
	 *
	 * @var Taxonomies|null
	 * @since 0.0.1
	 */
	private static $instance = null;

	/**
	 * Ensures only one instance of the class is loaded or can be loaded.
	 *
	 * @return Taxonomies
	 * @since 0.0.1
	 */
	public static function instance() {
		if ( is_null( self::$instance ) ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	/**
	 * Constructor for the class.
	 *
	 * @since 0.0.1
	 */
	public function __construct() {
		//Register taxonomies
		add_action( 'init', array( $this, 'register_taxonomies' ) );

		//Limit depth
		add_filter( 'wp_insert_term_data', array( $this, 'limit_depth_before_insert' ), 10, 3 );

		//Update admin post-type urls when slugs change
		add_action( 'admin_init', array( $this, 'update_admin_taxonomy_urls' ) );

		//Update edit links when slugs change
		add_filter( 'get_edit_term_link', array( $this, 'update_taxonomy_edit_link' ), 10, 4 );

		//Add Country/State column to the Cities taxonomy admin table
		add_filter( 'manage_edit-gigneo_city_columns', array( $this, 'add_city_country_state_column' ) );
		add_filter( 'manage_gigneo_city_custom_column', array( $this, 'render_city_country_state_column' ), 10, 3 );
	}

	/**
	 * Registers all taxonomies for the plugin.
	 *
	 * @since 0.0.1
	 *
	 * @return void
	 */
	public function register_taxonomies() {

		//Facet
		$this->register_facet_taxonomy();

		//Skill
		$this->register_skill_taxonomy();

		//City
		$this->register_city_taxonomy();

		//Licenses
		$this->register_license_taxonomy();

		//Language
		$this->register_language_taxonomy();
	}

	/**
	 * Registers the 'facet' taxonomy for freelancers and employers.
	 *
	 * This function defines and registers the 'facet' taxonomy with WordPress.
	 * It applies filters for taxonomy labels and arguments, allowing customization.
	 *
	 * @since 0.0.1
	 *
	 * @return void
	 */
	public function register_facet_taxonomy() {

		$taxonomy_slug = self::taxonomy_slug( 'gigneo_facet' );

		$labels = array(
			'name'          => esc_html__( 'Categories', 'gigneo' ),
			'singular_name' => esc_html__( 'Category', 'gigneo' ),
			'menu_name'     => esc_html__( 'Categories', 'gigneo' ),
			'all_items'     => esc_html__( 'All', 'gigneo' ),
			'edit_item'     => esc_html__( 'Edit', 'gigneo' ),
			'view_item'     => esc_html__( 'View', 'gigneo' ),
			'update_item'   => esc_html__( 'Update', 'gigneo' ),
			'add_new_item'  => esc_html__( 'Add new', 'gigneo' ),
			'new_item_name' => esc_html__( 'New', 'gigneo' ),
			'search_items'  => esc_html__( 'Search', 'gigneo' ),
			'not_found'     => esc_html__( 'Not found', 'gigneo' ),
		);

		$taxonomy_args = apply_filters(
			'gigneo_taxonomy_facet_args',
			array(
				'hierarchical'       => true,
				'labels'             => $labels,
				'show_ui'            => true,
				'show_admin_column'  => true,
				'query_var'          => true,
				'rewrite'            => array( 'slug' => $taxonomy_slug ),
				'publicly_queryable' => false,
			)
		);

		register_taxonomy( 'gigneo_facet', array( 'gigneo_service', 'gigneo_job' ), $taxonomy_args );
	}

	/**
	 * Registers the 'tag' taxonomy for freelancers and employers.
	 *
	 * This function defines and registers the 'tag' taxonomy with WordPress.
	 * It applies filters for taxonomy labels and arguments, allowing customization.
	 *
	 * @since 0.0.1
	 *
	 * @return void
	 */
	public function register_skill_taxonomy() {

		$taxonomy_slug = self::taxonomy_slug( 'gigneo_skill' );

		$labels = array(
			'name'          => esc_html__( 'Skills', 'gigneo' ),
			'singular_name' => esc_html__( 'Skill', 'gigneo' ),
			'menu_name'     => esc_html__( 'Skills', 'gigneo' ),
			'all_items'     => esc_html__( 'All', 'gigneo' ),
			'edit_item'     => esc_html__( 'Edit', 'gigneo' ),
			'view_item'     => esc_html__( 'View', 'gigneo' ),
			'update_item'   => esc_html__( 'Update', 'gigneo' ),
			'add_new_item'  => esc_html__( 'Add new', 'gigneo' ),
			'new_item_name' => esc_html__( 'New', 'gigneo' ),
			'search_items'  => esc_html__( 'Search', 'gigneo' ),
			'not_found'     => esc_html__( 'Not found', 'gigneo' ),
		);

		$taxonomy_args = apply_filters(
			'gigneo_taxonomy_skill_args',
			array(
				'hierarchical'       => false,
				'labels'             => $labels,
				'show_ui'            => true,
				'show_admin_column'  => true,
				'query_var'          => true,
				'rewrite'            => array( 'slug' => $taxonomy_slug ),
				'publicly_queryable' => false,
			)
		);

		register_taxonomy( 'gigneo_skill', array( 'gigneo_service', 'gigneo_job' ), $taxonomy_args );
	}

	/**
	 * Registers the 'city' taxonomy for freelancers and employers.
	 *
	 * This function defines and registers the 'city' taxonomy with WordPress.
	 * It applies filters for taxonomy labels and arguments, allowing customization.
	 *
	 * @since 0.0.1
	 *
	 * @return void
	 */
	public function register_city_taxonomy() {

		$cities_option = gigneo_option( '_gigneo_ts_disable_cities', false );

		if ( $cities_option ) {
			return;
		}

		$taxonomy_slug = self::taxonomy_slug( 'gigneo_city' );

		$labels = array(
			'name'          => esc_html__( 'Cities', 'gigneo' ),
			'singular_name' => esc_html__( 'City', 'gigneo' ),
			'menu_name'     => esc_html__( 'Cities', 'gigneo' ),
			'all_items'     => esc_html__( 'All', 'gigneo' ),
			'edit_item'     => esc_html__( 'Edit', 'gigneo' ),
			'view_item'     => esc_html__( 'View', 'gigneo' ),
			'update_item'   => esc_html__( 'Update', 'gigneo' ),
			'add_new_item'  => esc_html__( 'Add new', 'gigneo' ),
			'new_item_name' => esc_html__( 'New', 'gigneo' ),
			'search_items'  => esc_html__( 'Search', 'gigneo' ),
			'not_found'     => esc_html__( 'Not found', 'gigneo' ),
		);

		$taxonomy_args = apply_filters(
			'gigneo_taxonomy_city_args',
			array(
				'hierarchical'       => false,
				'labels'             => $labels,
				'show_ui'            => true,
				'show_admin_column'  => true,
				'query_var'          => true,
				'rewrite'            => array( 'slug' => $taxonomy_slug ),
				'publicly_queryable' => false,
			)
		);

		register_taxonomy( 'gigneo_city', array(), $taxonomy_args );
	}

	/**
	 * Registers the 'language' taxonomy for freelancers and employers.
	 *
	 * This function defines and registers the 'language' taxonomy with WordPress.
	 * It applies filters for taxonomy labels and arguments, allowing customization.
	 *
	 * @since 0.0.1
	 *
	 * @return void
	 */
	public function register_language_taxonomy() {

		$taxonomy_slug = self::taxonomy_slug( 'gigneo_language' );

		$labels = array(
			'name'          => esc_html__( 'Languages', 'gigneo' ),
			'singular_name' => esc_html__( 'Language', 'gigneo' ),
			'menu_name'     => esc_html__( 'Languages', 'gigneo' ),
			'all_items'     => esc_html__( 'All', 'gigneo' ),
			'edit_item'     => esc_html__( 'Edit', 'gigneo' ),
			'view_item'     => esc_html__( 'View', 'gigneo' ),
			'update_item'   => esc_html__( 'Update', 'gigneo' ),
			'add_new_item'  => esc_html__( 'Add new', 'gigneo' ),
			'new_item_name' => esc_html__( 'New', 'gigneo' ),
			'search_items'  => esc_html__( 'Search', 'gigneo' ),
			'not_found'     => esc_html__( 'Not found', 'gigneo' ),
		);

		$taxonomy_args = apply_filters(
			'gigneo_taxonomy_language_args',
			array(
				'hierarchical'       => false,
				'labels'             => $labels,
				'show_ui'            => true,
				'show_admin_column'  => true,
				'query_var'          => true,
				'rewrite'            => array( 'slug' => $taxonomy_slug ),
				'publicly_queryable' => false,
			)
		);

		register_taxonomy( 'gigneo_language', array( 'gigneo_freelancer', 'gigneo_employer' ), $taxonomy_args );
	}

	/**
	 * Registers the 'license' taxonomy for freelancers and employers.
	 *
	 * This function defines and registers the 'license' taxonomy with WordPress.
	 * It applies filters for taxonomy labels and arguments, allowing customization.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function register_license_taxonomy() {

		$taxonomy_slug = self::taxonomy_slug( 'gigneo_license' );

		$labels = array(
			'name'          => esc_html__( 'Licenses', 'gigneo' ),
			'singular_name' => esc_html__( 'License', 'gigneo' ),
			'menu_name'     => esc_html__( 'Licenses', 'gigneo' ),
			'all_items'     => esc_html__( 'All', 'gigneo' ),
			'edit_item'     => esc_html__( 'Edit', 'gigneo' ),
			'view_item'     => esc_html__( 'View', 'gigneo' ),
			'update_item'   => esc_html__( 'Update', 'gigneo' ),
			'add_new_item'  => esc_html__( 'Add new', 'gigneo' ),
			'new_item_name' => esc_html__( 'New', 'gigneo' ),
			'search_items'  => esc_html__( 'Search', 'gigneo' ),
			'not_found'     => esc_html__( 'Not found', 'gigneo' ),
		);

		$taxonomy_args = apply_filters(
			'gigneo_taxonomy_license_args',
			array(
				'hierarchical'       => false,
				'labels'             => $labels,
				'show_ui'            => true,
				'show_admin_column'  => true,
				'query_var'          => true,
				'rewrite'            => array( 'slug' => $taxonomy_slug ),
				'publicly_queryable' => true,
			)
		);

		register_taxonomy( 'gigneo_license', array( 'gigneo_freelancer', 'gigneo_employer' ), $taxonomy_args );
	}

	/**
	 * Retrieves the slug for a given taxonomy.
	 *
	 * This function allows filtering of taxonomy slugs through the
	 * 'gigneo_taxonomy_slugs' and "gigneo_taxonomy_{$taxonomy}_slug" filters.
	 *
	 * @since 1.0.0
	 *
	 * @param string $taxonomy The taxonomy key.
	 * @return string|array The slug of the taxonomy.
	 */
	public static function taxonomy_slug( $taxonomy = '' ) {

		$skill    = gigneo_option( '_gigneo_ts_taxonomy_skill_slug', 'skill' );
		$category = gigneo_option( '_gigneo_ts_taxonomy_category_slug', 'facet' );
		$city     = gigneo_option( '_gigneo_ts_taxonomy_city_slug', 'city' );
		$language = gigneo_option( '_gigneo_ts_taxonomy_language_slug', 'language' );
		$license  = gigneo_option( '_gigneo_ts_taxonomy_license_slug', 'license' );

		$slugs = array(
			'gigneo_skill'    => $skill,
			'gigneo_facet'    => $category,
			'gigneo_city'     => $city,
			'gigneo_language' => $language,
			'gigneo_license'  => $license,
		);

		$slugs = apply_filters( 'gigneo_taxonomy_slugs', $slugs );

		if ( ! empty( $taxonomy ) ) {
			return $slugs[ $taxonomy ] ?? '';
		}

		return $slugs;
	}

	/**
	 * Add the Country / State column to the Cities taxonomy admin table.
	 *
	 * @param array $columns Existing table columns.
	 * @return array Modified table columns.
	 * @since 0.0.1
	 */

	public function add_city_country_state_column( $columns ) {
		// Insert the new column after the 'description' column.
		$new_columns = array();
		foreach ( $columns as $key => $label ) {
			$new_columns[ $key ] = $label;
			if ( 'description' === $key ) {
				$new_columns['gigneo_country_state'] = esc_html__( 'Country / State', 'gigneo' );
			}
		}

		// Fallback: if 'description' column wasn't found, just append.
		if ( ! isset( $new_columns['gigneo_country_state'] ) ) {
			$new_columns['gigneo_country_state'] = esc_html__( 'Country / State', 'gigneo' );
		}

		return $new_columns;
	}

	/**
	 * Render the Country / State column value for each city term row.
	 *
	 * @param string $content     Current cell content.
	 * @param string $column_name The column name being rendered.
	 * @param int    $term_id     The current term ID.
	 * @return string The cell content.
	 * @since 0.0.1
	 */

	public function render_city_country_state_column( $content, $column_name, $term_id ) {
		if ( 'gigneo_country_state' !== $column_name ) {
			return $content;
		}

		$country_state_value = get_term_meta( $term_id, '_gigneo_tm_country_state', true );

		if ( empty( $country_state_value ) || 'none' === $country_state_value ) {
			return '&mdash;';
		}

		// Resolve the stored key (e.g. "US:CA" or "AU") to its human-readable label.
		$countries_with_states = gigneo_countries_with_states( 'active', false, false );
		if ( isset( $countries_with_states[ $country_state_value ] ) ) {
			return esc_html( $countries_with_states[ $country_state_value ] );
		}

		// Fall back to the raw stored value if not found in the active list.
		return esc_html( $country_state_value );
	}

	/**
	 * Limits the depth of the 'gigneo_facet' taxonomy to 3 levels.
	 *
	 * This function checks the depth of the term being inserted and prevents
	 * saving if it exceeds 3 levels.
	 *
	 * @param array $data The term data.
	 * @param string $taxonomy The taxonomy key.
	 * @param array $args The arguments for the term.
	 *
	 * @return array The term data.
	 * @since 0.0.1
	 */
	public function limit_depth_before_insert( $data, $taxonomy, $args ) {
		if ( 'gigneo_facet' !== $taxonomy ) {
			return $data;
		}

		$parent_id = isset( $args['parent'] ) ? (int) $args['parent'] : 0;

		if ( $parent_id > 0 ) {
			$depth = 1;
			while ( $parent_id ) {
				$parent = get_term( $parent_id, $taxonomy );
				if ( ! $parent || is_wp_error( $parent ) ) {
					break;
				}
				++$depth;
				$parent_id = $parent->parent;

				if ( $depth > 3 ) {
					// Set an error and prevent saving
					wp_die(
						esc_html__( 'You cannot create a term deeper than 3 levels.', 'gigneo' ),
						esc_html__( 'Depth Limit Exceeded', 'gigneo' ),
						array( 'back_link' => true )
					);
				}
			}
		}

		return $data;
	}

	/**
	 * Update admin taxonomy URLs.
	 *
	 * This function updates the admin taxonomy URLs when the slugs change.
	 *
	 * @since 0.0.1
	 */
	public function update_admin_taxonomy_urls() {
		global $pagenow;

		// Get and sanitize the 'post_type' parameter
		$taxonomy = filter_input( INPUT_GET, 'taxonomy', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
		$taxonomy = ! empty( $taxonomy ) ? str_replace( 'gigneo_', '', $taxonomy ) : '';

		if ( is_admin() && 'edit-tags.php' === $pagenow && ! empty( $taxonomy ) ) {

			$taxonomies_slugs = self::taxonomy_slug();
			$tn               = array_search( $taxonomy, $taxonomies_slugs, true );
			if ( ! empty( $tn ) ) {
				$_GET['taxonomy']     = $tn;
				$_REQUEST['taxonomy'] = $tn;
			}
		}
	}

	/**
	 * Update the taxonomy edit link.
	 *
	 * This function updates the edit link for taxonomies when the slugs change.
	 *
	 * @param string $link The edit link.
	 * @param int $term_id The term ID.
	 * @param string $taxonomy The taxonomy key.
	 * @param string $context The context of the link.
	 *
	 * @return string The updated edit link.
	 * @since 0.0.1
	 */
	public function update_taxonomy_edit_link( $link, $term_id, $taxonomy, $context ) {
		$taxonomy_slug = self::taxonomy_slug( $taxonomy );
		if ( is_admin() && ! empty( $taxonomy_slug ) && 'post' === $context ) {
			$taxonomy_object      = get_taxonomy( $taxonomy );
			$taxonomy_object_type = $taxonomy_object->object_type[0] ?? '';
			if ( ! empty( $taxonomy_object_type ) ) {
				$link = str_replace( 'post_type=post', "post_type=$taxonomy_object_type", $link );
			}
			$link = str_replace( "taxonomy=$taxonomy", "taxonomy=gigneo_$taxonomy_slug", $link );
		}
		return $link;
	}
}
