<?php

declare(strict_types=1);

namespace App\Services;

use App\Enums\MembershipStatus;
use Illuminate\Support\Facades\Validator;

/**
 * Normalizes and validates a single import CSV row against the same rule
 * set as the manual-create API path (MemberRules), so the two write paths
 * can't drift per docs/01-architecture.md.
 */
class ImportRowValidator
{
    /**
     * @param  array<string, mixed>  $row
     * @param  array<string, int>  $branchCodeMap  code => branch_id
     * @return array{ok: bool, attributes?: array<string, mixed>, error?: string, branch_id?: int}
     */
    public function validate(array $row, ?int $fixedBranchId, array $branchCodeMap, ?string $defaultStatus): array
    {
        $branchResolution = $this->resolveBranch($row, $fixedBranchId, $branchCodeMap);

        if (! $branchResolution['ok']) {
            return ['ok' => false, 'error' => $branchResolution['error']];
        }

        $normalized = $this->normalize($row, $defaultStatus);

        $validator = Validator::make($normalized, MemberRules::forCreate());

        if ($validator->fails()) {
            return ['ok' => false, 'error' => $this->firstErrorMessage($validator)];
        }

        return [
            'ok' => true,
            'attributes' => $validator->validated(),
            'branch_id' => $branchResolution['branch_id'],
        ];
    }

    /**
     * @param  array<string, mixed>  $row
     * @param  array<string, int>  $branchCodeMap
     * @return array{ok: bool, branch_id?: int, error?: string}
     */
    private function resolveBranch(array $row, ?int $fixedBranchId, array $branchCodeMap): array
    {
        if ($fixedBranchId !== null) {
            return ['ok' => true, 'branch_id' => $fixedBranchId];
        }

        $code = trim((string) ($row['branch_code'] ?? ''));

        if ($code === '' || ! isset($branchCodeMap[$code])) {
            return ['ok' => false, 'error' => "Unknown or missing branch_code: '{$code}'."];
        }

        return ['ok' => true, 'branch_id' => $branchCodeMap[$code]];
    }

    /**
     * @param  array<string, mixed>  $row
     * @return array<string, mixed>
     */
    private function normalize(array $row, ?string $defaultStatus): array
    {
        $get = fn (string $key) => isset($row[$key]) && trim((string) $row[$key]) !== '' ? trim((string) $row[$key]) : null;

        return [
            'first_name' => $get('first_name'),
            'middle_name' => $get('middle_name'),
            'last_name' => $get('last_name'),
            'title' => $get('title'),
            'gender' => $get('gender') !== null ? strtolower((string) $get('gender')) : null,
            'date_of_birth' => $this->normalizeDate($get('date_of_birth')),
            'marital_status' => $get('marital_status') !== null ? strtolower((string) $get('marital_status')) : null,
            'phone_primary' => $get('phone_primary'),
            'phone_secondary' => $get('phone_secondary'),
            'email' => $get('email'),
            'address_line1' => $get('address_line1'),
            'address_line2' => $get('address_line2'),
            'city' => $get('city'),
            'state_province' => $get('state_province'),
            'postal_code' => $get('postal_code'),
            'country' => $get('country'),
            'membership_status' => ($get('membership_status') !== null ? strtolower((string) $get('membership_status')) : null)
                ?? $defaultStatus
                ?? MembershipStatus::Member->value,
            'date_joined' => $this->normalizeDate($get('date_joined')),
            'date_baptized' => $this->normalizeDate($get('date_baptized')),
            'occupation' => $get('occupation'),
        ];
    }

    private function normalizeDate(?string $value): ?string
    {
        if ($value === null) {
            return null;
        }

        if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
            return $value;
        }

        if (preg_match('#^(\d{1,2})/(\d{1,2})/(\d{4})$#', $value, $matches)) {
            [, $day, $month, $year] = $matches;

            return sprintf('%04d-%02d-%02d', (int) $year, (int) $month, (int) $day);
        }

        return $value;
    }

    private function firstErrorMessage(\Illuminate\Contracts\Validation\Validator $validator): string
    {
        $messages = $validator->errors()->all();

        return $messages[0] ?? 'Invalid row.';
    }
}
