<?php
/**
 * Password hashing, validation, and management.
 *
 * @package PrivexPasswordProtect
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class PPMSTR_Password_Validator
 */
class PPMSTR_Password_Validator {

	/**
	 * Hash a plain-text password.
	 *
	 * @param string $password Plain-text password.
	 * @return string Hashed password.
	 */
	public function hash( $password ) {
		return wp_hash_password( $password );
	}

	/**
	 * Compare a plain-text password against a hash.
	 *
	 * @param string $password Plain-text password.
	 * @param string $hash     Stored hash.
	 * @return bool
	 */
	public function verify( $password, $hash ) {
		return wp_check_password( trim( $password ), $hash );
	}

	/**
	 * Validate against a post's passwords (supports multi-password).
	 * Returns the matched password ID or true/false.
	 *
	 * @param string $password Plain-text password.
	 * @param int    $post_id  Post ID.
	 * @return string|false Password ID on success, false on failure.
	 */
	public function validate_for_post( $password, $post_id ) {
		// Check master password first.
		if ( $this->validate_master( $password ) ) {
			return 'master';
		}

		// Check multi-password array.
		$passwords = get_post_meta( $post_id, '_ppmstr_passwords', true );
		if ( is_array( $passwords ) && ! empty( $passwords ) ) {
			foreach ( $passwords as &$entry ) {
				if ( 'active' !== ( $entry['status'] ?? 'active' ) ) {
					continue;
				}
				// Check expiration.
				if ( ! empty( $entry['expires_at'] ) && time() > (int) $entry['expires_at'] ) {
					$entry['status'] = 'expired';
					continue;
				}
				// Check usage limit.
				if ( ! empty( $entry['max_uses'] ) && (int) $entry['use_count'] >= (int) $entry['max_uses'] ) {
					$entry['status'] = 'expired';
					continue;
				}

				if ( $this->verify( $password, $entry['hash'] ) ) {
					// Increment usage count.
					$entry['use_count'] = ( $entry['use_count'] ?? 0 ) + 1;
					if ( empty( $entry['first_used'] ) ) {
						$entry['first_used'] = time();
					}
					// Check if now over limit.
					if ( ! empty( $entry['max_uses'] ) && (int) $entry['use_count'] >= (int) $entry['max_uses'] ) {
						$entry['status'] = 'expired';
					}
					update_post_meta( $post_id, '_ppmstr_passwords', $passwords );
					return $entry['id'];
				}
			}
			// Save any status changes.
			update_post_meta( $post_id, '_ppmstr_passwords', $passwords );
		}

		// Fallback: legacy single password.
		$legacy_hash = get_post_meta( $post_id, '_ppmstr_password', true );
		if ( ! empty( $legacy_hash ) && $this->verify( $password, $legacy_hash ) ) {
			return 'legacy';
		}

		return false;
	}

	/**
	 * Validate against the site-wide password.
	 *
	 * @param string $password Plain-text password.
	 * @return bool
	 */
	public function validate_for_site( $password ) {
		// Master password also works for site.
		if ( $this->validate_master( $password ) ) {
			return true;
		}

		$settings = get_option( 'ppmstr_settings', array() );
		$stored   = isset( $settings['site_password'] ) ? $settings['site_password'] : '';
		if ( empty( $stored ) ) {
			return false;
		}
		return $this->verify( $password, $stored );
	}

	/**
	 * Validate against the post type global password.
	 *
	 * @param string $password  Plain-text password.
	 * @param string $post_type The post type slug.
	 * @return bool
	 */
	public function validate_for_post_type( $password, $post_type ) {
		if ( $this->validate_master( $password ) ) {
			return true;
		}

		$settings = get_option( 'ppmstr_settings', array() );
		$protected = $settings['protected_post_types'] ?? array();

		if ( ! in_array( $post_type, $protected ) ) {
			return false;
		}

		$stored = $settings['post_type_password'] ?? '';
		if ( empty( $stored ) ) {
			return false;
		}

		return $this->verify( $password, $stored );
	}

	/**
	 * Validate against the category global password.
	 *
	 * @param string $password Plain-text password.
	 * @param int    $term_id  Term ID.
	 * @return bool
	 */
	public function validate_for_category( $password, $term_id ) {
		if ( $this->validate_master( $password ) ) {
			return true;
		}

		$settings = get_option( 'ppmstr_settings', array() );
		$protected = $settings['protected_categories'] ?? array();

		if ( ! in_array( $term_id, $protected ) ) {
			return false;
		}

		$stored = $settings['category_password'] ?? '';
		if ( empty( $stored ) ) {
			return false;
		}

		return $this->verify( $password, $stored );
	}

	/**
	 * Validate against the master password.
	 *
	 * @param string $password Plain-text password.
	 * @return bool
	 */
	public function validate_master( $password ) {
		$settings = get_option( 'ppmstr_settings', array() );
		$master   = isset( $settings['master_password'] ) ? $settings['master_password'] : '';
		if ( empty( $master ) ) {
			return false;
		}
		return $this->verify( trim( $password ), $master );
	}

	/**
	 * Generate a unique password ID.
	 *
	 * @return string
	 */
	public static function generate_id() {
		return 'ppmstr_' . bin2hex( random_bytes( 6 ) );
	}

	/**
	 * Generate a random password string.
	 *
	 * @param int $length Password length.
	 * @return string
	 */
	public static function generate_random( $length = 12 ) {
		$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%';
		$pass  = '';
		$max   = strlen( $chars ) - 1;
		for ( $i = 0; $i < $length; $i++ ) {
			$pass .= $chars[ wp_rand( 0, $max ) ];
		}
		return $pass;
	}

	/**
	 * Create a password entry array.
	 *
	 * @param string $plain_password Plain-text password.
	 * @param string $label          Label.
	 * @param int    $expires_at     Expiry timestamp (0 = never).
	 * @param int    $max_uses       Max uses (0 = unlimited).
	 * @return array
	 */
	public static function create_entry( $plain_password, $label = '', $expires_at = 0, $max_uses = 0 ) {
		return array(
			'id'         => self::generate_id(),
			'hash'       => wp_hash_password( $plain_password ),
			'label'      => sanitize_text_field( $label ),
			'created'    => time(),
			'expires_at' => absint( $expires_at ),
			'max_uses'   => absint( $max_uses ),
			'use_count'  => 0,
			'first_used' => 0,
			'status'     => 'active',
		);
	}
}
