<?php

namespace GriffinForms\Includes\Pipelines\Accounts;

class AdminActionService
{
    protected $state_store;
    protected $executor;
    protected $log;

    public function __construct()
    {
        $this->state_store = new ActionStateStore();
        $this->executor = new LifecycleExecutor();
        $this->log = \GriffinForms\Log::getInstance();
    }

    public function getSubmissionAccountsViewModel(int $submission_id): array
    {
        $state = $this->state_store->getAccountsState($submission_id);
        if (empty($state) || empty($state['setups']) || !is_array($state['setups'])) {
            return [];
        }

        $setups = [];
        foreach ($state['setups'] as $setup) {
            if (!is_array($setup)) {
                continue;
            }

            $setups[] = $this->mapSetupForView($setup);
        }

        return [
            'submission_id' => $submission_id,
            'contract_phase' => (string) ($state['contract_phase'] ?? ''),
            'integration_id' => (string) ($state['integration_id'] ?? Contract::INTEGRATION_ID),
            'updated_at' => (string) ($state['updated_at'] ?? ''),
            'setups' => $setups,
        ];
    }

    public function validateActionRequest(int $submission_id, string $action_id, string $action_type)
    {
        $action_id = sanitize_key($action_id);
        $action_type = sanitize_key($action_type);

        if ($submission_id <= 0 || $action_id === '' || $action_type === '') {
            return new \WP_Error('invalid_account_action_request', __('Invalid account action request.', 'griffinforms-form-builder'));
        }

        $view_model = $this->getSubmissionAccountsViewModel($submission_id);
        if (empty($view_model)) {
            return new \WP_Error('missing_submission_accounts_state', __('No account lifecycle state was found for this submission.', 'griffinforms-form-builder'));
        }

        foreach ($view_model['setups'] as $setup) {
            $targets = isset($setup['targets']) && is_array($setup['targets']) ? $setup['targets'] : [];
            foreach ($targets as $target) {
                if ((string) ($target['action_id'] ?? '') !== $action_id) {
                    continue;
                }

                $available_actions = isset($target['available_actions']) && is_array($target['available_actions'])
                    ? $target['available_actions']
                    : [];

                if (!isset($available_actions[$action_type])) {
                    return new \WP_Error(
                        'account_action_not_allowed',
                        __('This account action is not currently allowed for the selected target.', 'griffinforms-form-builder')
                    );
                }

                return [
                    'submission_id' => $submission_id,
                    'setup_id' => (string) ($setup['setup_id'] ?? ''),
                    'action_id' => $action_id,
                    'action_type' => $action_type,
                    'action_label' => (string) ($available_actions[$action_type]['label'] ?? ''),
                    'setup' => $setup,
                    'target' => $target,
                ];
            }
        }

        return new \WP_Error('missing_account_action', __('The requested account action could not be found for this submission.', 'griffinforms-form-builder'));
    }

    private function mapSetupForView(array $setup): array
    {
        $targets = [];
        $status_counts = [];

        foreach (($setup['targets'] ?? []) as $target) {
            if (!is_array($target)) {
                continue;
            }

            $mapped_target = $this->mapTargetForView($target);
            $targets[] = $mapped_target;
            $status_key = (string) ($mapped_target['status']['key'] ?? '');
            if ($status_key !== '') {
                if (!isset($status_counts[$status_key])) {
                    $status_counts[$status_key] = 0;
                }
                $status_counts[$status_key]++;
            }
        }

        return [
            'setup_id' => (string) ($setup['setup_id'] ?? ''),
            'setup_index' => isset($setup['setup_index']) ? (int) $setup['setup_index'] : 0,
            'creation_mode' => (string) ($setup['creation_mode'] ?? ''),
            'creation_mode_label' => $this->getCreationModeLabel((string) ($setup['creation_mode'] ?? '')),
            'existing_account_behavior' => (string) ($setup['existing_account_behavior'] ?? ''),
            'existing_account_behavior_label' => $this->getExistingAccountBehaviorLabel((string) ($setup['existing_account_behavior'] ?? '')),
            'target_count' => count($targets),
            'has_mixed_statuses' => count($status_counts) > 1,
            'status_counts' => $status_counts,
            'targets' => $targets,
        ];
    }

    private function mapTargetForView(array $target): array
    {
        $status_key = (string) ($target['status'] ?? '');
        $status_label = $this->getTargetStatusLabel($status_key, (string) ($target['result'] ?? ''));
        $confirmation_status = (string) ($target['confirmation_email_status'] ?? '');

        return [
            'action_id' => (string) ($target['action_id'] ?? ''),
            'target_index' => isset($target['target_index']) ? (int) $target['target_index'] : 0,
            'target_email' => (string) ($target['target_email'] ?? ''),
            'avatar_url' => $this->getTargetAvatarUrl($target),
            'existing_account_behavior' => (string) ($target['existing_account_behavior'] ?? ''),
            'password_mode' => (string) ($target['password_mode'] ?? ''),
            'role' => (string) ($target['role'] ?? ''),
            'send_confirmation_email' => !empty($target['send_confirmation_email']),
            'confirmation_message_id' => isset($target['confirmation_message_id']) ? (int) ($target['confirmation_message_id']) : 0,
            'status' => [
                'key' => $status_key,
                'label' => $status_label,
            ],
            'status_reason' => (string) ($target['status_reason'] ?? ''),
            'status_message' => (string) ($target['status_message'] ?? ''),
            'result' => (string) ($target['result'] ?? ''),
            'user_id' => isset($target['user_id']) ? (int) $target['user_id'] : 0,
            'user_edit_url' => $this->getUserEditUrl(isset($target['user_id']) ? (int) $target['user_id'] : 0),
            'resolved_username' => (string) ($target['resolved_username'] ?? ''),
            'display_username' => $this->getDisplayUsername($target),
            'admin_action_summary' => $this->getAdminActionSummary($target),
            'resolved_first_name' => (string) ($target['resolved_first_name'] ?? ''),
            'resolved_last_name' => (string) ($target['resolved_last_name'] ?? ''),
            'resolved_profile_image' => isset($target['resolved_profile_image']) && is_array($target['resolved_profile_image'])
                ? $target['resolved_profile_image']
                : [],
            'password_setup_email' => [
                'enabled' => (string) ($target['password_mode'] ?? '') !== 'mapped_password',
                'status' => (string) ($target['password_setup_email_status'] ?? ''),
                'label' => $this->getEmailStatusLabel((string) ($target['password_setup_email_status'] ?? '')),
                'reason' => $this->getEmailReasonLabel((string) ($target['password_setup_email_reason'] ?? '')),
                'sent_at' => $this->formatDateTime((string) ($target['password_setup_email_sent_at'] ?? '')),
            ],
            'confirmation_email' => [
                'status' => $confirmation_status,
                'label' => $this->getEmailStatusLabel($confirmation_status),
                'reason' => $this->getEmailReasonLabel((string) ($target['confirmation_email_reason'] ?? '')),
                'sent_at' => $this->formatDateTime((string) ($target['confirmation_email_sent_at'] ?? '')),
            ],
            'runtime_issues' => isset($target['runtime_issues']) && is_array($target['runtime_issues'])
                ? $this->mapRuntimeIssuesForView($target['runtime_issues'])
                : [],
            'available_actions' => $this->getAvailableActionsForTarget($target),
        ];
    }

    private function getDisplayUsername(array $target): string
    {
        $resolved_username = (string) ($target['resolved_username'] ?? '');
        $result = (string) ($target['result'] ?? '');
        $user_id = isset($target['user_id']) ? (int) $target['user_id'] : 0;

        if ($user_id > 0 && in_array($result, ['updated', 'accepted_noop'], true)) {
            $user = get_userdata($user_id);
            if ($user && !empty($user->user_login)) {
                return (string) $user->user_login;
            }
        }

        return $resolved_username;
    }

    private function getUserEditUrl(int $user_id): string
    {
        if ($user_id <= 0) {
            return '';
        }

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

        return (string) admin_url('user-edit.php?user_id=' . $user_id);
    }

    private function getTargetAvatarUrl(array $target): string
    {
        $user_id = isset($target['user_id']) ? (int) $target['user_id'] : 0;
        if ($user_id > 0) {
            $user = get_userdata($user_id);
            $user_email = ($user instanceof \WP_User) ? sanitize_email((string) $user->user_email) : '';

            if ($user_email !== '') {
                $hash = md5(strtolower(trim($user_email)));
                return 'https://secure.gravatar.com/avatar/' . $hash . '?s=48&d=mm&r=g';
            }
        }

        return plugins_url('admin/images/user.svg', dirname(__DIR__, 3) . '/griffinforms.php');
    }

    private function buildAdminActorContext(string $action_type): array
    {
        $user_id = get_current_user_id();
        $user = $user_id > 0 ? get_userdata($user_id) : null;
        $actor_label = '';

        if ($user instanceof \WP_User) {
            $actor_label = (string) ($user->display_name ?: $user->user_login);
        }

        $action_labels = [
            Contract::ACTION_CREATE_NOW => __('Create Account Now', 'griffinforms-form-builder'),
            Contract::ACTION_ACTIVATE_PENDING => __('Activate Pending Account', 'griffinforms-form-builder'),
            Contract::ACTION_RETRY_FAILED => __('Retry Failed Action', 'griffinforms-form-builder'),
            Contract::ACTION_SEND_CONFIRMATION_NOW => __('Send Confirmation Now', 'griffinforms-form-builder'),
            Contract::ACTION_MARK_NO_ACTION => __('Mark No Action', 'griffinforms-form-builder'),
        ];

        return [
            'admin_action_type' => sanitize_key($action_type),
            'admin_action_label' => (string) ($action_labels[$action_type] ?? $action_type),
            'admin_actor_user_id' => $user_id,
            'admin_actor_label' => $actor_label,
            'admin_action_at' => gmdate('c'),
        ];
    }

    private function getAdminActionSummary(array $target): array
    {
        $action_label = (string) ($target['admin_action_label'] ?? '');
        $actor_label = (string) ($target['admin_actor_label'] ?? '');
        $actor_id = isset($target['admin_actor_user_id']) ? (int) $target['admin_actor_user_id'] : 0;
        $action_at = $this->formatDateTime((string) ($target['admin_action_at'] ?? ''));

        if ($action_label === '' && $actor_label === '' && $actor_id <= 0 && $action_at === '') {
            return [];
        }

        $value = $action_label;
        if ($actor_label !== '') {
            $value .= ' ' . sprintf(__('by %s', 'griffinforms-form-builder'), $actor_label . ($actor_id > 0 ? ' (#' . $actor_id . ')' : ''));
        }

        return [
            'label' => __('Admin action', 'griffinforms-form-builder'),
            'value' => trim($value),
            'timestamp' => $action_at,
        ];
    }

    private function getEmailReasonLabel(string $reason): string
    {
        $reason = sanitize_key($reason);
        if ($reason === '') {
            return '';
        }

        $labels = [
            'awaiting_account_creation' => __('Waiting for account creation', 'griffinforms-form-builder'),
            'awaiting_admin_activation' => __('Waiting for admin activation', 'griffinforms-form-builder'),
            'awaiting_manual_review' => __('Waiting for manual review', 'griffinforms-form-builder'),
            'sent_after_account_creation' => __('Sent after account creation', 'griffinforms-form-builder'),
            'sent_on_admin_request' => __('Sent on admin request', 'griffinforms-form-builder'),
            'existing_account_updated' => __('Skipped because the existing account was updated', 'griffinforms-form-builder'),
            'existing_account_noop' => __('Skipped because the existing account was left unchanged', 'griffinforms-form-builder'),
            'existing_account_created' => __('Skipped because the account already existed', 'griffinforms-form-builder'),
            'admin_marked_no_action' => __('Skipped by admin choice', 'griffinforms-form-builder'),
            'mail_send_failed' => __('Email delivery failed', 'griffinforms-form-builder'),
            'native_password_setup_email_not_sent' => __('WordPress did not confirm password setup email delivery', 'griffinforms-form-builder'),
            'missing_password_value' => __('Blocked because the password value was missing', 'griffinforms-form-builder'),
            'missing_confirmation_message' => __('No confirmation message was selected', 'griffinforms-form-builder'),
            'confirmation_message_missing' => __('The confirmation message is no longer available', 'griffinforms-form-builder'),
            'empty_confirmation_message' => __('The confirmation message is empty', 'griffinforms-form-builder'),
        ];

        if (isset($labels[$reason])) {
            return $labels[$reason];
        }

        return ucwords(str_replace('_', ' ', $reason));
    }

    private function formatDateTime(string $sent_at): string
    {
        $sent_at = trim($sent_at);
        if ($sent_at === '') {
            return '';
        }

        $timestamp = strtotime($sent_at);
        if ($timestamp === false) {
            return $sent_at;
        }

        return wp_date(get_option('date_format') . ' ' . get_option('time_format'), $timestamp);
    }

    private function mapRuntimeIssuesForView(array $issues): array
    {
        $mapped = [];

        foreach ($issues as $issue_code) {
            $issue_code = sanitize_key((string) $issue_code);
            if ($issue_code === '') {
                continue;
            }

            $mapped[] = array(
                'code' => $issue_code,
                'message' => $this->getRuntimeIssueLabel($issue_code),
            );
        }

        return $mapped;
    }

    private function getRuntimeIssueLabel(string $issue_code): string
    {
        $labels = array(
            'missing_mapped_password_value' => __('The mapped password field did not contain a usable value for this target.', 'griffinforms-form-builder'),
            'duplicate_target_email' => __('This submission produced the same account email more than once.', 'griffinforms-form-builder'),
            'missing_mapped_first_name_value' => __('The mapped first name value was missing for this target.', 'griffinforms-form-builder'),
            'missing_mapped_last_name_value' => __('The mapped last name value was missing for this target.', 'griffinforms-form-builder'),
            'missing_mapped_username_value' => __('The mapped username value was missing for this target.', 'griffinforms-form-builder'),
            'missing_mapped_profile_image_value' => __('The mapped profile image value was missing for this target.', 'griffinforms-form-builder'),
            'username_not_changed_on_existing_user_update' => __('Username could not be changed for the existing user. WordPress keeps the original username.', 'griffinforms-form-builder'),
        );

        if (isset($labels[$issue_code])) {
            return $labels[$issue_code];
        }

        return ucwords(str_replace('_', ' ', $issue_code));
    }

    private function getAvailableActionsForTarget(array $target): array
    {
        $status = (string) ($target['status'] ?? '');
        $result = (string) ($target['result'] ?? '');
        $confirmation_status = (string) ($target['confirmation_email_status'] ?? '');
        $available = [];

        $supports_passwordless_execution = (string) ($target['password_mode'] ?? '') !== 'mapped_password';

        if ($status === Contract::STATUS_PENDING_ADMIN_ACTIVATION && $supports_passwordless_execution) {
            $available[Contract::ACTION_ACTIVATE_PENDING] = [
                'label' => __('Activate Pending Account', 'griffinforms-form-builder'),
            ];
        }

        if ($status === Contract::STATUS_MANUAL_REVIEW_ONLY && $supports_passwordless_execution) {
            $available[Contract::ACTION_CREATE_NOW] = [
                'label' => __('Create Account Now', 'griffinforms-form-builder'),
            ];
        }

        if ($status === Contract::STATUS_FAILED && $supports_passwordless_execution) {
            $available[Contract::ACTION_RETRY_FAILED] = [
                'label' => __('Retry Failed Action', 'griffinforms-form-builder'),
            ];
        }

        if (
            $status === Contract::STATUS_COMPLETED
            && $result === 'created'
            && in_array($confirmation_status, ['deferred', 'failed', 'blocked', 'skipped'], true)
        ) {
            $available[Contract::ACTION_SEND_CONFIRMATION_NOW] = [
                'label' => __('Send Confirmation Now', 'griffinforms-form-builder'),
            ];
        }

        if (in_array($status, [Contract::STATUS_PENDING_ADMIN_ACTIVATION, Contract::STATUS_MANUAL_REVIEW_ONLY, Contract::STATUS_FAILED], true)) {
            $available[Contract::ACTION_MARK_NO_ACTION] = [
                'label' => __('Mark No Action', 'griffinforms-form-builder'),
            ];
        }

        return $available;
    }

    public function executeAction(int $submission_id, string $action_id, string $action_type)
    {
        $validation = $this->validateActionRequest($submission_id, $action_id, $action_type);
        if (is_wp_error($validation)) {
            return $validation;
        }

        $target = isset($validation['target']) && is_array($validation['target']) ? $validation['target'] : [];
        $this->state_store->updateAction($submission_id, $action_id, $this->buildAdminActorContext($action_type));
        switch ($action_type) {
            case Contract::ACTION_CREATE_NOW:
                $this->logInfo($submission_id, sprintf('Admin requested Create Account Now for target %s.', (string) ($target['target_email'] ?? '')));
                return $this->normalizeExecutionResult(
                    $submission_id,
                    $action_id,
                    $this->executor->executeImmediateAction($submission_id, $action_id, $this->buildExecutionPayload($target), 'admin_create_now')
                );

            case Contract::ACTION_ACTIVATE_PENDING:
                $this->logInfo($submission_id, sprintf('Admin requested Activate Pending Account for target %s.', (string) ($target['target_email'] ?? '')));
                return $this->normalizeExecutionResult(
                    $submission_id,
                    $action_id,
                    $this->executor->executeImmediateAction($submission_id, $action_id, $this->buildExecutionPayload($target), 'admin_activate')
                );

            case Contract::ACTION_RETRY_FAILED:
                $this->logInfo($submission_id, sprintf('Admin requested retry for failed target %s.', (string) ($target['target_email'] ?? '')));
                $this->state_store->updateAction($submission_id, $action_id, [
                    'status' => Contract::STATUS_PROCESSING,
                    'status_reason' => 'retry_started',
                    'status_message' => 'Admin retry started for this account action.',
                ]);
                return $this->normalizeExecutionResult(
                    $submission_id,
                    $action_id,
                    $this->executor->executeImmediateAction($submission_id, $action_id, $this->buildExecutionPayload($target), 'admin_retry')
                );

            case Contract::ACTION_SEND_CONFIRMATION_NOW:
                $this->logInfo($submission_id, sprintf('Admin requested Send Confirmation Now for target %s.', (string) ($target['target_email'] ?? '')));
                return $this->executor->sendConfirmationNow($submission_id, $action_id, $this->buildExecutionPayload($target));

            case Contract::ACTION_MARK_NO_ACTION:
                $this->state_store->updateAction($submission_id, $action_id, [
                    'status' => Contract::STATUS_COMPLETED,
                    'status_reason' => 'admin_marked_no_action',
                    'status_message' => 'Admin marked this account action as no action required.',
                    'result' => 'accepted_noop',
                    'confirmation_email_status' => !empty($target['confirmation_email']['status']) && $target['confirmation_email']['status'] !== 'not_requested'
                        ? 'skipped'
                        : 'not_requested',
                    'confirmation_email_reason' => !empty($target['confirmation_email']['status']) && $target['confirmation_email']['status'] !== 'not_requested'
                        ? 'admin_marked_no_action'
                        : '',
                ]);
                $this->logInfo($submission_id, sprintf('Admin marked target %s as no action required.', (string) ($target['target_email'] ?? '')));
                return true;
        }

        return new \WP_Error('unsupported_account_action', __('This account action type is not supported.', 'griffinforms-form-builder'));
    }

    private function normalizeExecutionResult(int $submission_id, string $action_id, $result)
    {
        if (!is_wp_error($result)) {
            return $result;
        }

        if ($result->get_error_code() !== 'mail_send_failed') {
            return $result;
        }

        $action = $this->state_store->getAction($submission_id, $action_id);
        $status = (string) ($action['status'] ?? '');
        $action_result = (string) ($action['result'] ?? '');

        if ($status === Contract::STATUS_COMPLETED && $action_result === 'created') {
            return [
                'message' => __('Account was created, but the confirmation email could not be sent. Review mail settings or use Send Confirmation Now later.', 'griffinforms-form-builder'),
                'notice_variant' => 'warning',
            ];
        }

        return $result;
    }

    private function getTargetStatusLabel(string $status_key, string $result): string
    {
        if ($status_key === Contract::STATUS_COMPLETED) {
            if ($result === 'created') {
                return __('Created', 'griffinforms-form-builder');
            }

            if ($result === 'updated') {
                return __('Updated', 'griffinforms-form-builder');
            }

            if ($result === 'accepted_noop') {
                return __('Accepted (No Action)', 'griffinforms-form-builder');
            }
        }

        $map = [
            Contract::STATUS_QUEUED => __('Queued', 'griffinforms-form-builder'),
            Contract::STATUS_PROCESSING => __('Processing', 'griffinforms-form-builder'),
            Contract::STATUS_PENDING_ADMIN_ACTIVATION => __('Pending Admin Activation', 'griffinforms-form-builder'),
            Contract::STATUS_MANUAL_REVIEW_ONLY => __('Manual Review', 'griffinforms-form-builder'),
            Contract::STATUS_BLOCKED => __('Blocked', 'griffinforms-form-builder'),
            Contract::STATUS_FAILED => __('Failed', 'griffinforms-form-builder'),
            Contract::STATUS_COMPLETED => __('Completed', 'griffinforms-form-builder'),
        ];

        return isset($map[$status_key]) ? $map[$status_key] : __('Unknown', 'griffinforms-form-builder');
    }

    private function getEmailStatusLabel(string $status): string
    {
        if ($status === '') {
            return '';
        }

        $map = [
            'not_requested' => __('Not Requested', 'griffinforms-form-builder'),
            'pending_runtime' => __('Pending', 'griffinforms-form-builder'),
            'deferred' => __('Deferred', 'griffinforms-form-builder'),
            'sent' => __('Sent', 'griffinforms-form-builder'),
            'skipped' => __('Skipped', 'griffinforms-form-builder'),
            'failed' => __('Failed', 'griffinforms-form-builder'),
            'blocked' => __('Blocked', 'griffinforms-form-builder'),
        ];

        return isset($map[$status]) ? $map[$status] : __('Unknown', 'griffinforms-form-builder');
    }

    private function getCreationModeLabel(string $creation_mode): string
    {
        $map = [
            'immediate_create' => __('Immediate Create', 'griffinforms-form-builder'),
            'pending_admin_activation' => __('Pending Admin Activation', 'griffinforms-form-builder'),
            'manual_review_only' => __('Manual Review Only', 'griffinforms-form-builder'),
        ];

        return isset($map[$creation_mode]) ? $map[$creation_mode] : __('Unknown', 'griffinforms-form-builder');
    }

    private function getExistingAccountBehaviorLabel(string $behavior): string
    {
        $map = [
            'error' => __('Show Error', 'griffinforms-form-builder'),
            'update_user' => __('Update User', 'griffinforms-form-builder'),
            'accept_noop' => __('Accept Form / Do Nothing', 'griffinforms-form-builder'),
        ];

        return isset($map[$behavior]) ? $map[$behavior] : __('Unknown', 'griffinforms-form-builder');
    }

    private function buildExecutionPayload(array $target): array
    {
        return [
            'action_id' => (string) ($target['action_id'] ?? ''),
            'target_email' => (string) ($target['target_email'] ?? ''),
            'existing_account_behavior' => (string) ($target['existing_account_behavior'] ?? ''),
            'password_mode' => (string) ($target['password_mode'] ?? ''),
            'role' => (string) ($target['role'] ?? ''),
            'send_confirmation_email' => !empty($target['send_confirmation_email']),
            'confirmation_message_id' => isset($target['confirmation_message_id']) ? (int) ($target['confirmation_message_id']) : 0,
            'resolved_username' => (string) ($target['resolved_username'] ?? ''),
            'resolved_first_name' => (string) ($target['resolved_first_name'] ?? ''),
            'resolved_last_name' => (string) ($target['resolved_last_name'] ?? ''),
            'resolved_profile_image' => isset($target['resolved_profile_image']) && is_array($target['resolved_profile_image'])
                ? $target['resolved_profile_image']
                : [],
            'runtime_issues' => isset($target['runtime_issues']) && is_array($target['runtime_issues'])
                ? $target['runtime_issues']
                : [],
            'user_id' => isset($target['user_id']) ? (int) ($target['user_id']) : 0,
        ];
    }

    private function logInfo(int $submission_id, string $message): void
    {
        if (!$this->log) {
            return;
        }

        $this->log->add(
            'info',
            'submission',
            $submission_id,
            get_current_user_id(),
            $message,
            Contract::LOG_CATEGORY
        );
    }
}
