<?php

declare( strict_types=1 );

namespace LocalUserAvatar;

use WP_PowerSuite\Core\AbstractModule;
use WP_PowerSuite\Core\Contracts\DataLocationsInterface;
use WP_User;

class Module extends AbstractModule implements DataLocationsInterface {

	private const MODULE_ID             = 'local-user-avatar';
	private const MODULE_VER            = '1.1.0';
	private const META_KEY              = 'powersuite_avatar';
	private const TRANSIENT_HAS_AVATARS = 'wpps_local_user_avatar_any';

	/** @var array<int, int> */
	private array $avatar_id_cache = [];

	/** @var array<string, ?WP_User> */
	private array $resolved_user_cache = [];

	public function getId(): string {
		return self::MODULE_ID;
	}

	public function getName(): string {
		return __( 'Local User Avatar', 'wp-powersuite' );
	}

	public function getVersion(): string {
		return self::MODULE_VER;
	}

	public function getDescription(): string {
		return __( 'On-site profile photos instead of Gravatar—simple for members and hosts.', 'wp-powersuite' );
	}

	public function getContext(): string|array {
		return 'both';
	}

	public function getDependencies(): array {
		return [];
	}

	public function getDataLocations(): array {
		return [
			'user_meta' => [ self::META_KEY ],
		];
	}

	protected function registerHooks(): void {
		add_filter( 'get_avatar_data', [ $this, 'filterAvatarData' ], 15, 2 );
		add_filter( 'get_avatar_url', [ $this, 'filterAvatarUrl' ], 15, 3 );

		if ( is_admin() ) {
			add_action( 'show_user_profile', [ $this, 'renderProfileSection' ], 9 );
			add_action( 'edit_user_profile', [ $this, 'renderProfileSection' ], 9 );
			add_action( 'personal_options_update', [ $this, 'saveProfileAvatar' ] );
			add_action( 'edit_user_profile_update', [ $this, 'saveProfileAvatar' ] );
			add_action( 'delete_attachment', [ $this, 'deleteAttachmentUserMeta' ] );
			add_action( 'admin_enqueue_scripts', [ $this, 'enqueueProfileAssets' ] );
			add_action( 'admin_head', [ $this, 'hideCoreProfilePicture' ] );
		}
	}

	public function filterAvatarData( array $args, $id_or_email ): array {
		$local = $this->resolveLocalAvatar( $id_or_email, $args );
		if ( $local === null ) {
			return $args;
		}

		$args['url']          = $local['url'];
		$args['found_avatar'] = true;
		$args['width']        = $local['width'];
		$args['height']       = $local['height'];

		if ( empty( $args['class'] ) ) {
			$args['class'] = [];
		} elseif ( is_string( $args['class'] ) ) {
			$args['class'] = preg_split( '/\s+/', trim( $args['class'] ) ) ?: [];
		}

		$args['class'][] = 'avatar-local';

		return $args;
	}

	public function filterAvatarUrl( string $url, $id_or_email, array $args ): string {
		$local = $this->resolveLocalAvatar( $id_or_email, $args );
		if ( $local === null ) {
			return $url;
		}

		return $local['url'];
	}

	public function renderProfileSection( WP_User $user ): void {
		if ( ! current_user_can( 'edit_user', $user->ID ) ) {
			return;
		}

		$aid            = $this->getAvatarId( $user->ID );
		$has_local      = $aid > 0 && wp_attachment_is_image( $aid );
		$src            = $has_local ? wp_get_attachment_image_url( $aid, [ 150, 150 ] ) : '';
		$default_avatar = get_avatar( $user->ID, 150, '', $user->display_name, [ 'force_display' => true ] );
		$name           = 'powersuite_avatar';
		?>
		<h3><?php esc_html_e( 'Profile Picture', 'wp-powersuite' ); ?></h3>
		<table class="form-table">
			<tr>
				<th scope="row"><label for="wpps-local-avatar-upload"><?php esc_html_e( 'Local avatar', 'wp-powersuite' ); ?></label></th>
				<td>
					<div class="wpps-local-avatar-wrap" data-user-id="<?php echo esc_attr( (string) $user->ID ); ?>">
						<div class="wpps-local-avatar-preview">
							<?php if ( $src ) : ?>
								<img src="<?php echo esc_url( $src ); ?>" alt="" class="wpps-local-avatar-img" />
							<?php else : ?>
								<div class="wpps-local-avatar-default"><?php echo $default_avatar; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
							<?php endif; ?>
						</div>
						<div class="wpps-local-avatar-default-tpl" hidden><?php echo $default_avatar; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
						<input type="hidden" name="<?php echo esc_attr( $name ); ?>" id="wpps-local-avatar-id" value="<?php echo $has_local ? (int) $aid : ''; ?>" />
						<p class="wpps-local-avatar-actions">
							<button type="button" class="button" id="wpps-local-avatar-upload"><?php echo $has_local ? esc_html__( 'Replace', 'wp-powersuite' ) : esc_html__( 'Upload Avatar', 'wp-powersuite' ); ?></button>
							<button type="button" class="button" id="wpps-local-avatar-remove" <?php echo $has_local ? '' : ' hidden'; ?>><?php esc_html_e( 'Remove', 'wp-powersuite' ); ?></button>
						</p>
						<p class="description"><?php esc_html_e( 'Upload a custom profile picture. Stored on this site; overrides Gravatar when set.', 'wp-powersuite' ); ?></p>
					</div>
				</td>
			</tr>
		</table>
		<?php
	}

	public function saveProfileAvatar( int $user_id ): void {
		if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'update-user_' . $user_id ) ) {
			return;
		}

		if ( ! current_user_can( 'edit_user', $user_id ) ) {
			return;
		}

		$raw = isset( $_POST['powersuite_avatar'] ) ? sanitize_text_field( wp_unslash( $_POST['powersuite_avatar'] ) ) : '';
		if ( $raw === '' ) {
			delete_user_meta( $user_id, self::META_KEY );
			unset( $this->avatar_id_cache[ $user_id ] );
			delete_transient( self::TRANSIENT_HAS_AVATARS );
			return;
		}

		$aid = absint( $raw );
		if ( $aid === 0 ) {
			return;
		}

		$att = get_post( $aid );
		if ( ! $att || $att->post_type !== 'attachment' || ! wp_attachment_is_image( $aid ) ) {
			return;
		}

		$is_own = (int) $user_id === (int) get_current_user_id();
		if ( $is_own && ! current_user_can( 'manage_options' ) && (int) $att->post_author !== (int) get_current_user_id() ) {
			return;
		}

		update_user_meta( $user_id, self::META_KEY, $aid );
		$this->avatar_id_cache[ $user_id ] = $aid;
		delete_transient( self::TRANSIENT_HAS_AVATARS );
	}

	public function deleteAttachmentUserMeta( int $post_id ): void {
		global $wpdb;

		$wpdb->delete(
			$wpdb->usermeta,
			[
				// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- clear avatar usermeta for one attachment.
				'meta_key'   => self::META_KEY,
				// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- scoped to the deleted attachment id.
				'meta_value' => (string) $post_id,
			],
			[ '%s', '%s' ]
		);

		$this->avatar_id_cache = [];
		delete_transient( self::TRANSIENT_HAS_AVATARS );
	}

	public function enqueueProfileAssets( string $hook_suffix ): void {
		if ( $hook_suffix !== 'profile.php' && $hook_suffix !== 'user-edit.php' ) {
			return;
		}

		wp_enqueue_media();

		$base          = defined( 'WP_POWERSUITE_PLUGIN_URL' ) ? WP_POWERSUITE_PLUGIN_URL : '';
		$style_handle  = 'wpps-local-user-avatar-profile';
		$script_handle = 'wpps-local-user-avatar-profile';

		wp_enqueue_style(
			$style_handle,
			$base . 'modules/' . self::MODULE_ID . '/assets/profile.css',
			[],
			self::MODULE_VER
		);

		wp_enqueue_script(
			$script_handle,
			$base . 'modules/' . self::MODULE_ID . '/assets/profile.js',
			[ 'jquery', 'media-editor', 'media-views' ],
			self::MODULE_VER,
			true
		);

		wp_localize_script(
			$script_handle,
			'wppsLocalUserAvatar',
			[
				'strings' => [
					'replace'     => __( 'Replace', 'wp-powersuite' ),
					'upload'      => __( 'Upload Avatar', 'wp-powersuite' ),
					'selectTitle' => __( 'Select or upload avatar', 'wp-powersuite' ),
					'useAsAvatar' => __( 'Use as avatar', 'wp-powersuite' ),
				],
			]
		);
	}

	public function hideCoreProfilePicture(): void {
		$screen = get_current_screen();
		if ( ! $screen || ! in_array( $screen->id, [ 'profile', 'user-edit' ], true ) ) {
			return;
		}
		echo '<style>tr.user-profile-picture{display:none!important;}</style>';
	}

	/**
	 * @param array<string, mixed> $args
	 * @return array{url: string, width: int, height: int}|null
	 */
	private function resolveLocalAvatar( $id_or_email, array $args ): ?array {
		if ( ! $this->siteHasLocalAvatars() ) {
			return null;
		}

		$user = $this->resolveUser( $id_or_email );
		if ( ! $user instanceof WP_User ) {
			return null;
		}

		$aid = $this->getAvatarId( $user->ID );
		if ( $aid === 0 || ! wp_attachment_is_image( $aid ) ) {
			return null;
		}

		$image_size     = $this->normalizeAvatarSize( $args );
		$attachment_src = wp_get_attachment_image_src( $aid, $image_size );
		if ( ! is_array( $attachment_src ) || empty( $attachment_src[0] ) ) {
			return null;
		}

		return [
			'url'    => (string) $attachment_src[0],
			'width'  => isset( $attachment_src[1] ) ? (int) $attachment_src[1] : 96,
			'height' => isset( $attachment_src[2] ) ? (int) $attachment_src[2] : 96,
		];
	}

	/**
	 * @param array<string, mixed> $args
	 * @return array{0: int, 1: int}|string
	 */
	private function normalizeAvatarSize( array $args ): array|string {
		$fallback = [ 96, 96 ];

		if ( ! isset( $args['size'] ) ) {
			return $fallback;
		}

		$size = $args['size'];

		if ( is_numeric( $size ) ) {
			$n = absint( $size );
			return $n > 0 ? [ $n, $n ] : $fallback;
		}

		if ( is_string( $size ) && $size !== '' ) {
			$name = sanitize_key( $size );
			return $name !== '' ? $name : $fallback;
		}

		if ( is_array( $size ) && isset( $size[0], $size[1] ) ) {
			return [
				max( 1, absint( $size[0] ) ),
				max( 1, absint( $size[1] ) ),
			];
		}

		return $fallback;
	}

	private function siteHasLocalAvatars(): bool {
		$cached = get_transient( self::TRANSIENT_HAS_AVATARS );
		if ( $cached !== false ) {
			return (bool) $cached;
		}

		global $wpdb;
		$exists = (bool) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT 1 FROM {$wpdb->usermeta} WHERE meta_key = %s LIMIT 1",
				self::META_KEY
			)
		);

		set_transient( self::TRANSIENT_HAS_AVATARS, $exists ? 1 : 0, DAY_IN_SECONDS );

		return $exists;
	}

	private function resolveUser( $id_or_email ): ?WP_User {
		if ( $id_or_email instanceof WP_User ) {
			return $id_or_email;
		}

		if ( is_object( $id_or_email ) ) {
			if ( isset( $id_or_email->ID ) && is_numeric( $id_or_email->ID ) ) {
				$found = get_user_by( 'id', (int) $id_or_email->ID );
				return $found instanceof WP_User ? $found : null;
			}

			if ( isset( $id_or_email->user_id ) && (int) $id_or_email->user_id > 0 ) {
				$found = get_user_by( 'id', (int) $id_or_email->user_id );
				return $found instanceof WP_User ? $found : null;
			}

			if ( isset( $id_or_email->comment_author_email ) && is_string( $id_or_email->comment_author_email ) && $id_or_email->comment_author_email !== '' ) {
				$found = get_user_by( 'email', $id_or_email->comment_author_email );
				return $found instanceof WP_User ? $found : null;
			}

			return null;
		}

		if ( ! is_scalar( $id_or_email ) ) {
			return null;
		}

		$cache_key = (string) $id_or_email;
		if ( array_key_exists( $cache_key, $this->resolved_user_cache ) ) {
			return $this->resolved_user_cache[ $cache_key ];
		}

		$user = null;
		if ( is_numeric( $id_or_email ) ) {
			$found = get_user_by( 'id', (int) $id_or_email );
			$user  = $found instanceof WP_User ? $found : null;
		} elseif ( is_string( $id_or_email ) && strpos( $id_or_email, '@' ) !== false ) {
			$found = get_user_by( 'email', $id_or_email );
			$user  = $found instanceof WP_User ? $found : null;
		} elseif ( is_string( $id_or_email ) && $id_or_email !== '' ) {
			$found = get_user_by( 'login', $id_or_email );
			$user  = $found instanceof WP_User ? $found : null;
		}

		$this->resolved_user_cache[ $cache_key ] = $user;

		return $user;
	}

	private function getAvatarId( int $user_id ): int {
		if ( array_key_exists( $user_id, $this->avatar_id_cache ) ) {
			return $this->avatar_id_cache[ $user_id ];
		}

		$v  = get_user_meta( $user_id, self::META_KEY, true );
		$id = is_numeric( $v ) ? absint( $v ) : 0;
		$this->avatar_id_cache[ $user_id ] = $id;

		return $id;
	}
}
