<?php
namespace {
    if (!defined('WD')) {
        define('WD', dirname(__DIR__, 2));
    }

    global $response, $router, $DEBUG;
    $DEBUG = true;
    $_SERVER['REQUEST_URI'] = '/auth/register/cvr';
    $_SERVER['REQUEST_METHOD'] = 'POST';
}

namespace classes {
    class MockExitException extends \Error {}

    class response
    {
        public static mixed $last_success = null;
        public static mixed $last_error = null;
        public static ?int $last_status = null;
        public static array $request_parameters = [];

        public static function reset(): void
        {
            self::$last_success = null;
            self::$last_error = null;
            self::$last_status = null;
            self::$request_parameters = [];
        }

        public function success($data, $status = 200): void
        {
            self::$last_success = $data;
            self::$last_status = $status;
            throw new MockExitException('SUCCESS_EXIT');
        }

        public function error($data, $status = 400): void
        {
            self::$last_error = $data;
            self::$last_status = $status;
            throw new MockExitException('ERROR_EXIT');
        }

        public function getRequestParameter($key)
        {
            return self::$request_parameters[$key] ?? null;
        }

        public function isRequestParameterSet($key): bool
        {
            return array_key_exists($key, self::$request_parameters);
        }
    }
}

namespace classes {
    class recaptcha
    {
        public static bool $mock_valid = true;

        public function validate($response): bool
        {
            return self::$mock_valid;
        }
    }

    class economic
    {
        public static array $mock_collection = [];
        public static ?object $mock_create_response = null;
        public static ?\RuntimeException $mock_create_exception = null;
        public static array $mock_collection_after_create_exception = [];
        public static array $search_calls = [];
        public static array $create_calls = [];

        public object $customers;

        public static function reset(): void
        {
            self::$mock_collection = [];
            self::$mock_create_response = null;
            self::$mock_create_exception = null;
            self::$mock_collection_after_create_exception = [];
            self::$search_calls = [];
            self::$create_calls = [];
        }

        public function __construct()
        {
            $this->customers = new \stdClass();
            $this->customers->customers = new class {
                public function search($params, $options): object
                {
                    \classes\economic::$search_calls[] = [
                        'params' => $params,
                        'options' => $options,
                    ];

                    $mock = new \stdClass();
                    $mock->collection = \classes\economic::$mock_collection;

                    return $mock;
                }
            };
        }

        public static function normalizeCustomerEan(mixed $value): ?string
        {
            if ($value === null) {
                return null;
            }

            $digits = preg_replace('/\D+/', '', (string)$value);
            if (!is_string($digits)) {
                return null;
            }

            $digits = trim($digits);
            if ($digits === '') {
                return null;
            }

            if (strlen($digits) > 13) {
                throw new \InvalidArgumentException('EAN must be at most 13 digits.');
            }

            return $digits;
        }

        public function createCustomer($number, $name, $cvr, $email, $phone, $mobilePhone = null, $companyInformation = null, $ean = null): object
        {
            self::$create_calls[] = [
                'number' => (int)$number,
                'name' => (string)$name,
                'cvr' => (string)$cvr,
                'email' => (string)$email,
                'phone' => (int)$phone,
                'mobile_phone' => $mobilePhone === null ? null : (int)$mobilePhone,
                'company_information' => $companyInformation,
                'ean' => $ean === null ? null : (string)$ean,
            ];

            if (self::$mock_create_exception !== null) {
                self::$mock_collection = self::$mock_collection_after_create_exception;
                throw self::$mock_create_exception;
            }

            $response = self::$mock_create_response ?? (object)[
                'customerNumber' => (int)$number,
            ];

            if (isset($response->customerNumber) && is_numeric($response->customerNumber)) {
                \objects\users_o::$mock_importable_customer_numbers[] = (int)$response->customerNumber;
                \objects\users_o::$mock_importable_customer_numbers = array_values(array_unique(\objects\users_o::$mock_importable_customer_numbers));
            }

            return $response;
        }
    }

    class virkdata
    {
        public static string $mock_name = 'Mock Company';
        public static string $mock_address = 'Demo Street 1';
        public static int $mock_zipcode = 2630;
        public static string $mock_city = 'Taastrup';
        public static string $mock_website = 'https://demo.test';
        public static ?\RuntimeException $mock_exception = null;

        public function getCompanyInformation($cvr, $endpoint, $data): object
        {
            if (self::$mock_exception !== null) {
                throw self::$mock_exception;
            }

            $result = new \stdClass();
            $result->name = self::$mock_name;
            $result->address = self::$mock_address;
            $result->zipcode = self::$mock_zipcode;
            $result->city = self::$mock_city;
            $result->website = self::$mock_website;

            return $result;
        }
    }

    class email
    {
        public static array $sent = [];
        public static array $superuser_notifications = [];

        public static function reset(): void
        {
            self::$sent = [];
            self::$superuser_notifications = [];
        }

        public function sendWelcomeEmailToCustomer($phone, $email): bool
        {
            self::$sent[] = [
                'customer_number' => (int)$phone,
                'email' => (string)$email,
            ];
            \objects\users_o::$interaction_log[] = 'email:' . (int)$phone . ':' . (string)$email;

            return true;
        }

        public function sendNewCustomerRegistrationNotifications($phone): bool
        {
            self::$superuser_notifications[] = [
                'customer_number' => (int)$phone,
            ];
            \objects\users_o::$interaction_log[] = 'superuser-notification:' . (int)$phone;

            return true;
        }
    }

    class slack
    {
        public static array $customer_registration_notifications = [];

        public static function reset(): void
        {
            self::$customer_registration_notifications = [];
        }

        public function send_customer_registration_notification($customer_number): self
        {
            self::$customer_registration_notifications[] = [
                'customer_number' => (int)$customer_number,
            ];
            \objects\users_o::$interaction_log[] = 'slack-customer-registration:' . (int)$customer_number;

            return $this;
        }
    }

    class authentication
    {
        public function get_plate_scanner(): bool
        {
            return true;
        }
    }
}

namespace objects {
    class users_o
    {
        public static array $mock_existing_customer_numbers = [];
        public static array $mock_importable_customer_numbers = [];
        public static bool $mock_external_lookup_enabled = true;
        public static array $interaction_log = [];

        public int $id = 0;
        private bool $exists = false;

        public static function reset(): void
        {
            self::$mock_existing_customer_numbers = [];
            self::$mock_importable_customer_numbers = [];
            self::$mock_external_lookup_enabled = true;
            self::$interaction_log = [];
        }

        public function getFieldsWhere(array $fieldsAndValues, array $fields): array
        {
            $customerNumber = (int)($fieldsAndValues['customer_number'] ?? 0);
            if (in_array($customerNumber, self::$mock_existing_customer_numbers, true)) {
                return [['id' => $customerNumber]];
            }

            return [];
        }

        public function getUserByCustomerNumber($num): self
        {
            $customerNumber = (int)$num;
            self::$interaction_log[] = 'bootstrap:' . $customerNumber;

            $existsLocally = in_array($customerNumber, self::$mock_existing_customer_numbers, true);
            $canImport = self::$mock_external_lookup_enabled
                && in_array($customerNumber, self::$mock_importable_customer_numbers, true);

            if ($existsLocally || $canImport) {
                $this->id = $customerNumber;
                $this->exists = true;

                if (!$existsLocally) {
                    self::$mock_existing_customer_numbers[] = $customerNumber;
                    self::$mock_existing_customer_numbers = array_values(array_unique(self::$mock_existing_customer_numbers));
                }
            }

            return $this;
        }

        public function importCustomerFromEconomicCustomerData(object $customerData): self|bool
        {
            $customerNumber = (int)($customerData->customerNumber ?? 0);
            if ($customerNumber <= 0) {
                return false;
            }

            self::$interaction_log[] = 'snapshot-import:' . $customerNumber;
            $this->id = $customerNumber;
            $this->exists = true;
            self::$mock_existing_customer_numbers[] = $customerNumber;
            self::$mock_existing_customer_numbers = array_values(array_unique(self::$mock_existing_customer_numbers));

            return $this;
        }

        public function exists(): bool
        {
            return $this->exists;
        }
    }

    class logs_o
    {
        public static array $entries = [];

        public static function reset(): void
        {
            self::$entries = [];
        }

        public function add($module, $department, $type, $user_id, $event, $details): void
        {
            self::$entries[] = [
                'module' => (string)$module,
                'department' => (string)$department,
                'type' => (int)$type,
                'user_id' => (int)$user_id,
                'event' => (string)$event,
                'details' => (string)$details,
            ];
        }
    }

    class tokens_o
    {
        public function delete($token): void
        {
        }
    }

    class customer_password_reset_keys_o
    {
        public static function generateToken(): string
        {
            return 'mock_token';
        }

        public function add($data): void
        {
        }

        public function findValidByToken($token)
        {
            return null;
        }
    }
}

namespace {
    class MockRouter
    {
        public array $routes = [];

        public function add($route, $method, $callback, $permissions): void
        {
            $this->routes[$method][$route] = $callback;
        }
    }

    function ok(string $message): void
    {
        echo "\033[32m[PASS]\033[0m $message\n";
    }

    function fail(string $message): void
    {
        echo "\033[31m[FAIL]\033[0m $message\n";
    }

    function assert_true(bool $condition, string $message): void
    {
        if (!$condition) {
            throw new \RuntimeException($message);
        }
    }

    function assert_same_data(mixed $actual, mixed $expected, string $message): void
    {
        $normalize = static fn(mixed $value): string => (string)json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        if ($normalize($actual) !== $normalize($expected)) {
            throw new \RuntimeException($message . ' Expected ' . $normalize($expected) . ' but got ' . $normalize($actual));
        }
    }

    $router = new MockRouter();
    $response = new \classes\response();

    require_once WD . '/traits/route_t.php';
    require_once WD . '/routes/authRoute.php';

    $authRoute = new \routes\authRoute();
    $authRoute->run();

    if (!isset($router->routes['POST']['/auth/register/cvr'])) {
        die("Route /auth/register/cvr not found\n");
    }

    $callback = $router->routes['POST']['/auth/register/cvr'];
    $baseParams = [
        'cvr' => '12345678',
        'companyPhone' => 12345678,
        'invoiceEmail' => 'test@test.com',
        'contactEmail' => 'test@test.com',
        'contactPhone' => 12345678,
        'g_recaptcha_response' => 'valid',
    ];

    $testCases = [
        [
            'name' => 'Missing reCAPTCHA',
            'params' => [],
            'setup' => static function (): void {
                \classes\recaptcha::$mock_valid = false;
            },
            'expected_error' => 'Authentication failed. Invalid or missing reCAPTCHA.',
            'expected_status' => 401,
        ],
        [
            'name' => 'Missing parameters',
            'params' => ['g_recaptcha_response' => 'valid'],
            'expected_error' => 'Missing required parameters: cvr, companyPhone, invoiceEmail, contactEmail, contactPhone',
            'expected_status' => 400,
        ],
        [
            'name' => 'Invalid CVR length (too short)',
            'params' => array_merge($baseParams, ['cvr' => '123']),
            'expected_error' => 'Parameter cvr must be at least 8 characters long',
            'expected_status' => 400,
        ],
        [
            'name' => 'Invalid EAN length (too long)',
            'params' => array_merge($baseParams, ['ean' => '57900012345678']),
            'expected_error' => 'EAN must be at most 13 digits.',
            'expected_status' => 400,
            'assert' => static function (): void {
                assert_true(count(\classes\economic::$create_calls) === 0, 'Invalid EAN must not create e-conomic customers.');
                assert_true(count(\classes\email::$sent) === 0, 'Invalid EAN must not send welcome emails.');
            },
        ],
        [
            'name' => 'CVR lookup failure returns validation error without creating customer',
            'params' => array_merge($baseParams, ['cvr' => '11111112']),
            'setup' => static function (): void {
                \classes\virkdata::$mock_exception = new \RuntimeException('An error occurred');
            },
            'expected_error' => 'CVR could not be verified. Please check the CVR number and try again.',
            'expected_status' => 400,
            'assert' => static function (): void {
                assert_true(count(\classes\economic::$create_calls) === 0, 'CVR lookup failures must not create e-conomic customers.');
                assert_true(count(\classes\email::$sent) === 0, 'CVR lookup failures must not send welcome emails.');
                assert_true(count(\classes\email::$superuser_notifications) === 0, 'CVR lookup failures must not send superuser notifications.');
                assert_true(count(\classes\slack::$customer_registration_notifications) === 0, 'CVR lookup failures must not send Slack customer registration notifications.');
                assert_true(count(\objects\logs_o::$entries) === 1, 'CVR lookup failures should be logged for diagnostics.');
                assert_true(\objects\logs_o::$entries[0]['event'] === 'AUTH_REGISTER_CVR_LOOKUP_FAILED', 'CVR lookup failure should use the lookup failure log event.');
            },
        ],
        [
            'name' => 'CVR lookup without company name returns validation error without creating customer',
            'params' => array_merge($baseParams, ['cvr' => '11111112']),
            'setup' => static function (): void {
                \classes\virkdata::$mock_name = '';
            },
            'expected_error' => 'CVR could not be verified. Please check the CVR number and try again.',
            'expected_status' => 400,
            'assert' => static function (): void {
                assert_true(count(\classes\economic::$create_calls) === 0, 'CVR lookup responses without a company name must not create e-conomic customers.');
                assert_true(count(\classes\email::$sent) === 0, 'CVR lookup responses without a company name must not send welcome emails.');
                assert_true(count(\classes\email::$superuser_notifications) === 0, 'CVR lookup responses without a company name must not send superuser notifications.');
                assert_true(count(\classes\slack::$customer_registration_notifications) === 0, 'CVR lookup responses without a company name must not send Slack customer registration notifications.');
                assert_true(count(\objects\logs_o::$entries) === 1, 'CVR lookup responses without a company name should be logged for diagnostics.');
                assert_true(\objects\logs_o::$entries[0]['event'] === 'AUTH_REGISTER_CVR_LOOKUP_INVALID_RESPONSE', 'CVR lookup response without a company name should use the invalid response log event.');
            },
        ],
        [
            'name' => 'CVR lookup failure takes precedence over local customer number collision',
            'params' => array_merge($baseParams, ['cvr' => '11111112']),
            'setup' => static function (): void {
                \classes\virkdata::$mock_exception = new \RuntimeException('An error occurred');
                \objects\users_o::$mock_existing_customer_numbers = [12345678];
            },
            'expected_error' => 'CVR could not be verified. Please check the CVR number and try again.',
            'expected_status' => 400,
            'assert' => static function (): void {
                assert_true(count(\classes\economic::$create_calls) === 0, 'CVR lookup failures with local collisions must not create e-conomic customers.');
                assert_true(count(\classes\email::$sent) === 0, 'CVR lookup failures with local collisions must not send welcome emails.');
                assert_true(count(\classes\email::$superuser_notifications) === 0, 'CVR lookup failures with local collisions must not send superuser notifications.');
                assert_true(count(\classes\slack::$customer_registration_notifications) === 0, 'CVR lookup failures with local collisions must not send Slack customer registration notifications.');
                assert_true(count(\objects\logs_o::$entries) === 1, 'CVR lookup failures with local collisions should be logged once.');
                assert_true(\objects\logs_o::$entries[0]['event'] === 'AUTH_REGISTER_CVR_LOOKUP_FAILED', 'CVR lookup failure should not be masked by the local duplicate check.');
            },
        ],
        [
            'name' => 'Existing company phone with local customer stays blocked',
            'params' => $baseParams,
            'setup' => static function (): void {
                \objects\users_o::$mock_existing_customer_numbers = [12345678];
            },
            'expected_error' => 'Company phone number already registered',
            'expected_status' => 400,
            'assert' => static function (): void {
                assert_true(count(\classes\economic::$create_calls) === 0, 'Fresh create must not run for local duplicates.');
                assert_true(count(\classes\email::$sent) === 0, 'Duplicate registrations must not send welcome emails.');
                assert_true(count(\classes\email::$superuser_notifications) === 0, 'Duplicate registrations must not send superuser notifications.');
                assert_true(count(\classes\slack::$customer_registration_notifications) === 0, 'Duplicate registrations must not send Slack customer registration notifications.');
            },
        ],
        [
            'name' => 'Existing matching e-conomic customer recovers partial local registration',
            'params' => $baseParams,
            'setup' => static function (): void {
                \classes\economic::$mock_collection = [
                    (object)[
                        'customerNumber' => 12345678,
                        'name' => 'Recovered Company',
                    ],
                ];
                \objects\users_o::$mock_importable_customer_numbers = [12345678];
            },
            'expected_success' => (object)[
                'customerNumber' => 12345678,
                'name' => 'Recovered Company',
            ],
            'expected_status' => 200,
            'assert' => static function (): void {
                assert_true(count(\classes\economic::$create_calls) === 0, 'Recovery must not create a second e-conomic customer.');
                assert_true(count(\classes\email::$sent) === 2, 'Recovery must send two welcome emails.');
                assert_true(\objects\users_o::$interaction_log[0] === 'bootstrap:12345678', 'Local bootstrap must happen before sending emails.');
                assert_true(\classes\email::$sent[0]['email'] === 'jm@truckwash.dk', 'Recovery should notify the internal welcome recipient first.');
                assert_true(\classes\email::$sent[1]['email'] === 'test@test.com', 'Recovery should notify the invoice email.');
                assert_true(count(\classes\email::$superuser_notifications) === 1, 'Recovery must notify opted-in superusers once.');
                assert_true(\classes\email::$superuser_notifications[0]['customer_number'] === 12345678, 'Recovery superuser notification must use the recovered customer number.');
                assert_true(count(\classes\slack::$customer_registration_notifications) === 1, 'Recovery must notify Slack once.');
                assert_true(\classes\slack::$customer_registration_notifications[0]['customer_number'] === 12345678, 'Recovery Slack notification must use the recovered customer number.');
            },
        ],
        [
            'name' => 'Existing matching e-conomic customer with local user still returns duplicate',
            'params' => $baseParams,
            'setup' => static function (): void {
                \classes\economic::$mock_collection = [
                    (object)[
                        'customerNumber' => 12345678,
                        'name' => 'Recovered Company',
                    ],
                ];
                \objects\users_o::$mock_existing_customer_numbers = [12345678];
            },
            'expected_error' => 'Company phone number already registered',
            'expected_status' => 400,
            'assert' => static function (): void {
                assert_true(count(\classes\email::$sent) === 0, 'Duplicate recovery attempts must not send welcome emails.');
                assert_true(count(\classes\email::$superuser_notifications) === 0, 'Duplicate recovery attempts must not send superuser notifications.');
                assert_true(count(\classes\slack::$customer_registration_notifications) === 0, 'Duplicate recovery attempts must not send Slack customer registration notifications.');
            },
        ],
        [
            'name' => 'Existing mismatched e-conomic customer returns conflict',
            'params' => $baseParams,
            'setup' => static function (): void {
                \classes\economic::$mock_collection = [
                    (object)[
                        'customerNumber' => 87654321,
                        'name' => 'Wrong Number Company',
                    ],
                ];
            },
            'expected_error' => 'CVR already registered under customer number 87654321. The submitted phone number must match the customer id. Manual cleanup or reassignment is required before retrying.',
            'expected_status' => 409,
            'assert' => static function (): void {
                assert_true(count(\classes\economic::$create_calls) === 0, 'Conflict on existing mismatched customer must not trigger create.');
                assert_true(count(\classes\email::$sent) === 0, 'Conflict on existing mismatched customer must not send welcome emails.');
                assert_true(count(\classes\email::$superuser_notifications) === 0, 'Conflict on existing mismatched customer must not send superuser notifications.');
                assert_true(count(\classes\slack::$customer_registration_notifications) === 0, 'Conflict on existing mismatched customer must not send Slack customer registration notifications.');
                assert_true(count(\objects\logs_o::$entries) === 1, 'Conflict should be logged for manual cleanup.');
            },
        ],
        [
            'name' => 'Successful registration bootstraps local user before welcome emails',
            'params' => array_merge($baseParams, ['contactPhone' => 87654320, 'ean' => '57 90-001234567']),
            'setup' => static function (): void {
                \classes\economic::$mock_create_response = (object)[
                    'customerNumber' => 12345678,
                    'name' => 'Mock Company',
                ];
            },
            'expected_success' => (object)[
                'customerNumber' => 12345678,
                'name' => 'Mock Company',
            ],
            'expected_status' => 201,
            'assert' => static function (): void {
                assert_true(count(\classes\economic::$create_calls) === 1, 'Fresh registration must call create exactly once.');
                assert_true(\classes\economic::$create_calls[0]['phone'] === 12345678, 'Fresh registration must use the company phone as the e-conomic customer phone.');
                assert_true(\classes\economic::$create_calls[0]['mobile_phone'] === 87654320, 'Fresh registration must pass the contact phone as the e-conomic mobile phone.');
                assert_true(\classes\economic::$create_calls[0]['ean'] === '5790001234567', 'Fresh registration must pass normalized EAN to e-conomic.');
                $companyInformation = \classes\economic::$create_calls[0]['company_information'];
                assert_true(is_object($companyInformation), 'Fresh registration must pass CVR company information to e-conomic.');
                assert_true($companyInformation->address === 'Demo Street 1', 'Fresh registration must pass the CVR address to e-conomic.');
                assert_true($companyInformation->zipcode === 2630, 'Fresh registration must pass the CVR zipcode to e-conomic.');
                assert_true($companyInformation->city === 'Taastrup', 'Fresh registration must pass the CVR city to e-conomic.');
                assert_true($companyInformation->website === 'https://demo.test', 'Fresh registration must pass the CVR website to e-conomic.');
                assert_true(count(\classes\email::$sent) === 2, 'Fresh registration must send two welcome emails.');
                assert_true(\objects\users_o::$interaction_log[0] === 'bootstrap:12345678', 'Fresh registration must bootstrap the local user before emails.');
                assert_true(\classes\email::$sent[0]['email'] === 'jm@truckwash.dk', 'Fresh registration should notify the internal welcome recipient first.');
                assert_true(count(\classes\email::$superuser_notifications) === 1, 'Fresh registration must notify opted-in superusers once.');
                assert_true(\classes\email::$superuser_notifications[0]['customer_number'] === 12345678, 'Fresh registration superuser notification must use the created customer number.');
                assert_true(count(\classes\slack::$customer_registration_notifications) === 1, 'Fresh registration must notify Slack once.');
                assert_true(\classes\slack::$customer_registration_notifications[0]['customer_number'] === 12345678, 'Fresh registration Slack notification must use the created customer number.');
            },
        ],
        [
            'name' => 'Successful registration falls back to the create response when immediate import lookup misses',
            'params' => array_merge($baseParams, ['contactPhone' => 87654320]),
            'setup' => static function (): void {
                \classes\economic::$mock_create_response = (object)[
                    'customerNumber' => 12345678,
                    'name' => 'Mock Company',
                    'email' => 'test@test.com',
                ];
                \objects\users_o::$mock_external_lookup_enabled = false;
            },
            'expected_success' => (object)[
                'customerNumber' => 12345678,
                'name' => 'Mock Company',
                'email' => 'test@test.com',
            ],
            'expected_status' => 201,
            'assert' => static function (): void {
                assert_true(count(\classes\economic::$create_calls) === 1, 'Fresh registration must call create exactly once.');
                assert_true(\objects\users_o::$interaction_log[0] === 'bootstrap:12345678', 'Fresh registration must try the standard local bootstrap first.');
                assert_true(\objects\users_o::$interaction_log[1] === 'snapshot-import:12345678', 'Fresh registration must import from the create response when the immediate lookup misses.');
                assert_true(count(\classes\email::$sent) === 2, 'Snapshot fallback registration must send two welcome emails.');
                assert_true(count(\classes\email::$superuser_notifications) === 1, 'Snapshot fallback registration must notify opted-in superusers once.');
                assert_true(count(\classes\slack::$customer_registration_notifications) === 1, 'Snapshot fallback registration must notify Slack once.');
            },
        ],
        [
            'name' => 'Duplicate create response recovers a just-created e-conomic customer and sends notifications',
            'params' => $baseParams,
            'setup' => static function (): void {
                \classes\economic::$mock_create_exception = new \RuntimeException('e-conomic request failed with HTTP 400: Customer already exists');
                \classes\economic::$mock_collection_after_create_exception = [
                    (object)[
                        'customerNumber' => 12345678,
                        'name' => 'Recovered After Create',
                        'email' => 'test@test.com',
                    ],
                ];
                \objects\users_o::$mock_importable_customer_numbers = [12345678];
            },
            'expected_success' => (object)[
                'customerNumber' => 12345678,
                'name' => 'Recovered After Create',
                'email' => 'test@test.com',
            ],
            'expected_status' => 200,
            'assert' => static function (): void {
                assert_true(count(\classes\economic::$create_calls) === 1, 'Recovery must still record the attempted create call.');
                assert_true(count(\classes\economic::$search_calls) === 2, 'Recovery must verify the duplicate by searching e-conomic again.');
                assert_true(count(\classes\email::$sent) === 2, 'Duplicate create recovery must send two welcome emails.');
                assert_true(count(\classes\email::$superuser_notifications) === 1, 'Duplicate create recovery must notify opted-in superusers once.');
                assert_true(count(\classes\slack::$customer_registration_notifications) === 1, 'Duplicate create recovery must notify Slack once.');
            },
        ],
        [
            'name' => 'Generic create failure recovers a confirmed just-created e-conomic customer',
            'params' => $baseParams,
            'setup' => static function (): void {
                \classes\economic::$mock_create_exception = new \RuntimeException('e-conomic request failed with HTTP 400: Validation failed. | details={"httpStatusCode":400}');
                \classes\economic::$mock_collection_after_create_exception = [
                    (object)[
                        'customerNumber' => 12345678,
                        'name' => 'Recovered After Generic Create Failure',
                        'email' => 'test@test.com',
                    ],
                ];
                \objects\users_o::$mock_importable_customer_numbers = [12345678];
            },
            'expected_success' => (object)[
                'customerNumber' => 12345678,
                'name' => 'Recovered After Generic Create Failure',
                'email' => 'test@test.com',
            ],
            'expected_status' => 200,
            'assert' => static function (): void {
                assert_true(count(\classes\economic::$create_calls) === 1, 'Generic create recovery must still record the attempted create call.');
                assert_true(count(\classes\economic::$search_calls) === 2, 'Generic create recovery must confirm the customer by searching e-conomic again.');
                assert_true(count(\classes\email::$sent) === 2, 'Generic create recovery must send two welcome emails.');
                assert_true(count(\classes\email::$superuser_notifications) === 1, 'Generic create recovery must notify opted-in superusers once.');
                assert_true(count(\classes\slack::$customer_registration_notifications) === 1, 'Generic create recovery must notify Slack once.');
            },
        ],
        [
            'name' => 'Fresh create mismatch returns conflict without local bootstrap or email',
            'params' => $baseParams,
            'setup' => static function (): void {
                \classes\economic::$mock_create_response = (object)[
                    'customerNumber' => 87654321,
                    'logId' => 'abc123',
                    'message' => 'Customer created with different number',
                ];
            },
            'expected_error' => 'E-conomic created the customer under customer number 87654321 instead of the submitted phone number 12345678. Manual cleanup or reassignment is required before retrying.',
            'expected_status' => 409,
            'assert' => static function (): void {
                assert_true(count(\classes\economic::$create_calls) === 1, 'Create mismatch must still record the attempted create call.');
                assert_true(count(\objects\users_o::$interaction_log) === 0, 'Create mismatch must not bootstrap the wrong local customer.');
                assert_true(count(\classes\email::$sent) === 0, 'Create mismatch must not send welcome emails.');
                assert_true(count(\classes\email::$superuser_notifications) === 0, 'Create mismatch must not send superuser notifications.');
                assert_true(count(\classes\slack::$customer_registration_notifications) === 0, 'Create mismatch must not send Slack customer registration notifications.');
                assert_true(count(\objects\logs_o::$entries) === 1, 'Create mismatch should be logged for manual cleanup.');
            },
        ],
    ];

    $allPassed = true;
    foreach ($testCases as $test) {
        \classes\response::reset();
        \classes\recaptcha::$mock_valid = true;
        \classes\economic::reset();
        \classes\email::reset();
        \classes\slack::reset();
        \classes\virkdata::$mock_name = 'Mock Company';
        \classes\virkdata::$mock_address = 'Demo Street 1';
        \classes\virkdata::$mock_zipcode = 2630;
        \classes\virkdata::$mock_city = 'Taastrup';
        \classes\virkdata::$mock_website = 'https://demo.test';
        \classes\virkdata::$mock_exception = null;
        \objects\users_o::reset();
        \objects\logs_o::reset();

        if (isset($test['setup'])) {
            $test['setup']();
        }

        \classes\response::$request_parameters = $test['params'];
        $issues = [];

        try {
            $callback();
            $issues[] = 'Callback did not exit as expected.';
        } catch (\classes\MockExitException $e) {
            if (array_key_exists('expected_error', $test)) {
                if (\classes\response::$last_error !== $test['expected_error']) {
                    $issues[] = 'Expected error "' . $test['expected_error'] . '" but got "' . (\classes\response::$last_error ?? 'NULL') . '".';
                }
            } elseif (array_key_exists('expected_success', $test)) {
                try {
                    assert_same_data(\classes\response::$last_success, $test['expected_success'], 'Unexpected success payload.');
                } catch (\Throwable $assertionFailure) {
                    $issues[] = $assertionFailure->getMessage();
                }
            }

            if (\classes\response::$last_status !== $test['expected_status']) {
                $issues[] = 'Expected status ' . $test['expected_status'] . ' but got ' . (\classes\response::$last_status ?? 'NULL') . '.';
            }
        } catch (\Throwable $e) {
            $issues[] = 'Unexpected exception: ' . $e->getMessage();
        }

        if (empty($issues) && isset($test['assert'])) {
            try {
                $test['assert']();
            } catch (\Throwable $e) {
                $issues[] = $e->getMessage();
            }
        }

        if (empty($issues)) {
            ok($test['name']);
            continue;
        }

        fail($test['name'] . ' - ' . implode(' ', $issues));
        $allPassed = false;
    }

    echo "\nRegisterCvrTest completed.\n";
    exit($allPassed ? 0 : 1);
}
