<?php

namespace SoftminalB2B\Http\Controllers;

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

use SoftminalB2B\Models\Group;
use SoftminalB2B\Registration\Applicant;
use SoftminalB2B\Services\Settings;

/**
 * The approval queue (FEATURES C2). Approve or reject one applicant or many,
 * because a shop that opens registration publicly gets batches, not singles.
 */
class RegistrationsController extends Controller
{
    public function index(\WP_REST_Request $request, $params = [])
    {
        $status  = sanitize_key((string) $request->get_param('status')) ?: Applicant::STATUS_PENDING;
        $page    = max(1, (int) $request->get_param('page'));
        $perPage = min(100, max(1, (int) ($request->get_param('per_page') ?: 20)));
        $search  = sanitize_text_field((string) $request->get_param('search'));

        $allowed = [Applicant::STATUS_PENDING, Applicant::STATUS_APPROVED, Applicant::STATUS_REJECTED];

        if (!in_array($status, $allowed, true)) {
            $status = Applicant::STATUS_PENDING;
        }

        $args = [
            'meta_key'   => Applicant::META_STATUS, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
            'meta_value' => $status, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
            'number'     => $perPage,
            'paged'      => $page,
            'orderby'    => 'registered',
            'order'      => 'DESC',
        ];

        if ($search !== '') {
            $args['search']         = '*' . $search . '*';
            $args['search_columns'] = ['user_login', 'user_email', 'display_name'];
        }

        $query = new \WP_User_Query($args);

        // Resolve group names once for the page rather than a Group::find() per
        // row inside present().
        $groupNames = $this->groupNames();

        return $this->success([
            'applicants'    => array_map(
                fn (\WP_User $user) => $this->present($user, $groupNames),
                $query->get_results()
            ),
            'total'         => (int) $query->get_total(),
            'page'          => $page,
            'per_page'      => $perPage,
            'status'        => $status,
            'pending_count' => Applicant::countPending(),
            'fields'        => $this->fieldLabels(),
            // The group an approval should default to — the one new applicants
            // are configured to join — so the approve dialog pre-selects it
            // rather than whichever group happens to sort first.
            'default_group' => (int) Settings::get('registration_default_group'),
        ]);
    }

    public function approve(\WP_REST_Request $request, $params = [])
    {
        $groupId = $request->get_param('group_id');
        $groupId = ($groupId === null || $groupId === '' || (int) $groupId === 0) ? null : (int) $groupId;

        if ($groupId !== null && !Group::find($groupId)) {
            return $this->notFound(__('That customer group no longer exists.', 'softminal-b2b-wholesale-for-woocommerce'));
        }

        $userIds  = $this->userIds($request, $params);
        $approved = 0;

        foreach ($userIds as $userId) {
            // Only act on applicants who are actually waiting, so a repeated
            // click cannot re-send a welcome email to someone already approved.
            if (!Applicant::isPending($userId)) {
                continue;
            }

            if (Applicant::approve($userId, $groupId)) {
                $approved++;
            }
        }

        if ($approved === 0) {
            return $this->error(__('Nothing to approve — those applications have already been decided.', 'softminal-b2b-wholesale-for-woocommerce'), 422);
        }

        return $this->success(
            ['approved' => $approved, 'pending_count' => Applicant::countPending()],
            sprintf(
                /* translators: %d: number of applicants */
                _n('%d application approved. They have been emailed.', '%d applications approved. They have been emailed.', $approved, 'softminal-b2b-wholesale-for-woocommerce'),
                $approved
            )
        );
    }

    public function reject(\WP_REST_Request $request, $params = [])
    {
        $reason   = sanitize_textarea_field((string) $request->get_param('reason'));
        $userIds  = $this->userIds($request, $params);
        $rejected = 0;

        foreach ($userIds as $userId) {
            if (!Applicant::isPending($userId)) {
                continue;
            }

            if (Applicant::reject($userId, $reason)) {
                $rejected++;
            }
        }

        if ($rejected === 0) {
            return $this->error(__('Nothing to reject — those applications have already been decided.', 'softminal-b2b-wholesale-for-woocommerce'), 422);
        }

        return $this->success(
            ['rejected' => $rejected, 'pending_count' => Applicant::countPending()],
            sprintf(
                /* translators: %d: number of applicants */
                _n('%d application rejected.', '%d applications rejected.', $rejected, 'softminal-b2b-wholesale-for-woocommerce'),
                $rejected
            )
        );
    }

    /**
     * Accepts either a single id in the route or a list in the body, so one
     * handler serves the row buttons and the bulk action.
     *
     * @return array<int, int>
     */
    private function userIds(\WP_REST_Request $request, array $params): array
    {
        $single = (int) ($params['id'] ?? 0);

        if ($single > 0) {
            return [$single];
        }

        return array_values(array_filter(array_map('absint', (array) $request->get_param('user_ids'))));
    }

    /**
     * @return array<string, string>
     */
    private function fieldLabels(): array
    {
        $labels = [];

        foreach (Applicant::fields() as $key => $field) {
            $labels[$key] = $field['label'];
        }

        return $labels;
    }

    /**
     * Group id => name, built once per request so present() resolves a name
     * from memory instead of a query per applicant row.
     *
     * @return array<int, string>
     */
    private function groupNames(): array
    {
        $names = [];

        foreach (Group::all() as $group) {
            $names[(int) $group->id] = (string) $group->name;
        }

        return $names;
    }

    /**
     * @param array<int, string> $groupNames group id => name
     * @return array<string, mixed>
     */
    private function present(\WP_User $user, array $groupNames = []): array
    {
        $groupId = (int) get_user_meta($user->ID, Group::USER_META_KEY, true);

        return [
            'id'         => (int) $user->ID,
            'name'       => $user->display_name,
            'email'      => $user->user_email,
            'status'     => Applicant::statusOf((int) $user->ID),
            'business'   => Applicant::business((int) $user->ID),
            'applied_at' => (string) get_user_meta($user->ID, Applicant::META_APPLIED, true),
            'decided_at' => (string) get_user_meta($user->ID, Applicant::META_DECIDED, true),
            'reason'     => (string) get_user_meta($user->ID, Applicant::META_REASON, true),
            'group_id'   => $groupId ?: null,
            'group_name' => $groupId ? ($groupNames[$groupId] ?? null) : null,
        ];
    }
}
