<?php

declare(strict_types=1);

namespace App\Services;

use App\Enums\Gender;
use App\Enums\HouseholdRole;
use App\Enums\MaritalStatus;
use App\Enums\MembershipStatus;
use Illuminate\Validation\Rule;

/**
 * Single source of truth for member field validation, shared by
 * StoreMemberRequest/UpdateMemberRequest and (Phase 7) the CSV import
 * pipeline, so the rules can't drift between the two write paths.
 */
class MemberRules
{
    /**
     * @return array<string, array<mixed>>
     */
    public static function forCreate(): array
    {
        return self::base(required: true);
    }

    /**
     * @return array<string, array<mixed>>
     */
    public static function forUpdate(): array
    {
        return self::base(required: false);
    }

    /**
     * @return array<string, array<mixed>>
     */
    private static function base(bool $required): array
    {
        $req = $required ? 'required' : 'sometimes';

        return [
            'first_name' => [$req, 'string', 'max:100'],
            'middle_name' => ['nullable', 'string', 'max:100'],
            'last_name' => [$req, 'string', 'max:100'],
            'preferred_name' => ['nullable', 'string', 'max:100'],
            'title' => ['nullable', 'string', 'max:20'],
            'gender' => [$req, Rule::enum(Gender::class)],
            'date_of_birth' => ['nullable', 'date', 'after:1900-01-01', 'before_or_equal:today'],
            'marital_status' => ['nullable', Rule::enum(MaritalStatus::class)],
            'email' => ['nullable', 'email', 'max:150'],
            'phone_primary' => ['nullable', self::phoneRule()],
            'phone_secondary' => ['nullable', self::phoneRule()],
            'address_line1' => ['nullable', 'string', 'max:150'],
            'address_line2' => ['nullable', 'string', 'max:150'],
            'city' => ['nullable', 'string', 'max:100'],
            'state_province' => ['nullable', 'string', 'max:100'],
            'postal_code' => ['nullable', 'string', 'max:20'],
            'country' => ['nullable', 'string', 'size:2'],
            'membership_status' => ['nullable', Rule::enum(MembershipStatus::class)],
            'date_joined' => ['nullable', 'date'],
            'date_baptized' => ['nullable', 'date'],
            'baptism_type' => ['nullable', 'string', 'max:30'],
            'occupation' => ['nullable', 'string', 'max:150'],
            'employer' => ['nullable', 'string', 'max:150'],
            'emergency_contact_name' => ['nullable', 'string', 'max:150'],
            'emergency_contact_phone' => ['nullable', 'string', 'max:30'],
            'emergency_contact_relationship' => ['nullable', 'string', 'max:50'],
            'custom_fields' => ['nullable', 'array', 'max:30', self::customFieldsSizeRule()],
            'custom_fields.*' => ['nullable', 'scalar'],
            'household_id' => ['nullable', 'integer', 'exists:households,id'],
            'household_role' => ['required_with:household_id', 'nullable', Rule::enum(HouseholdRole::class)],
        ];
    }

    private static function phoneRule(): \Closure
    {
        return function (string $attribute, mixed $value, \Closure $fail) {
            $digits = preg_replace('/\D+/', '', (string) $value);

            if (strlen($digits) < 6 || strlen($digits) > 15) {
                $fail("The {$attribute} must have between 6 and 15 digits.");
            }
        };
    }

    private static function customFieldsSizeRule(): \Closure
    {
        return function (string $attribute, mixed $value, \Closure $fail) {
            if (! is_array($value)) {
                return;
            }

            if (strlen(json_encode($value)) > 8192) {
                $fail('The custom fields payload must not exceed 8KB.');
            }
        };
    }
}
