<?php

namespace GriffinForms\Includes\Pipelines\Handlers;

use GriffinForms\Includes\Pipelines\Accounts\ActionStateStore;
use GriffinForms\Includes\Pipelines\Accounts\Contract;
use GriffinForms\Includes\Pipelines\Accounts\PasswordVault;
use GriffinForms\Includes\Pipelines\Accounts\SetupResolver;
use GriffinForms\Includes\Pipelines\JobRepository;
use GriffinForms\Includes\Pipelines\PostSubmissionHandlerInterface;
use GriffinForms\Log;

class AccountRegistrationHandler implements PostSubmissionHandlerInterface
{
    protected $repository;
    protected $resolver;
    protected $state_store;
    protected $password_vault;
    protected $log;

    public function __construct()
    {
        $this->repository = JobRepository::getInstance();
        $this->resolver = new SetupResolver();
        $this->state_store = new ActionStateStore();
        $this->password_vault = new PasswordVault();
        $this->log = Log::getInstance();
    }

    public function getId(): string
    {
        return Contract::INTEGRATION_ID;
    }

    public function getPriority(): int
    {
        return 90;
    }

    public function supports(array $context): bool
    {
        $is_enabled = apply_filters(Contract::ENABLE_HANDLER_FILTER, true, $context);
        if ($is_enabled !== true) {
            return false;
        }

        $form_id = isset($context['form_id']) ? (int) $context['form_id'] : 0;
        if ($form_id <= 0) {
            return false;
        }

        return $this->resolver->hasConfiguredSetups($form_id);
    }

    public function handle(array $context): void
    {
        $submission_id = isset($context['submission_id']) ? (int) $context['submission_id'] : 0;
        $form_id = isset($context['form_id']) ? (int) $context['form_id'] : 0;
        if ($submission_id <= 0 || $form_id <= 0) {
            return;
        }

        $accounts_state = $this->resolver->buildInitialAccountsState($context);
        if (empty($accounts_state)) {
            return;
        }

        $queue_enabled = apply_filters(Contract::ENABLE_QUEUE_FILTER, true, $context, $accounts_state) === true;

        if ($queue_enabled) {
            $accounts_state = $this->enqueueImmediateCreateJobs($accounts_state, $submission_id, $form_id, $context);
        }

        $this->state_store->saveAccountsState($submission_id, $accounts_state);
        $this->logResolvedTargets($submission_id, $accounts_state);

        if ($this->log) {
            $this->log->add(
                'info',
                'submission',
                $submission_id,
                get_current_user_id(),
                sprintf(
                    'WordPress account pipeline contract initialized for %d setup(s)%s.',
                    count($accounts_state['setups'] ?? []),
                    $queue_enabled ? '; immediate-create jobs queued where applicable' : '; queueing disabled until lifecycle execution is enabled'
                ),
                Contract::LOG_CATEGORY
            );
        }
    }

    private function enqueueImmediateCreateJobs(array $accounts_state, int $submission_id, int $form_id, array $context): array
    {
        if (empty($accounts_state['setups']) || !is_array($accounts_state['setups'])) {
            return $accounts_state;
        }

        $field_sequence_map = isset($context['account_meta']['field_sequence_map']) && is_array($context['account_meta']['field_sequence_map'])
            ? $context['account_meta']['field_sequence_map']
            : [];

        foreach ($accounts_state['setups'] as $setup_index => $setup) {
            if (empty($setup['targets']) || !is_array($setup['targets'])) {
                continue;
            }

            foreach ($setup['targets'] as $target_index => $target) {
                if (!is_array($target) || (string) ($target['status'] ?? '') !== Contract::STATUS_READY_FOR_QUEUE) {
                    continue;
                }

                $password_secret_ready = true;
                if ((string) ($target['password_mode'] ?? '') === 'mapped_password') {
                    $resolved_password = $this->resolveMappedPasswordForQueue($setup, $target, $field_sequence_map);
                    if ($resolved_password === '' || !$this->password_vault->storeForAction((string) ($target['action_id'] ?? ''), $resolved_password)) {
                        $password_secret_ready = false;
                    }
                }

                if (!$password_secret_ready) {
                    $accounts_state['setups'][$setup_index]['targets'][$target_index]['status'] = Contract::STATUS_FAILED;
                    $accounts_state['setups'][$setup_index]['targets'][$target_index]['status_reason'] = 'password_secret_unavailable';
                    $accounts_state['setups'][$setup_index]['targets'][$target_index]['status_message'] = 'Mapped password could not be prepared for queued account processing.';
                    $accounts_state['setups'][$setup_index]['targets'][$target_index]['updated_at'] = gmdate('c');
                    continue;
                }

                $payload = [
                    'action_id' => (string) ($target['action_id'] ?? ''),
                    'setup_id' => (string) ($setup['setup_id'] ?? ''),
                    'target_index' => isset($target['target_index']) ? (int) $target['target_index'] : $target_index,
                    'target_email' => (string) ($target['target_email'] ?? ''),
                    'creation_mode' => (string) ($target['creation_mode'] ?? ''),
                    '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' => (int) ($target['confirmation_message_id'] ?? 0),
                    'email_field_is_iterable' => !empty($target['email_field_is_iterable']),
                    'resolved_username' => (string) ($target['resolved_username'] ?? ''),
                    'resolved_username_source' => (string) ($target['resolved_username_source'] ?? ''),
                    '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']
                        : [],
                    'contract_phase' => (string) ($accounts_state['contract_phase'] ?? 'phase_2'),
                ];

                $job_id = $this->repository->enqueue(
                    Contract::JOB_TYPE_PROCESS,
                    Contract::INTEGRATION_ID,
                    $submission_id,
                    $form_id,
                    $payload,
                    20
                );

                $accounts_state['setups'][$setup_index]['targets'][$target_index]['job_id'] = $job_id;
                $accounts_state['setups'][$setup_index]['targets'][$target_index]['status'] = $job_id > 0
                    ? Contract::STATUS_QUEUED
                    : Contract::STATUS_FAILED;
                $accounts_state['setups'][$setup_index]['targets'][$target_index]['status_reason'] = $job_id > 0
                    ? ''
                    : 'job_enqueue_failed';
                $accounts_state['setups'][$setup_index]['targets'][$target_index]['status_message'] = $job_id > 0
                    ? 'Account job queued.'
                    : 'Account job could not be queued.';
                $accounts_state['setups'][$setup_index]['targets'][$target_index]['updated_at'] = gmdate('c');
            }
        }

        $accounts_state['updated_at'] = gmdate('c');
        return $accounts_state;
    }

    private function logResolvedTargets(int $submission_id, array $accounts_state): void
    {
        if (!$this->log || empty($accounts_state['setups']) || !is_array($accounts_state['setups'])) {
            return;
        }

        foreach ($accounts_state['setups'] as $setup) {
            $setup_id = (string) ($setup['setup_id'] ?? '');
            if (empty($setup['targets']) || !is_array($setup['targets'])) {
                continue;
            }

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

                $status = (string) ($target['status'] ?? '');
                $target_email = (string) ($target['target_email'] ?? '');
                $target_label = $target_email !== '' ? $target_email : 'unresolved target';
                $message = '';
                $level = 'info';

                if ($status === Contract::STATUS_BLOCKED) {
                    $level = 'warning';
                    $message = sprintf(
                        'Account setup %s blocked target %s: %s',
                        $setup_id,
                        $target_label,
                        (string) ($target['status_message'] ?? 'Target could not be resolved.')
                    );
                } elseif ($status === Contract::STATUS_PENDING_ADMIN_ACTIVATION) {
                    $message = sprintf(
                        'Account setup %s kept target %s pending admin activation%s.',
                        $setup_id,
                        $target_label,
                        !empty($target['send_confirmation_email']) ? '; confirmation email deferred until admin activation' : ''
                    );
                } elseif ($status === Contract::STATUS_MANUAL_REVIEW_ONLY) {
                    $message = sprintf(
                        'Account setup %s kept target %s for manual review%s.',
                        $setup_id,
                        $target_label,
                        !empty($target['send_confirmation_email']) ? '; confirmation email deferred until manual account action' : ''
                    );
                } elseif ($status === Contract::STATUS_QUEUED) {
                    $message = sprintf(
                        'Account setup %s queued target %s for immediate processing.',
                        $setup_id,
                        $target_label
                    );
                } elseif ($status === Contract::STATUS_FAILED) {
                    $level = 'error';
                    $message = sprintf(
                        'Account setup %s failed to queue target %s: %s',
                        $setup_id,
                        $target_label,
                        (string) ($target['status_message'] ?? 'Job enqueue failed.')
                    );
                }

                if ($message !== '') {
                    $this->log->add(
                        $level,
                        'submission',
                        $submission_id,
                        get_current_user_id(),
                        $message,
                        Contract::LOG_CATEGORY
                    );
                }
            }
        }
    }

    private function resolveMappedPasswordForQueue(array $setup, array $target, array $field_sequence_map): string
    {
        $password_field_id = absint($setup['password_field_id'] ?? 0);
        if ($password_field_id <= 0) {
            return '';
        }

        $values = $field_sequence_map[$password_field_id] ?? $field_sequence_map[(string) $password_field_id] ?? [];
        if (!is_array($values) || empty($values)) {
            return '';
        }

        $target_index = isset($target['target_index']) ? (int) $target['target_index'] : 0;
        $value = isset($values[$target_index]) ? $values[$target_index] : $values[0];
        if (is_array($value)) {
            if (isset($value['value']) && is_scalar($value['value'])) {
                return trim((string) $value['value']);
            }

            return '';
        }

        return is_scalar($value) ? trim((string) $value) : '';
    }
}
