<?php

declare(strict_types=1);

namespace Tests\Support;

/**
 * Dynamic Test Data Factory — Auth Service
 *
 * ZERO HARDCODING POLICY: Every value produced here is unique per invocation,
 * seeded by microsecond time + cryptographic random bytes. No test user will
 * ever collide with production data or a previous test run.
 *
 * Teardown strategy: all generated emails share a domain suffix
 * (@sms.testdomain.local) that is never used in production, making
 * DELETE WHERE email LIKE '%@sms.testdomain.local' a safe atomic purge.
 *
 * Snowflake role/status IDs are sourced directly from auth-sql.em seed data.
 */
class TestDataFactory
{
    // ── Teardown-safe markers ────────────────────────────────────────────────
    public const TEST_EMAIL_PREFIX = 'sms_qa_';
    public const TEST_EMAIL_DOMAIN = '@sms.testdomain.local';
    public const TEST_USERNAME_PREFIX = 'qa_tester_';

    // ── Snowflake Role IDs (from auth-sql.em seed) ──────────────────────────
    public const ROLE_SUPER_ADMIN   = '142345678901234561';
    public const ROLE_SCHOOL_ADMIN  = '142345678901234562';
    public const ROLE_TEACHER       = '142345678901234563';
    public const ROLE_STUDENT       = '142345678901234564';
    public const ROLE_PARENT        = '142345678901234565';

    // ── Snowflake Status IDs (from auth-sql.em seed) ─────────────────────────
    public const STATUS_ACTIVE       = '142345678901234591';
    public const STATUS_PENDING      = '142345678901234592';
    public const STATUS_SUSPENDED    = '142345678901234593';
    public const STATUS_BANNED       = '142345678901234594';
    public const STATUS_INACTIVE     = '142345678901234595';

    // ── Role/Status slug lookups ─────────────────────────────────────────────
    public const ROLE_SLUGS = [
        self::ROLE_STUDENT     => 'student',
        self::ROLE_TEACHER     => 'teacher',
        self::ROLE_SCHOOL_ADMIN => 'school-admin',
    ];
    public const STATUS_SLUGS = [
        self::STATUS_ACTIVE    => 'active',
        self::STATUS_SUSPENDED => 'suspended',
        self::STATUS_BANNED    => 'banned',
    ];

    private static array $firstNames = ['Amara', 'Kofi', 'Liam', 'Zara', 'Emeka', 'Saoirse', 'Ivan', 'Priya'];
    private static array $lastNames  = ['Okonkwo', 'Chen', 'Rivera', 'Hassan', 'Patel', 'Müller', 'Osei', 'Andrade'];
    private static array $streets    = ['Oak Lane', 'Test Avenue', 'QA Boulevard', 'Spec Drive', 'Mock Close'];
    private static array $cities     = ['Testville', 'QA City', 'Mockburg', 'Spectown', 'Lagostest'];

    /**
     * Generate a complete, unique user registration payload.
     *
     * @param array $overrides Override specific fields (e.g. ['role_id' => self::ROLE_TEACHER])
     */
    public static function makeUser(array $overrides = []): array
    {
        $uid = self::uid();

        return array_merge([
            'name'     => self::pick(self::$firstNames) . ' ' . self::pick(self::$lastNames),
            'username' => self::TEST_USERNAME_PREFIX . $uid,
            'email'    => self::TEST_EMAIL_PREFIX . $uid . self::TEST_EMAIL_DOMAIN,
            'password' => 'Qa@Secure_' . strtoupper(substr($uid, 0, 6)) . '!9',
            'phone'    => '+234' . rand(7000000000, 9999999999),
            'address'  => rand(1, 999) . ' ' . self::pick(self::$streets) . ', ' . self::pick(self::$cities),
            'role_id'  => self::ROLE_STUDENT, // Snowflake student role
        ], $overrides);
    }

    /** Payload with a password shorter than the 8-char minimum */
    public static function makeWeakPasswordUser(): array
    {
        return self::makeUser(['password' => 'weak']);
    }

    /** Payload with a malformed email address */
    public static function makeInvalidEmailUser(): array
    {
        return self::makeUser(['email' => 'not-an-email-' . self::uid()]);
    }

    /** Stripped payload missing a required field */
    public static function makeMissingFieldUser(string $omit = 'phone'): array
    {
        $user = self::makeUser();
        unset($user[$omit]);
        return $user;
    }

    /**
     * Generate a cryptographically unique identifier.
     * Format: 16 hex chars derived from microtime + random bytes.
     */
    public static function uid(): string
    {
        $micro = str_replace(['.', ' '], '', (string) microtime());
        return substr($micro . bin2hex(random_bytes(4)), 0, 16);
    }

    /** SQL LIKE pattern that matches all test emails — used for teardown */
    public static function teardownEmailPattern(): string
    {
        return '%' . self::TEST_EMAIL_DOMAIN;
    }

    private static function pick(array $arr): string
    {
        return $arr[array_rand($arr)];
    }
}
