<?php

declare(strict_types=1);

namespace SecurWP\Modules\Tools;

use SecurWP\Includes\SettingsStore;

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

/**
 * Shared policy for wp-config.php access in Tools.
 *
 * Centralizes path resolution and write policy so status checks and tool
 * execution use the same source of truth.
 */
final class ConfigFilePolicy {

	/**
	 * Resolve standard WordPress wp-config.php candidates.
	 *
	 * @return array<int,string>
	 */
	private static function get_allowed_wp_config_paths(): array {
		$candidates = [];

		$primary = ABSPATH . 'wp-config.php';
		if ( file_exists( $primary ) ) {
			$candidates[] = $primary;
		}

		$parent = dirname( ABSPATH ) . '/wp-config.php';
		if ( file_exists( $parent ) && ! file_exists( dirname( ABSPATH ) . '/wp-settings.php' ) ) {
			$candidates[] = $parent;
		}

		$allowed = [];
		foreach ( $candidates as $candidate ) {
			$resolved = self::normalize_wp_config_path( $candidate );
			if ( $resolved !== '' && ! in_array( $resolved, $allowed, true ) ) {
				$allowed[] = $resolved;
			}
		}

		return $allowed;
	}

	/**
	 * Normalize a filesystem path for safe comparisons.
	 */
	private static function normalize_wp_config_path( string $path ): string {
		$trimmed = trim( $path );
		if ( $trimmed === '' ) {
			return '';
		}

		$real = realpath( $trimmed );
		if ( is_string( $real ) && $real !== '' ) {
			return wp_normalize_path( $real );
		}

		return wp_normalize_path( $trimmed );
	}

	/**
	 * Validate a candidate against the standard WordPress wp-config.php locations.
	 *
	 * @param string            $candidate Candidate path from core resolution or filter.
	 * @param array<int,string> $allowed   Canonical allowlist of accepted locations.
	 * @return string|false Canonical allowed path, false when rejected.
	 */
	private static function validate_wp_config_path( string $candidate, array $allowed ) {
		$normalized = self::normalize_wp_config_path( $candidate );
		if ( $normalized === '' || basename( $normalized ) !== 'wp-config.php' ) {
			return false;
		}

		foreach ( $allowed as $allowed_path ) {
			if ( $normalized === $allowed_path ) {
				return $allowed_path;
			}
		}

		return false;
	}

	/**
	 * Resolve wp-config.php path using WordPress-compatible lookup.
	 *
	 * @return string|false Absolute path when found, false otherwise.
	 */
	public static function get_wp_config_path() {
		$allowed = self::get_allowed_wp_config_paths();
		if ( $allowed === [] ) {
			return false;
		}

		$default_path = $allowed[0];
		$filtered     = apply_filters( 'securwp_filter_wp_config_file_path', $default_path );
		if ( ! is_string( $filtered ) ) {
			return false;
		}

		return self::validate_wp_config_path( $filtered, $allowed );
	}

	/**
	 * Whether SecurWP is allowed to write managed files.
	 *
	 * @return bool
	 */
	public static function can_write_to_files(): bool {
		$global    = SettingsStore::get( SettingsStore::COL_GLOBAL, [] );
		$can_write = ! isset( $global['write_to_files'] ) || (bool) $global['write_to_files'];

		return (bool) apply_filters( 'securwp_filter_can_write_to_files', $can_write );
	}

	/**
	 * Compute effective wp-config writability status for tools.
	 *
	 * @return array{
	 *   path: string|false,
	 *   write_enabled: bool,
	 *   file_exists: bool,
	 *   file_writable: bool,
	 *   dir_writable: bool,
	 *   writable: bool
	 * }
	 */
	public static function get_wp_config_status(): array {
		$path          = self::get_wp_config_path();
		$write_enabled = self::can_write_to_files();
		$file_exists   = is_string( $path ) && file_exists( $path );
		$file_writable = $file_exists && wp_is_writable( $path );
		$dir_writable  = $file_exists && wp_is_writable( dirname( $path ) );

		return [
			'path'          => $path,
			'write_enabled' => $write_enabled,
			'file_exists'   => $file_exists,
			'file_writable' => $file_writable,
			'dir_writable'  => $dir_writable,
			'writable'      => $write_enabled && $file_exists && $file_writable && $dir_writable,
		];
	}
}
