<?php

/**
 * UserHandler — Domain handler for WordPress user and role management.
 *
 * Owns: get_users, get_roles, manage_users, manage_roles
 *
 * @package LolaCore\Actions\Handlers
 * @license GPL-2.0-or-later
 */

declare( strict_types=1 );

namespace LolaCore\Actions\Handlers;

use LolaCore\Actions\Contracts\ActionHandlerInterface;

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

class UserHandler implements ActionHandlerInterface {

	/** Built-in WordPress roles that cannot be deleted. */
	private const PROTECTED_ROLES = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' );

	/** Maximum capabilities per bulk operation. */
	private const MAX_CAPS = 50;

	public function getSupportedActions(): array {
		return array( 'get_users', 'get_roles', 'manage_users', 'manage_roles' );
	}

	public function handle( string $actionId, array $params, bool $dryRun ): array {
		return match ( $actionId ) {
			'get_users'    => $this->getUsers( $params ),
			'get_roles'    => $this->getRoles( $params ),
			'manage_users' => $this->manageUsers( $params, $dryRun ),
			'manage_roles' => $this->manageRoles( $params, $dryRun ),
			default        => throw new \Exception( esc_html( "UserHandler: unknown action '{$actionId}'." ) ),
		};
	}

	private function getUsers( array $params ): array {
		$users = get_users( array( 'number' => 20 ) );
		$data  = array();
		foreach ( $users as $u ) {
			$data[] = array( 'id' => $u->ID, 'login' => $u->user_login, 'roles' => $u->roles );
		}
		return array( 'users' => $data );
	}

	private function getRoles( array $params ): array {
		$wp_roles  = wp_roles();
		$role_slug = $params['role_slug'] ?? '';

		if ( ! empty( $role_slug ) ) {
			if ( ! isset( $wp_roles->roles[ $role_slug ] ) ) {
				// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
				throw new \Exception( "Role '{$role_slug}' does not exist." );
			}
			$role_data  = $wp_roles->roles[ $role_slug ];
			$caps       = $role_data['capabilities'] ?? array();
			$user_count = count( get_users( array( 'role' => $role_slug, 'fields' => 'ID' ) ) );
			$granted    = array();
			$denied     = array();
			foreach ( $caps as $cap => $enabled ) {
				$enabled ? $granted[] = $cap : $denied[] = $cap;
			}
			sort( $granted );
			sort( $denied );
			return array(
				'role_slug'    => $role_slug,
				'display_name' => $role_data['name'],
				'user_count'   => $user_count,
				'capabilities' => $granted,
				'denied'       => $denied,
				'total_caps'   => count( $granted ),
			);
		}

		$roles = array();
		foreach ( $wp_roles->roles as $slug => $role_data ) {
			$caps_granted = array_keys( array_filter( $role_data['capabilities'] ?? array() ) );
			$user_count   = count( get_users( array( 'role' => $slug, 'fields' => 'ID' ) ) );
			$roles[]      = array( 'slug' => $slug, 'display_name' => $role_data['name'], 'user_count' => $user_count, 'cap_count' => count( $caps_granted ) );
		}
		return array( 'roles' => $roles, 'total' => count( $roles ) );
	}

	private function manageUsers( array $params, bool $dryRun ): array {
		$action = $params['action'] ?? '';
		if ( ! in_array( $action, array( 'create', 'update_role', 'delete' ), true ) ) {
			throw new \Exception( "Invalid action. Use 'create', 'update_role', or 'delete'." );
		}

		if ( $action === 'create' ) {
			$username = $params['username'] ?? '';
			$email    = $params['email'] ?? '';
			$role     = $params['role'] ?? 'subscriber';
			if ( empty( $username ) || empty( $email ) ) {
				throw new \Exception( 'Username and email are required to create a user.' );
			}
			if ( $dryRun ) {
				return array( 'action' => 'Will create user', 'username' => $username, 'email' => $email, 'role' => $role );
			}
			$password = wp_generate_password( 16, true, true );
			$user_id  = wp_create_user( $username, $password, $email );
			if ( is_wp_error( $user_id ) ) {
				// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
				throw new \Exception( 'User creation failed: ' . $user_id->get_error_message() );
			}
			( new \WP_User( $user_id ) )->set_role( $role );
			return array( 'success' => true, 'user_id' => $user_id, 'username' => $username, 'password' => $password, 'role' => $role, 'warning' => 'Copy the password now. It will not be shown again.' );
		}

		if ( $action === 'update_role' ) {
			$user_id = (int) ( $params['user_id'] ?? 0 );
			$role    = $params['role'] ?? '';
			if ( $user_id <= 0 || empty( $role ) ) {
				throw new \Exception( 'user_id and role are required.' );
			}
			$user = get_userdata( $user_id );
			if ( ! $user ) {
				// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
				throw new \Exception( "User with ID {$user_id} not found." );
			}
			if ( in_array( 'administrator', $user->roles, true ) && $role !== 'administrator' ) {
				$admin_count = count( get_users( array( 'role' => 'administrator', 'fields' => 'ID' ) ) );
				if ( $admin_count <= 1 ) {
					throw new \Exception( 'Cannot change role of the only administrator.' );
				}
			}
			if ( $dryRun ) {
				return array( 'user_id' => $user_id, 'username' => $user->user_login, 'current_role' => implode( ', ', $user->roles ), 'new_role' => $role );
			}
			( new \WP_User( $user_id ) )->set_role( $role );
			return array( 'success' => true, 'user_id' => $user_id, 'username' => $user->user_login, 'new_role' => $role );
		}

		// delete
		$user_id     = (int) ( $params['user_id'] ?? 0 );
		$reassign_to = (int) ( $params['reassign_to'] ?? 0 );
		if ( $user_id <= 0 ) {
			throw new \Exception( 'user_id is required for delete.' );
		}
		if ( $reassign_to <= 0 ) {
			throw new \Exception( 'reassign_to is mandatory. Content must be reassigned to prevent data loss.' );
		}
		$user = get_userdata( $user_id );
		if ( ! $user ) {
			// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
			throw new \Exception( "User with ID {$user_id} not found." );
		}
		if ( $user_id === get_current_user_id() ) {
			throw new \Exception( 'Cannot delete the currently logged-in user.' );
		}
		if ( in_array( 'administrator', $user->roles, true ) ) {
			$admin_count = count( get_users( array( 'role' => 'administrator', 'fields' => 'ID' ) ) );
			if ( $admin_count <= 1 ) {
				throw new \Exception( 'Cannot delete the only administrator.' );
			}
		}
		if ( $dryRun ) {
			$post_count = count_user_posts( $user_id );
			return array( 'action' => 'Will delete user', 'user_id' => $user_id, 'username' => $user->user_login, 'roles' => $user->roles, 'posts_owned' => $post_count, 'reassign_to' => $reassign_to, 'warning' => "All {$post_count} posts will be reassigned to user ID {$reassign_to}." );
		}
		require_once ABSPATH . 'wp-admin/includes/user.php';
		$result = wp_delete_user( $user_id, $reassign_to );
		return array( 'success' => (bool) $result, 'message' => $result ? "User '{$user->user_login}' deleted. Content reassigned to user ID {$reassign_to}." : 'User deletion failed.' );
	}

	private function manageRoles( array $params, bool $dryRun ): array {
		$action    = $params['action'] ?? '';
		$role_slug = $params['role_slug'] ?? '';

		if ( ! in_array( $action, array( 'create', 'delete', 'add_caps', 'remove_caps' ), true ) ) {
			throw new \Exception( "Invalid action. Use 'create', 'delete', 'add_caps', or 'remove_caps'." );
		}
		if ( empty( $role_slug ) ) {
			throw new \Exception( 'role_slug is required.' );
		}

		$wp_roles = wp_roles();

		if ( $action === 'create' ) {
			$display_name = $params['display_name'] ?? '';
			$clone_from   = $params['clone_from'] ?? '';
			if ( empty( $display_name ) ) {
				throw new \Exception( 'display_name is required when creating a role.' );
			}
			if ( isset( $wp_roles->roles[ $role_slug ] ) ) {
				// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
				throw new \Exception( "Role '{$role_slug}' already exists." );
			}
			$capabilities = array();
			if ( ! empty( $clone_from ) ) {
				if ( ! isset( $wp_roles->roles[ $clone_from ] ) ) {
					// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
					throw new \Exception( "Cannot clone from '{$clone_from}': role does not exist." );
				}
				$capabilities = $wp_roles->roles[ $clone_from ]['capabilities'] ?? array();
			}
			if ( $dryRun ) {
				$granted = array_keys( array_filter( $capabilities ) );
				sort( $granted );
				return array( 'action' => 'Will create role', 'role_slug' => $role_slug, 'display_name' => $display_name, 'cloned_from' => $clone_from ?: '(none — empty role)', 'capabilities' => $granted, 'cap_count' => count( $granted ) );
			}
			$result = add_role( $role_slug, $display_name, $capabilities );
			if ( null === $result ) {
				throw new \Exception( 'Failed to create role. The role may already exist or the slug is invalid.' );
			}
			return array( 'success' => true, 'role_slug' => $role_slug, 'display_name' => $display_name, 'cap_count' => count( array_filter( $capabilities ) ), 'message' => "Role '{$display_name}' ({$role_slug}) created successfully." );
		}

		if ( ! isset( $wp_roles->roles[ $role_slug ] ) ) {
			// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
			throw new \Exception( "Role '{$role_slug}' does not exist." );
		}

		if ( $action === 'delete' ) {
			if ( in_array( $role_slug, self::PROTECTED_ROLES, true ) ) {
				// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
				throw new \Exception( "Cannot delete built-in role '{$role_slug}'. Only custom roles can be deleted." );
			}
			$user_count = count( get_users( array( 'role' => $role_slug, 'fields' => 'ID' ) ) );
			if ( $user_count > 0 ) {
				// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
				throw new \Exception( "Cannot delete role '{$role_slug}': {$user_count} user(s) still assigned to it. Reassign them first." );
			}
			if ( $dryRun ) {
				return array( 'action' => 'Will delete role', 'role_slug' => $role_slug, 'display_name' => $wp_roles->roles[ $role_slug ]['name'], 'warning' => 'This role will be permanently removed.' );
			}
			remove_role( $role_slug );
			return array( 'success' => true, 'message' => "Role '{$role_slug}' has been deleted." );
		}

		// add_caps / remove_caps
		$capabilities = $params['capabilities'] ?? array();
		if ( empty( $capabilities ) || ! is_array( $capabilities ) ) {
			throw new \Exception( 'capabilities array is required for add_caps/remove_caps.' );
		}
		if ( count( $capabilities ) > self::MAX_CAPS ) {
			throw new \Exception( 'Maximum 50 capabilities per operation.' );
		}
		if ( $action === 'remove_caps' && $role_slug === 'administrator' && in_array( 'manage_options', $capabilities, true ) ) {
			throw new \Exception( 'Cannot remove manage_options from the administrator role. This would lock everyone out of site administration.' );
		}

		$role_object  = $wp_roles->get_role( $role_slug );
		$display_name = $wp_roles->roles[ $role_slug ]['name'];

		if ( $dryRun ) {
			return array( 'action' => $action === 'add_caps' ? 'Will add capabilities' : 'Will remove capabilities', 'role_slug' => $role_slug, 'display_name' => $display_name, 'capabilities' => $capabilities, 'count' => count( $capabilities ) );
		}

		foreach ( $capabilities as $cap ) {
			$cap = sanitize_key( $cap );
			if ( empty( $cap ) ) {
				continue;
			}
			$action === 'add_caps' ? $role_object->add_cap( $cap, true ) : $role_object->remove_cap( $cap );
		}

		$verb = $action === 'add_caps' ? 'added to' : 'removed from';
		return array( 'success' => true, 'role_slug' => $role_slug, 'display_name' => $display_name, 'capabilities' => $capabilities, 'message' => count( $capabilities ) . " capability(ies) {$verb} role '{$display_name}'." );
	}
}
