<?php

namespace Automattic\Gravatar\GravatarEnhanced\Proxy;

use WP_Error;
use WP_Filesystem_Base;

class ProxyCache {
	const FILTER_PROXY_URL_BASE = 'gravatar_proxy_url_base';
	const FILTER_PROXY_DIRECTORY = 'gravatar_proxy_directory';
	const FILTER_PROXY_AVATAR = 'gravatar_proxy_avatar';

	/**
	 * Check if the user has already been cached locally
	 *
	 * @param AvatarHash $hash
	 * @return bool
	 */
	public function exists_locally( $hash ) {
		$local_filename = $this->get_proxied_filename( $hash );

		return file_exists( $local_filename );
	}

	/**
	 * Download the avatar for a user
	 *
	 * @param AvatarHash $hash
	 * @return true | WP_Error
	 */
	public function copy_avatar_locally( $hash ) {
		$proxy_filename = $this->get_proxied_filename( $hash );

		// Make sure the directory exists
		wp_mkdir_p( dirname( $proxy_filename ) );

		if ( ! $this->can_cache() ) {
			return new WP_Error( 'directory-failed', 'Failed to create directory' );
		}

		// Set the user agent for even more privacy
		$args = [
			'user-agent' => 'Gravatar Proxy',
		];

		// Get the file from Gravatar
		$request = wp_safe_remote_get( $hash->get_remote_url(), $args );

		if ( is_wp_error( $request ) || wp_remote_retrieve_response_code( $request ) !== 200 ) {
			return new WP_Error( 'download-failed', 'Failed to download from Gravatar' );
		}

		$body = wp_remote_retrieve_body( $request );
		if ( ! $body ) {
			return new WP_Error( 'download-failed', 'Failed to download from Gravatar' );
		}

		// Allow others to modify the image itself
		$body = apply_filters( self::FILTER_PROXY_AVATAR, $body );

		// Store the file
		$result = file_put_contents( $proxy_filename, $body );
		if ( $result === false ) {
			return new WP_Error( 'write-failed', 'Failed to write to local file' );
		}

		return true;
	}

	/**
	 * Remove all cached avatars
	 *
	 * @return bool
	 */
	public function flush() {
		$wp_filesystem = $this->get_filesystem();

		// Delete the directory and everything in it
		try {
			return $wp_filesystem->delete( $this->get_proxied_directory(), true, 'd' );
		} catch ( \TypeError $e ) {
			return false;
		}
	}

	/**
	 * Return an array of entries (file paths) that have been cached in the proxy
	 *
	 * @return string[]
	 */
	public function get_entries() {
		$wp_filesystem = $this->get_filesystem();

		try {
			$entries = $wp_filesystem->dirlist( $this->get_proxied_directory() );
		} catch ( \TypeError $e ) {
			$entries = false;
		}

		if ( $entries === false ) {
			return [];
		}

		return array_map(
			function ( $entry ) {
				return $this->get_proxied_directory() . $entry['name'];
			},
			$entries
		);
	}

	/**
	 * Return the URL for a proxied avatar
	 *
	 * @param AvatarHash $hash
	 * @return string
	 */
	public function get_local_url( $hash ) {
		$dir = wp_upload_dir();
		$base = $dir['baseurl'];

		if ( is_ssl() ) {
			$base = str_replace( 'http://', 'https://', $base );
		}

		return apply_filters( self::FILTER_PROXY_URL_BASE, $base . '/gravatar/' ) . $hash->get_local_hash() . '.png';
	}

	/**
	 * Return the file location for a proxied avatar
	 *
	 * @param AvatarHash $hash
	 * @return string
	 */
	private function get_proxied_filename( $hash ) {
		return $this->get_proxied_directory() . $hash->get_local_hash() . '.png';
	}

	/**
	 * Return the directory for proxied avatars
	 *
	 * @return string
	 */
	private function get_proxied_directory() {
		$dir = wp_upload_dir();

		return apply_filters( self::FILTER_PROXY_DIRECTORY, $dir['basedir'] . '/gravatar/' );
	}

	/**
	 * @return WP_Filesystem_Base
	 */
	private function get_filesystem() {
		global $wp_filesystem;

		// If the filesystem has not been instantiated yet, do it here.
		if ( ! $wp_filesystem ) {
			if ( ! function_exists( 'WP_Filesystem' ) ) {
				require_once wp_normalize_path( ABSPATH . '/wp-admin/includes/file.php' );
			}
			WP_Filesystem();
		}

		return $wp_filesystem;
	}

	/**
	 * Can we create files in the cache?
	 *
	 * @return bool
	 */
	public function can_cache() {
		$proxy_filename = $this->get_proxied_directory();

		return is_dir( $proxy_filename ) && is_writable( $proxy_filename );
	}
}