<?php

namespace EasyLinkAutomations\Actions\Rapidmail;

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

/**
 * The rapidmail recipient schema and payload construction.
 *
 * rapidmail uses a fixed recipient schema rather than arbitrary custom
 * attributes: named fields plus ten `extra` and ten `extrabig` slots that
 * each account maps to its own meaning.
 */
final class RapidmailRecipientHelper
{
    /**
     * Writable recipient fields, in the order shown to the user.
     *
     * Read-only fields (id, created, updated, activated) are deliberately
     * absent so a mapping can never try to write them.
     *
     * @var array<string, string>
     */
    private const FIELDS = [
        'firstname'    => 'First Name',
        'lastname'     => 'Last Name',
        'gender'       => 'Gender (male or female)',
        'title'        => 'Title (academic)',
        'zip'          => 'ZIP Code',
        'birthdate'    => 'Birthdate (YYYY-MM-DD)',
        'foreign_id'   => 'Foreign ID',
        'mailtype'     => 'Mail Type (text or html)',
        'created_ip'   => 'Signup IP Address',
        'created_host' => 'Signup Hostname',
    ];

    /**
     * Values rapidmail accepts for the constrained fields.
     *
     * @var array<string, array<string>>
     */
    private const ENUMS = [
        'gender'   => ['male', 'female'],
        'mailtype' => ['text', 'html'],
    ];

    /**
     * Mapping columns for the recipient field widget.
     *
     * @return array<int, array{value: string, label: string, required?: bool}>
     */
    public static function columns(): array
    {
        $columns = [
            [
                'value'    => 'email',
                'label'    => 'Email',
                'required' => true,
            ],
        ];

        foreach (self::FIELDS as $key => $label) {
            $columns[] = ['value' => $key, 'label' => $label];
        }

        for ($i = 1; $i <= 10; $i++) {
            $columns[] = ['value' => 'extra' . $i, 'label' => 'Extra ' . $i];
        }
        for ($i = 1; $i <= 10; $i++) {
            $columns[] = ['value' => 'extrabig' . $i, 'label' => 'Extra Big ' . $i];
        }

        return $columns;
    }

    /**
     * Every writable field key, used to reject unknown mapping entries.
     *
     * @return array<string>
     */
    public static function writableKeys(): array
    {
        $keys = array_merge(['email'], array_keys(self::FIELDS));

        for ($i = 1; $i <= 10; $i++) {
            $keys[] = 'extra' . $i;
            $keys[] = 'extrabig' . $i;
        }

        return $keys;
    }

    /**
     * Decode a field_mapping config value into an array.
     */
    public static function decodeMapping(mixed $value): array
    {
        if (is_string($value)) {
            $value = json_decode($value, true);
        }

        return is_array($value) ? $value : [];
    }

    /**
     * Build the recipient payload from a decoded field mapping.
     *
     * Empty values are dropped so an unmapped field never overwrites an
     * existing rapidmail value with a blank string.
     */
    public static function buildPayload(array $mapping): array
    {
        $writable = array_flip(self::writableKeys());
        $payload  = [];

        foreach ($mapping as $key => $value) {
            if (!isset($writable[$key]) || is_array($value)) {
                continue;
            }

            $value = is_string($value) ? trim($value) : $value;
            if ($value === '' || $value === null) {
                continue;
            }

            $payload[$key] = $value;
        }

        return $payload;
    }

    /**
     * Validate the constrained fields before the request is sent.
     *
     * @return string Empty when valid, otherwise the reason.
     */
    public static function validate(array $payload): string
    {
        foreach (self::ENUMS as $key => $allowed) {
            if (!isset($payload[$key])) {
                continue;
            }

            $value = strtolower((string) $payload[$key]);
            if (!in_array($value, $allowed, true)) {
                return sprintf(
                    '%s must be one of: %s (got "%s")',
                    $key,
                    implode(', ', $allowed),
                    (string) $payload[$key]
                );
            }
        }

        if (isset($payload['birthdate'])) {
            $birthdate = (string) $payload['birthdate'];
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $birthdate)) {
                return 'birthdate must use the YYYY-MM-DD format (got "' . $birthdate . '")';
            }
        }

        return '';
    }

    /**
     * Normalise the enum fields rapidmail matches case-sensitively.
     */
    public static function normalize(array $payload): array
    {
        foreach (array_keys(self::ENUMS) as $key) {
            if (isset($payload[$key])) {
                $payload[$key] = strtolower((string) $payload[$key]);
            }
        }

        return $payload;
    }

    /**
     * Parse a comma-separated tag list.
     *
     * @return array<string>
     */
    public static function parseTags(mixed $value): array
    {
        $parts = is_array($value) ? $value : explode(',', (string) $value);

        $tags = [];
        foreach ($parts as $part) {
            $tag = trim((string) $part);
            if ($tag !== '') {
                $tags[] = $tag;
            }
        }

        return array_values(array_unique($tags));
    }
}
