<?php
/**
 * Project content model and admin UI.
 *
 * @package GustavoPortfolioCore
 */

defined( 'ABSPATH' ) || exit;

/**
 * Registers the portfolio project post type, taxonomies and metadata.
 */
final class GSO_Projects {
	const POST_TYPE = 'gso_project';
	const TAX_TYPE  = 'gso_project_type';
	const TAX_TECH  = 'gso_project_tech';

	const META_URL        = '_gso_project_url';
	const META_CLIENT     = '_gso_project_client';
	const META_YEAR       = '_gso_project_year';
	const META_FEATURED   = '_gso_project_featured';
	const META_LINK_LABEL = '_gso_project_link_label';
	const META_ACCENT     = '_gso_project_accent';

	/**
	 * Register hooks.
	 *
	 * @return void
	 */
	public static function init() {
		add_action( 'init', array( __CLASS__, 'register_content_types' ) );
		add_action( 'add_meta_boxes_' . self::POST_TYPE, array( __CLASS__, 'add_meta_boxes' ) );
		add_action( 'save_post_' . self::POST_TYPE, array( __CLASS__, 'save_meta_box' ) );

		add_filter( 'manage_' . self::POST_TYPE . '_posts_columns', array( __CLASS__, 'admin_columns' ) );
		add_action( 'manage_' . self::POST_TYPE . '_posts_custom_column', array( __CLASS__, 'render_admin_column' ), 10, 2 );
		add_filter( 'manage_edit-' . self::POST_TYPE . '_sortable_columns', array( __CLASS__, 'sortable_columns' ) );
		add_action( 'pre_get_posts', array( __CLASS__, 'apply_admin_sorting' ) );
		add_action( 'restrict_manage_posts', array( __CLASS__, 'taxonomy_filters' ) );
	}

	/**
	 * Register the post type, taxonomies and REST-aware metadata.
	 *
	 * @return void
	 */
	public static function register_content_types() {
		register_post_type(
			self::POST_TYPE,
			array(
				'labels'              => array(
					'name'                  => __( 'Projetos', 'gustavoo-portfolio-core' ),
					'singular_name'         => __( 'Projeto', 'gustavoo-portfolio-core' ),
					'menu_name'             => __( 'Portfólio', 'gustavoo-portfolio-core' ),
					'name_admin_bar'        => __( 'Projeto', 'gustavoo-portfolio-core' ),
					'add_new'               => __( 'Adicionar projeto', 'gustavoo-portfolio-core' ),
					'add_new_item'          => __( 'Adicionar novo projeto', 'gustavoo-portfolio-core' ),
					'edit_item'             => __( 'Editar projeto', 'gustavoo-portfolio-core' ),
					'new_item'              => __( 'Novo projeto', 'gustavoo-portfolio-core' ),
					'view_item'             => __( 'Ver projeto', 'gustavoo-portfolio-core' ),
					'search_items'          => __( 'Buscar projetos', 'gustavoo-portfolio-core' ),
					'not_found'             => __( 'Nenhum projeto encontrado.', 'gustavoo-portfolio-core' ),
					'not_found_in_trash'    => __( 'Nenhum projeto encontrado na lixeira.', 'gustavoo-portfolio-core' ),
					'all_items'             => __( 'Projetos', 'gustavoo-portfolio-core' ),
					'featured_image'        => __( 'Imagem do projeto', 'gustavoo-portfolio-core' ),
					'set_featured_image'    => __( 'Definir imagem do projeto', 'gustavoo-portfolio-core' ),
					'remove_featured_image' => __( 'Remover imagem do projeto', 'gustavoo-portfolio-core' ),
				),
				'public'              => false,
				'publicly_queryable'  => false,
				'exclude_from_search' => true,
				'show_ui'             => true,
				'show_in_menu'        => true,
				'show_in_admin_bar'   => true,
				'show_in_nav_menus'   => false,
				'show_in_rest'        => true,
				'menu_position'       => 25,
				'menu_icon'           => 'dashicons-portfolio',
				'capability_type'     => 'post',
				'map_meta_cap'        => true,
				'hierarchical'        => false,
				'has_archive'         => false,
				'rewrite'             => false,
				'query_var'           => false,
				'supports'            => array( 'title', 'editor', 'excerpt', 'thumbnail', 'page-attributes', 'revisions', 'custom-fields' ),
			)
		);

		register_taxonomy(
			self::TAX_TYPE,
			self::POST_TYPE,
			array(
				'labels'            => array(
					'name'          => __( 'Tipos de projeto', 'gustavoo-portfolio-core' ),
					'singular_name' => __( 'Tipo de projeto', 'gustavoo-portfolio-core' ),
					'menu_name'     => __( 'Tipos', 'gustavoo-portfolio-core' ),
					'all_items'     => __( 'Todos os tipos', 'gustavoo-portfolio-core' ),
					'edit_item'     => __( 'Editar tipo', 'gustavoo-portfolio-core' ),
					'add_new_item'  => __( 'Adicionar tipo', 'gustavoo-portfolio-core' ),
					'search_items'  => __( 'Buscar tipos', 'gustavoo-portfolio-core' ),
				),
				'public'            => false,
				'publicly_queryable' => false,
				'show_ui'           => true,
				'show_admin_column' => false,
				'show_in_rest'      => true,
				'hierarchical'      => true,
				'rewrite'           => false,
			)
		);

		register_taxonomy(
			self::TAX_TECH,
			self::POST_TYPE,
			array(
				'labels'            => array(
					'name'                       => __( 'Tecnologias', 'gustavoo-portfolio-core' ),
					'singular_name'              => __( 'Tecnologia', 'gustavoo-portfolio-core' ),
					'menu_name'                  => __( 'Tecnologias', 'gustavoo-portfolio-core' ),
					'all_items'                  => __( 'Todas as tecnologias', 'gustavoo-portfolio-core' ),
					'edit_item'                  => __( 'Editar tecnologia', 'gustavoo-portfolio-core' ),
					'add_new_item'               => __( 'Adicionar tecnologia', 'gustavoo-portfolio-core' ),
					'search_items'               => __( 'Buscar tecnologias', 'gustavoo-portfolio-core' ),
					'separate_items_with_commas' => __( 'Separe tecnologias com vírgulas', 'gustavoo-portfolio-core' ),
				),
				'public'            => false,
				'publicly_queryable' => false,
				'show_ui'           => true,
				'show_admin_column' => false,
				'show_in_rest'      => true,
				'hierarchical'      => false,
				'rewrite'           => false,
			)
		);

		self::register_meta();
	}

	/**
	 * Register project metadata.
	 *
	 * @return void
	 */
	private static function register_meta() {
		$common = array(
			'single'        => true,
			'show_in_rest'  => true,
			'auth_callback' => array( __CLASS__, 'authorize_meta' ),
		);

		register_post_meta(
			self::POST_TYPE,
			self::META_URL,
			array_merge(
				$common,
				array(
					'type'              => 'string',
					'sanitize_callback' => array( __CLASS__, 'sanitize_external_url' ),
				)
			)
		);

		foreach ( array( self::META_CLIENT, self::META_LINK_LABEL ) as $meta_key ) {
			register_post_meta(
				self::POST_TYPE,
				$meta_key,
				array_merge(
					$common,
					array(
						'type'              => 'string',
						'sanitize_callback' => 'sanitize_text_field',
					)
				)
			);
		}

		register_post_meta(
			self::POST_TYPE,
			self::META_YEAR,
			array_merge(
				$common,
				array(
					'type'              => 'integer',
					'sanitize_callback' => array( __CLASS__, 'sanitize_year' ),
				)
			)
		);

		register_post_meta(
			self::POST_TYPE,
			self::META_FEATURED,
			array_merge(
				$common,
				array(
					'type'              => 'boolean',
					'sanitize_callback' => 'rest_sanitize_boolean',
				)
			)
		);

		register_post_meta(
			self::POST_TYPE,
			self::META_ACCENT,
			array_merge(
				$common,
				array(
					'type'              => 'string',
					'sanitize_callback' => 'sanitize_hex_color',
				)
			)
		);
	}

	/**
	 * Restrict meta updates to users who can edit the project.
	 *
	 * @param bool   $allowed  Current authorization result.
	 * @param string $meta_key Meta key.
	 * @param int    $post_id  Project ID.
	 * @return bool
	 */
	public static function authorize_meta( $allowed, $meta_key, $post_id ) {
		unset( $allowed, $meta_key );

		return current_user_can( 'edit_post', (int) $post_id );
	}

	/**
	 * Allow only valid external HTTP(S) URLs.
	 *
	 * @param mixed $value Raw URL.
	 * @return string
	 */
	public static function sanitize_external_url( $value ) {
		$url = esc_url_raw( trim( (string) $value ), array( 'http', 'https' ) );

		return $url && wp_http_validate_url( $url ) ? $url : '';
	}

	/**
	 * Validate a four-digit project year.
	 *
	 * @param mixed $value Raw year.
	 * @return int
	 */
	public static function sanitize_year( $value ) {
		$year     = absint( $value );
		$max_year = (int) gmdate( 'Y' ) + 5;

		return $year >= 1900 && $year <= $max_year ? $year : 0;
	}

	/**
	 * Register the project details metabox.
	 *
	 * @return void
	 */
	public static function add_meta_boxes() {
		add_meta_box(
			'gso-project-details',
			__( 'Detalhes do projeto', 'gustavoo-portfolio-core' ),
			array( __CLASS__, 'render_meta_box' ),
			self::POST_TYPE,
			'normal',
			'high'
		);
	}

	/**
	 * Render project fields.
	 *
	 * @param WP_Post $post Current project.
	 * @return void
	 */
	public static function render_meta_box( $post ) {
		$url        = (string) get_post_meta( $post->ID, self::META_URL, true );
		$client     = (string) get_post_meta( $post->ID, self::META_CLIENT, true );
		$year       = absint( get_post_meta( $post->ID, self::META_YEAR, true ) );
		$featured   = (bool) get_post_meta( $post->ID, self::META_FEATURED, true );
		$link_label = (string) get_post_meta( $post->ID, self::META_LINK_LABEL, true );
		$accent     = sanitize_hex_color( get_post_meta( $post->ID, self::META_ACCENT, true ) );

		if ( ! $accent ) {
			$accent = '#7cf3da';
		}

		wp_nonce_field( 'gso_save_project_details', 'gso_project_details_nonce' );
		?>
		<table class="form-table" role="presentation">
			<tbody>
				<tr>
					<th scope="row"><label for="gso-project-url"><?php esc_html_e( 'URL externa', 'gustavoo-portfolio-core' ); ?></label></th>
					<td>
						<input class="regular-text" id="gso-project-url" name="gso_project_url" type="url" value="<?php echo esc_attr( $url ); ?>" inputmode="url" placeholder="https://">
						<p class="description"><?php esc_html_e( 'Aceita somente uma URL completa usando HTTP ou HTTPS.', 'gustavoo-portfolio-core' ); ?></p>
					</td>
				</tr>
				<tr>
					<th scope="row"><label for="gso-project-client"><?php esc_html_e( 'Cliente ou marca', 'gustavoo-portfolio-core' ); ?></label></th>
					<td><input class="regular-text" id="gso-project-client" name="gso_project_client" type="text" value="<?php echo esc_attr( $client ); ?>" maxlength="160"></td>
				</tr>
				<tr>
					<th scope="row"><label for="gso-project-year"><?php esc_html_e( 'Ano', 'gustavoo-portfolio-core' ); ?></label></th>
					<td><input class="small-text" id="gso-project-year" name="gso_project_year" type="number" value="<?php echo $year ? esc_attr( (string) $year ) : ''; ?>" min="1900" max="<?php echo esc_attr( (string) ( (int) gmdate( 'Y' ) + 5 ) ); ?>" step="1"></td>
				</tr>
				<tr>
					<th scope="row"><label for="gso-project-link-label"><?php esc_html_e( 'Texto do CTA', 'gustavoo-portfolio-core' ); ?></label></th>
					<td><input class="regular-text" id="gso-project-link-label" name="gso_project_link_label" type="text" value="<?php echo esc_attr( $link_label ); ?>" maxlength="80" placeholder="<?php esc_attr_e( 'Ver projeto', 'gustavoo-portfolio-core' ); ?>"></td>
				</tr>
				<tr>
					<th scope="row"><label for="gso-project-accent"><?php esc_html_e( 'Cor de acento', 'gustavoo-portfolio-core' ); ?></label></th>
					<td><input id="gso-project-accent" name="gso_project_accent" type="color" value="<?php echo esc_attr( $accent ); ?>"></td>
				</tr>
				<tr>
					<th scope="row"><?php esc_html_e( 'Destaque', 'gustavoo-portfolio-core' ); ?></th>
					<td>
						<label for="gso-project-featured">
							<input id="gso-project-featured" name="gso_project_featured" type="checkbox" value="1" <?php checked( $featured ); ?>>
							<?php esc_html_e( 'Exibir entre os projetos em destaque', 'gustavoo-portfolio-core' ); ?>
						</label>
					</td>
				</tr>
			</tbody>
		</table>
		<?php
	}

	/**
	 * Save project fields after nonce and capability checks.
	 *
	 * @param int $post_id Project ID.
	 * @return void
	 */
	public static function save_meta_box( $post_id ) {
		// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Verified immediately below.
		$nonce = isset( $_POST['gso_project_details_nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['gso_project_details_nonce'] ) ) : '';

		if ( ! $nonce || ! wp_verify_nonce( $nonce, 'gso_save_project_details' ) ) {
			return;
		}

		if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || wp_is_post_revision( $post_id ) ) {
			return;
		}

		if ( self::POST_TYPE !== get_post_type( $post_id ) || ! current_user_can( 'edit_post', $post_id ) ) {
			return;
		}

		// phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified above.
		$url        = isset( $_POST['gso_project_url'] ) ? self::sanitize_external_url( wp_unslash( $_POST['gso_project_url'] ) ) : '';
		$client     = isset( $_POST['gso_project_client'] ) ? sanitize_text_field( wp_unslash( $_POST['gso_project_client'] ) ) : '';
		$year       = isset( $_POST['gso_project_year'] ) ? self::sanitize_year( wp_unslash( $_POST['gso_project_year'] ) ) : 0;
		$link_label = isset( $_POST['gso_project_link_label'] ) ? sanitize_text_field( wp_unslash( $_POST['gso_project_link_label'] ) ) : '';
		$accent     = isset( $_POST['gso_project_accent'] ) ? sanitize_hex_color( wp_unslash( $_POST['gso_project_accent'] ) ) : '';
		$featured   = isset( $_POST['gso_project_featured'] ) ? '1' : '';
		// phpcs:enable WordPress.Security.NonceVerification.Missing

		self::update_or_delete_meta( $post_id, self::META_URL, $url );
		self::update_or_delete_meta( $post_id, self::META_CLIENT, $client );
		self::update_or_delete_meta( $post_id, self::META_YEAR, $year );
		self::update_or_delete_meta( $post_id, self::META_LINK_LABEL, $link_label );
		self::update_or_delete_meta( $post_id, self::META_ACCENT, $accent );
		self::update_or_delete_meta( $post_id, self::META_FEATURED, $featured );
	}

	/**
	 * Store a non-empty value, otherwise remove the metadata row.
	 *
	 * @param int    $post_id  Project ID.
	 * @param string $meta_key Metadata key.
	 * @param mixed  $value    Sanitized value.
	 * @return void
	 */
	private static function update_or_delete_meta( $post_id, $meta_key, $value ) {
		if ( '' === $value || 0 === $value || null === $value ) {
			delete_post_meta( $post_id, $meta_key );
			return;
		}

		update_post_meta( $post_id, $meta_key, $value );
	}

	/**
	 * Define project list columns.
	 *
	 * @param array<string, string> $columns Existing columns.
	 * @return array<string, string>
	 */
	public static function admin_columns( $columns ) {
		return array(
			'cb'                  => $columns['cb'] ?? '<input type="checkbox">',
			'gso_thumbnail'       => __( 'Imagem', 'gustavoo-portfolio-core' ),
			'title'               => __( 'Projeto', 'gustavoo-portfolio-core' ),
			'gso_client'          => __( 'Cliente', 'gustavoo-portfolio-core' ),
			'gso_type'            => __( 'Tipo', 'gustavoo-portfolio-core' ),
			'gso_tech'            => __( 'Tecnologias', 'gustavoo-portfolio-core' ),
			'gso_year'            => __( 'Ano', 'gustavoo-portfolio-core' ),
			'gso_featured'        => __( 'Destaque', 'gustavoo-portfolio-core' ),
			'gso_url'             => __( 'URL', 'gustavoo-portfolio-core' ),
			'gso_menu_order'      => __( 'Ordem', 'gustavoo-portfolio-core' ),
			'date'                => $columns['date'] ?? __( 'Data', 'gustavoo-portfolio-core' ),
		);
	}

	/**
	 * Render one project list cell.
	 *
	 * @param string $column  Column ID.
	 * @param int    $post_id Project ID.
	 * @return void
	 */
	public static function render_admin_column( $column, $post_id ) {
		switch ( $column ) {
			case 'gso_thumbnail':
				if ( has_post_thumbnail( $post_id ) ) {
					echo wp_kses_post( get_the_post_thumbnail( $post_id, array( 64, 48 ), array( 'style' => 'width:64px;height:48px;object-fit:cover;border-radius:4px;' ) ) );
				} else {
					echo '<span aria-hidden="true">—</span><span class="screen-reader-text">' . esc_html__( 'Sem imagem', 'gustavoo-portfolio-core' ) . '</span>';
				}
				break;
			case 'gso_client':
				echo esc_html( (string) get_post_meta( $post_id, self::META_CLIENT, true ) ?: '—' );
				break;
			case 'gso_type':
				self::render_terms_column( $post_id, self::TAX_TYPE );
				break;
			case 'gso_tech':
				self::render_terms_column( $post_id, self::TAX_TECH );
				break;
			case 'gso_year':
				$year = absint( get_post_meta( $post_id, self::META_YEAR, true ) );
				echo $year ? esc_html( (string) $year ) : '—';
				break;
			case 'gso_featured':
				if ( get_post_meta( $post_id, self::META_FEATURED, true ) ) {
					echo '<span class="dashicons dashicons-star-filled" aria-hidden="true"></span><span class="screen-reader-text">' . esc_html__( 'Sim', 'gustavoo-portfolio-core' ) . '</span>';
				} else {
					echo '<span aria-hidden="true">—</span><span class="screen-reader-text">' . esc_html__( 'Não', 'gustavoo-portfolio-core' ) . '</span>';
				}
				break;
			case 'gso_url':
				$url = self::sanitize_external_url( get_post_meta( $post_id, self::META_URL, true ) );
				if ( $url ) {
					$host = wp_parse_url( $url, PHP_URL_HOST );
					printf(
						'<a href="%1$s" target="_blank" rel="noopener noreferrer external">%2$s<span class="screen-reader-text"> %3$s</span></a>',
						esc_url( $url ),
						esc_html( $host ?: $url ),
						esc_html__( '(abre em nova aba)', 'gustavoo-portfolio-core' )
					);
				} else {
					echo '—';
				}
				break;
			case 'gso_menu_order':
				echo esc_html( (string) get_post_field( 'menu_order', $post_id ) );
				break;
		}
	}

	/**
	 * Render taxonomy terms in an admin column.
	 *
	 * @param int    $post_id  Project ID.
	 * @param string $taxonomy Taxonomy name.
	 * @return void
	 */
	private static function render_terms_column( $post_id, $taxonomy ) {
		$terms = get_the_terms( $post_id, $taxonomy );

		if ( ! $terms || is_wp_error( $terms ) ) {
			echo '—';
			return;
		}

		echo esc_html( implode( ', ', wp_list_pluck( $terms, 'name' ) ) );
	}

	/**
	 * Make selected columns sortable.
	 *
	 * @param array<string, string> $columns Existing sortable columns.
	 * @return array<string, string>
	 */
	public static function sortable_columns( $columns ) {
		$columns['gso_client']     = 'gso_client';
		$columns['gso_year']       = 'gso_year';
		$columns['gso_featured']   = 'gso_featured';
		$columns['gso_menu_order'] = 'menu_order';

		return $columns;
	}

	/**
	 * Apply safe sorting to the main project admin query.
	 *
	 * @param WP_Query $query Current query.
	 * @return void
	 */
	public static function apply_admin_sorting( $query ) {
		if ( ! is_admin() || ! $query->is_main_query() || self::POST_TYPE !== $query->get( 'post_type' ) ) {
			return;
		}

		switch ( $query->get( 'orderby' ) ) {
			case 'gso_client':
				$query->set( 'meta_key', self::META_CLIENT );
				$query->set( 'orderby', 'meta_value' );
				break;
			case 'gso_year':
				$query->set( 'meta_key', self::META_YEAR );
				$query->set( 'orderby', 'meta_value_num' );
				break;
			case 'gso_featured':
				$query->set( 'meta_key', self::META_FEATURED );
				$query->set( 'orderby', 'meta_value_num' );
				break;
			case 'menu_order':
				$query->set( 'orderby', 'menu_order title' );
				break;
		}
	}

	/**
	 * Add type and technology filters above the project list.
	 *
	 * @param string $post_type Current list post type.
	 * @return void
	 */
	public static function taxonomy_filters( $post_type ) {
		if ( self::POST_TYPE !== $post_type ) {
			return;
		}

		foreach ( array( self::TAX_TYPE, self::TAX_TECH ) as $taxonomy ) {
			$tax_object = get_taxonomy( $taxonomy );
			if ( ! $tax_object ) {
				continue;
			}

			// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only list filter.
			$selected = isset( $_GET[ $taxonomy ] ) ? sanitize_title( wp_unslash( $_GET[ $taxonomy ] ) ) : '';

			wp_dropdown_categories(
				array(
					'show_option_all' => sprintf(
						/* translators: %s: taxonomy label. */
						__( 'Todos: %s', 'gustavoo-portfolio-core' ),
						$tax_object->labels->name
					),
					'taxonomy'        => $taxonomy,
					'name'            => $taxonomy,
					'orderby'         => 'name',
					'selected'        => $selected,
					'hide_empty'      => false,
					'hierarchical'    => $tax_object->hierarchical,
					'value_field'     => 'slug',
				)
			);
		}
	}
}
