<?php
/**
 * The request register in the WordPress admin.
 *
 * @package SkyServers\Retragere
 */

declare(strict_types=1);

namespace SkyServers\Retragere\Wp\Admin;

use SkyServers\Retragere\Wp\Labels;
use SkyServers\Retragere\Wp\PostType;
use SkyServers\Retragere\Wp\Repository;
use WP_Post;

/**
 * Uses the native post list table with custom columns. The wording is
 * deliberate: we record that a message was handed to the mail system, which is
 * the only thing WordPress can honestly attest to.
 */
final class Registry
{
    public static function register(): void
    {
        add_filter('manage_' . PostType::NAME . '_posts_columns', [self::class, 'columns']);
        add_action('manage_' . PostType::NAME . '_posts_custom_column', [self::class, 'column'], 10, 2);
        add_action('add_meta_boxes', [self::class, 'metaBox']);
        add_filter('display_post_states', [self::class, 'postStates'], 10, 2);
        add_filter('bulk_actions-edit-' . PostType::NAME, [self::class, 'bulkActions']);
        add_filter('handle_bulk_actions-edit-' . PostType::NAME, [self::class, 'handleBulk'], 10, 3);
        add_action('admin_notices', [self::class, 'bulkNotice']);
    }

    /**
     * @param array<string,string> $columns
     *
     * @return array<string,string>
     */
    public static function columns(array $columns): array
    {
        return [
            'cb'        => $columns['cb'] ?? '',
            'title'     => __('Reference', 'skyservers-retragere-contract'),
            'ssrc_who'  => __('Customer', 'skyservers-retragere-contract'),
            'ssrc_ref'  => __('Order / contract', 'skyservers-retragere-contract'),
            'ssrc_mail' => __('Acknowledgement', 'skyservers-retragere-contract'),
            'date'      => __('Received at', 'skyservers-retragere-contract'),
        ];
    }

    public static function column(string $column, int $postId): void
    {
        if ('ssrc_who' === $column) {
            printf(
                '%s<br><span class="description">%s</span>',
                esc_html((string) get_post_meta($postId, Repository::META_NAME, true)),
                esc_html((string) get_post_meta($postId, Repository::META_EMAIL, true))
            );

            $b2b = (string) get_post_meta($postId, Repository::META_B2B, true);

            // Only shown when it is a company order. Printing "consumer" on
            // every other row would be noise on the screen used most often.
            if ('' !== $b2b && 'none' !== $b2b) {
                printf(
                    '<br><span style="display:inline-block;margin-top:.25em;padding:.1em .5em;border-radius:3px;background:#f0f0f1;font-size:.85em;">%s</span>',
                    esc_html__('company order', 'skyservers-retragere-contract')
                );
            }

            return;
        }

        if ('ssrc_ref' === $column) {
            echo esc_html((string) get_post_meta($postId, Repository::META_REF, true));

            return;
        }

        if ('ssrc_mail' === $column) {
            $sent = (string) get_post_meta($postId, Repository::META_MAIL_CLIENT, true);

            if ('' === $sent) {
                printf(
                    '<span style="color:#b32d2e;">%s</span>',
                    esc_html__('not handed over', 'skyservers-retragere-contract')
                );

                return;
            }

            printf(
                '%s<br><span class="description">%s</span>',
                esc_html__('handed to the mail system', 'skyservers-retragere-contract'),
                esc_html($sent . ' UTC')
            );
        }
    }

    /**
     * Marks the state beside the reference in the list.
     *
     * Only for records that moved on from "new". Labelling the common case adds
     * a word to every row and tells the reader nothing.
     *
     * @param array<string,string> $states
     * @param mixed                $post
     *
     * @return array<string,string>
     */
    public static function postStates(array $states, $post): array
    {
        if (!$post instanceof WP_Post || PostType::NAME !== $post->post_type) {
            return $states;
        }

        if (PostType::STATUS_NOUA === $post->post_status) {
            return $states;
        }

        $labels = PostType::statusLabels();

        if (isset($labels[$post->post_status])) {
            $states[$post->post_status] = $labels[$post->post_status];
        }

        return $states;
    }

/**
     * The register has a "refunded" state but WordPress puts no custom status in
     * its publish box, so without this the state could be read and counted but
     * never set. Settling several at once is also the shape of the real job:
     * a batch of transfers goes out, then the register catches up.
     *
     * @param array<string,string> $actions
     *
     * @return array<string,string>
     */
    public static function bulkActions(array $actions): array
    {
        $actions['ssrc_settle'] = __('Mark as refunded', 'skyservers-retragere-contract');

        return $actions;
    }

    /**
     * @param string     $redirect
     * @param string     $action
     * @param array<int> $ids
     *
     * @return string
     */
    public static function handleBulk($redirect, $action, $ids): string
    {
        if ('ssrc_settle' !== $action) {
            return (string) $redirect;
        }

        // WordPress verifies the bulk nonce before handing the action over, but
        // an authorisation that is only implied is one a reader has to take on
        // trust.
        check_admin_referer('bulk-posts');

        if (!current_user_can('edit_posts')) {
            return (string) $redirect;
        }

        $done = 0;

        foreach ((array) $ids as $id) {
            $id = (int) $id;

            if (!current_user_can('edit_post', $id)) {
                continue;
            }

            if (PostType::STATUS_RAMBURSATA === get_post_status($id)) {
                continue;
            }

            wp_update_post(
                [
                    'ID'          => $id,
                    'post_status' => PostType::STATUS_RAMBURSATA,
                ]
            );

            ++$done;
        }

        return add_query_arg('ssrc_settled', $done, (string) $redirect);
    }

    public static function bulkNotice(): void
    {
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only count for a notice.
        if (!isset($_GET['ssrc_settled'])) {
            return;
        }

        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only count for a notice.
        $done = (int) $_GET['ssrc_settled'];

        printf(
            '<div class="notice notice-success is-dismissible"><p>%s</p></div>',
            esc_html(
                sprintf(
                    /* translators: %d: number of requests marked as refunded. */
                    _n(
                        '%d request marked as refunded.',
                        '%d requests marked as refunded.',
                        $done,
                        'skyservers-retragere-contract'
                    ),
                    $done
                )
            )
        );
    }

    public static function metaBox(): void
    {
        add_meta_box(
            'ssrc-detail',
            __('Request details', 'skyservers-retragere-contract'),
            [self::class, 'renderDetail'],
            PostType::NAME,
            'normal',
            'high'
        );
    }

    /**
     * @param WP_Post $post
     */
    public static function renderDetail($post): void
    {
        $rows = [
            __('State', 'skyservers-retragere-contract')                  => Labels::status((string) $post->post_status),
            __('Name', 'skyservers-retragere-contract')                   => (string) get_post_meta($post->ID, Repository::META_NAME, true),
            __('Email', 'skyservers-retragere-contract')                  => (string) get_post_meta($post->ID, Repository::META_EMAIL, true),
            __('Order / contract', 'skyservers-retragere-contract')     => (string) get_post_meta($post->ID, Repository::META_REF, true),
            __('Type', 'skyservers-retragere-contract')                    => Labels::type((string) get_post_meta($post->ID, Repository::META_TYPE, true)),
            __('Company order', 'skyservers-retragere-contract')       => Labels::b2b((string) get_post_meta($post->ID, Repository::META_B2B, true)),
            __('Reason', 'skyservers-retragere-contract')                  => (string) get_post_meta($post->ID, Repository::META_REASON, true),
            __('IP address', 'skyservers-retragere-contract')              => (string) get_post_meta($post->ID, Repository::META_IP, true),
            __('Mail transport', 'skyservers-retragere-contract')        => (string) get_post_meta($post->ID, Repository::META_TRANSPORT, true),
            __('Send attempts', 'skyservers-retragere-contract') => (string) Repository::attempts((int) $post->ID),
        ];

        echo '<table class="widefat striped"><tbody>';
        foreach ($rows as $label => $value) {
            printf(
                '<tr><th style="width:220px;">%s</th><td>%s</td></tr>',
                esc_html((string) $label),
                esc_html($value)
            );
        }
        echo '</tbody></table>';

        printf(
            '<h4>%s</h4><pre style="white-space:pre-wrap;background:#f6f7f7;padding:12px;">%s</pre>',
            esc_html__('The declaration as it was submitted', 'skyservers-retragere-contract'),
            esc_html((string) $post->post_content)
        );
    }
}
