<?php

declare(strict_types=1);

/**
 * SmartApply
 * Mapping Engine Test
 *
 * Location:
 * C:\xampp\htdocs\SmartApply\test_mapping_engine.php
 */

require_once __DIR__ . '/Core/Database.php';
require_once __DIR__ . '/Core/MappingEngine.php';


/*
|--------------------------------------------------------------------------
| Output Helpers
|--------------------------------------------------------------------------
*/

function line(string $text = ''): void
{
    echo $text . PHP_EOL;
}

function section(string $title): void
{
    line();
    line(str_repeat('=', 70));
    line($title);
    line(str_repeat('=', 70));
}

function success(string $message): void
{
    line('[OK] ' . $message);
}

function info(string $message): void
{
    line('[INFO] ' . $message);
}

function warning(string $message): void
{
    line('[WARNING] ' . $message);
}

function failure(string $message): void
{
    line('[ERROR] ' . $message);
}


/*
|--------------------------------------------------------------------------
| Start Test
|--------------------------------------------------------------------------
*/

try {

    section('SMARTAPPLY MAPPING ENGINE TEST');

    line('Project : SmartApply');
    line('Folder  : ' . __DIR__);
    line('PHP     : ' . PHP_VERSION);
    line();


    /*
    |--------------------------------------------------------------------------
    | 1. Test Database Class
    |--------------------------------------------------------------------------
    */

    section('1. DATABASE CONNECTION');

    try {

        $pdo = Database::connection();

        if (!$pdo instanceof PDO) {
            throw new RuntimeException(
                'Database::connection() did not return PDO.'
            );
        }

        success('PDO connection established.');

        /*
         * Show database name.
         */
        $databaseName = $pdo->query(
            'SELECT DATABASE()'
        )->fetchColumn();

        info('Database: ' . (string)$databaseName);

        /*
         * Show server version.
         */
        $serverVersion = $pdo->query(
            'SELECT VERSION()'
        )->fetchColumn();

        info('MariaDB/MySQL: ' . (string)$serverVersion);

    } catch (Throwable $e) {

        failure(
            'Database connection failed: ' .
            $e->getMessage()
        );

        exit(1);
    }


    /*
    |--------------------------------------------------------------------------
    | 2. Test application_mappings Table
    |--------------------------------------------------------------------------
    */

    section('2. APPLICATION_MAPPINGS TABLE');

    try {

        $tableCheck = $pdo->query(
            "SHOW TABLES LIKE 'application_mappings'"
        );

        $tableExists = $tableCheck->fetchColumn();

        if (!$tableExists) {

            throw new RuntimeException(
                "Table 'application_mappings' does not exist."
            );
        }

        success(
            "Table 'application_mappings' exists."
        );

    } catch (Throwable $e) {

        failure(
            'application_mappings check failed: ' .
            $e->getMessage()
        );

        exit(1);
    }


    /*
    |--------------------------------------------------------------------------
    | 3. Check Table Columns
    |--------------------------------------------------------------------------
    */

    section('3. TABLE STRUCTURE');

    try {

        $columns = $pdo->query(
            "SHOW COLUMNS FROM application_mappings"
        )->fetchAll(PDO::FETCH_ASSOC);

        if (!$columns) {
            throw new RuntimeException(
                'application_mappings has no columns.'
            );
        }

        success(
            'application_mappings structure loaded.'
        );

        line();

        foreach ($columns as $column) {

            line(
                sprintf(
                    "%-25s %-25s %s",
                    $column['Field'],
                    $column['Type'],
                    $column['Null']
                )
            );
        }

    } catch (Throwable $e) {

        failure(
            'Could not inspect application_mappings: ' .
            $e->getMessage()
        );

        exit(1);
    }


    /*
    |--------------------------------------------------------------------------
    | 4. Count Mappings
    |--------------------------------------------------------------------------
    */

    section('4. MAPPING COUNT');

    try {

        $count = (int)$pdo->query(
            "SELECT COUNT(*) FROM application_mappings"
        )->fetchColumn();

        info(
            'Total mappings: ' . $count
        );

        if ($count > 0) {

            success(
                'application_mappings contains data.'
            );

        } else {

            warning(
                'application_mappings is empty.'
            );
        }

    } catch (Throwable $e) {

        failure(
            'Could not count mappings: ' .
            $e->getMessage()
        );
    }


    /*
    |--------------------------------------------------------------------------
    | 5. Show Portal Names
    |--------------------------------------------------------------------------
    */

    section('5. AVAILABLE PORTALS');

    $portalNames = [];

    try {

        $stmt = $pdo->query("
            SELECT
                portal_name,
                COUNT(*) AS total
            FROM application_mappings
            GROUP BY portal_name
            ORDER BY portal_name ASC
        ");

        $portalNames = $stmt->fetchAll(
            PDO::FETCH_ASSOC
        );

        if (!$portalNames) {

            warning(
                'No portal mappings found.'
            );

        } else {

            foreach ($portalNames as $portal) {

                line(
                    sprintf(
                        '%-40s %d mappings',
                        (string)$portal['portal_name'],
                        (int)$portal['total']
                    )
                );
            }

            success(
                'Portal list loaded successfully.'
            );
        }

    } catch (Throwable $e) {

        failure(
            'Could not load portals: ' .
            $e->getMessage()
        );
    }


    /*
    |--------------------------------------------------------------------------
    | 6. Create MappingEngine
    |--------------------------------------------------------------------------
    */

    section('6. MAPPING ENGINE INITIALIZATION');

    try {

        $engine = new MappingEngine();

        success(
            'MappingEngine initialized successfully.'
        );

    } catch (Throwable $e) {

        failure(
            'MappingEngine initialization failed: ' .
            $e->getMessage()
        );

        line();
        line('File: ' . $e->getFile());
        line('Line: ' . $e->getLine());

        exit(1);
    }


    /*
    |--------------------------------------------------------------------------
    | 7. Test MappingEngine PDO
    |--------------------------------------------------------------------------
    */

    section('7. MAPPING ENGINE PDO');

    try {

        if (!$engine->testConnection()) {

            throw new RuntimeException(
                'MappingEngine PDO test failed.'
            );
        }

        success(
            'MappingEngine PDO connection is working.'
        );

    } catch (Throwable $e) {

        failure(
            'MappingEngine PDO test failed: ' .
            $e->getMessage()
        );

        exit(1);
    }


    /*
    |--------------------------------------------------------------------------
    | 8. Determine Portal
    |--------------------------------------------------------------------------
    */

    section('8. TEST PORTAL');

    $portalName = null;

    if (!empty($portalNames)) {

        /*
         * Automatically use the first available portal.
         */
        $portalName = (string)$portalNames[0]['portal_name'];

        info(
            'Automatically selected portal: ' .
            $portalName
        );

    } else {

        /*
         * If database is empty, use a safe test name.
         */
        $portalName = 'TEST_PORTAL';

        warning(
            'No portal found. Using TEST_PORTAL.'
        );
    }


    /*
    |--------------------------------------------------------------------------
    | 9. Get Mappings
    |--------------------------------------------------------------------------
    */

    section('9. GET MAPPINGS');

    try {

        $mappings = $engine->getMappings(
            $portalName
        );

        info(
            'Mappings returned: ' .
            count($mappings)
        );

        if ($mappings) {

            success(
                'getMappings() is working.'
            );

            /*
             * Display first 10 mappings only.
             */
            $displayLimit = min(
                count($mappings),
                10
            );

            line();

            for ($i = 0; $i < $displayLimit; $i++) {

                $mapping = $mappings[$i];

                line(
                    sprintf(
                        '#%d | %s -> %s | value=%s',
                        (int)$mapping['id'],
                        (string)$mapping['source_field'],
                        (string)$mapping['portal_field'],
                        (string)($mapping['portal_value'] ?? '')
                    )
                );
            }

            if (count($mappings) > 10) {

                line(
                    '... ' .
                    (count($mappings) - 10) .
                    ' more mappings'
                );
            }

        } else {

            warning(
                'No active mappings found for portal: ' .
                $portalName
            );
        }

    } catch (Throwable $e) {

        failure(
            'getMappings() failed: ' .
            $e->getMessage()
        );
    }


    /*
    |--------------------------------------------------------------------------
    | 10. Get All Mappings Including Inactive
    |--------------------------------------------------------------------------
    */

    section('10. ALL MAPPINGS');

    try {

        $allMappings = $engine->getMappings(
            $portalName,
            false
        );

        info(
            'All mappings: ' .
            count($allMappings)
        );

        success(
            'getMappings(activeOnly=false) is working.'
        );

    } catch (Throwable $e) {

        failure(
            'Could not load all mappings: ' .
            $e->getMessage()
        );
    }


    /*
    |--------------------------------------------------------------------------
    | 11. Grouped Mappings
    |--------------------------------------------------------------------------
    */

    section('11. GROUPED MAPPINGS');

    try {

        $grouped = $engine->getGroupedMappings(
            $portalName
        );

        info(
            'Source fields: ' .
            count($grouped)
        );

        if ($grouped) {

            foreach ($grouped as $field => $items) {

                line(
                    sprintf(
                        '%-30s %d mapping(s)',
                        $field,
                        count($items)
                    )
                );
            }

            success(
                'getGroupedMappings() is working.'
            );

        } else {

            warning(
                'No grouped mappings found.'
            );
        }

    } catch (Throwable $e) {

        failure(
            'Grouped mappings failed: ' .
            $e->getMessage()
        );
    }


    /*
    |--------------------------------------------------------------------------
    | 12. Postback Mappings
    |--------------------------------------------------------------------------
    */

    section('12. POSTBACK MAPPINGS');

    try {

        $postbacks = $engine->getPostbackMappings(
            $portalName
        );

        info(
            'Postback mappings: ' .
            count($postbacks)
        );

        success(
            'getPostbackMappings() is working.'
        );

        foreach ($postbacks as $mapping) {

            line(
                sprintf(
                    '#%d | %s -> %s',
                    (int)$mapping['id'],
                    (string)$mapping['portal_field'],
                    (string)($mapping['postback_target'] ?? '')
                )
            );
        }

    } catch (Throwable $e) {

        failure(
            'Postback mappings failed: ' .
            $e->getMessage()
        );
    }


    /*
    |--------------------------------------------------------------------------
    | 13. Transformation Tests
    |--------------------------------------------------------------------------
    */

    section('13. VALUE TRANSFORMATION');

    try {

        $tests = [

            [
                'name' => 'trim',
                'input' => '   Rwanda   ',
                'type' => 'trim'
            ],

            [
                'name' => 'uppercase',
                'input' => 'rwanda',
                'type' => 'uppercase'
            ],

            [
                'name' => 'lowercase',
                'input' => 'RWANDA',
                'type' => 'lowercase'
            ],

            [
                'name' => 'country_code',
                'input' => 'Rwanda',
                'type' => 'country_code'
            ],

            [
                'name' => 'phone',
                'input' => '+250 788-123-456',
                'type' => 'phone'
            ],

            [
                'name' => 'date',
                'input' => '23/08/2026',
                'type' => 'date'
            ]
        ];

        foreach ($tests as $test) {

            $result = $engine->transformValue(
                $test['input'],
                $test['type']
            );

            line(
                sprintf(
                    '%-15s : %s -> %s',
                    $test['name'],
                    (string)$test['input'],
                    (string)$result
                )
            );
        }

        success(
            'Transformation tests completed.'
        );

    } catch (Throwable $e) {

        failure(
            'Transformation test failed: ' .
            $e->getMessage()
        );
    }


    /*
    |--------------------------------------------------------------------------
    | 14. Country Code Test
    |--------------------------------------------------------------------------
    */

    section('14. COUNTRY CODE TEST');

    $countryTests = [
        'Rwanda',
        'rwanda',
        'RW',
        'RW        ',
        'Uganda',
        'Kenya',
        'Burundi',
        'Tanzania',
        'United States',
        'Canada',
        'South Africa',
        'Zambia',
        'Zimbabwe'
    ];

    foreach ($countryTests as $country) {

        try {

            $result = $engine->transformValue(
                $country,
                'country_code'
            );

            line(
                sprintf(
                    '%-35s => %s',
                    '"' . $country . '"',
                    $result
                )
            );

        } catch (Throwable $e) {

            failure(
                'Country test failed for ' .
                $country .
                ': ' .
                $e->getMessage()
            );
        }
    }


    /*
    |--------------------------------------------------------------------------
    | 15. Resolve Mapping Test
    |--------------------------------------------------------------------------
    */

    section('15. RESOLVE MAPPING');

    try {

        /*
         * Find a likely faculty mapping.
         */
        $facultyMappings = $engine->getFieldMappings(
            $portalName,
            'faculty'
        );

        if ($facultyMappings) {

            success(
                'Faculty mappings found: ' .
                count($facultyMappings)
            );

            $facultyValue =
                $facultyMappings[0]['portal_value'] ?? 'FLAW';

            $resolved = $engine->resolveMapping(
                $portalName,
                'faculty',
                $facultyValue,
                [
                    'faculty' => $facultyValue
                ]
            );

            if ($resolved) {

                success(
                    'resolveMapping() successfully resolved a mapping.'
                );

                line(
                    'Mapping ID     : ' .
                    (int)$resolved['id']
                );

                line(
                    'Source Field   : ' .
                    (string)$resolved['source_field']
                );

                line(
                    'Portal Field   : ' .
                    (string)$resolved['portal_field']
                );

                line(
                    'Portal Value   : ' .
                    (string)($resolved['portal_value'] ?? '')
                );

            } else {

                warning(
                    'No faculty mapping could be resolved.'
                );
            }

        } else {

            warning(
                'No source_field="faculty" mapping found.'
            );
        }

    } catch (Throwable $e) {

        failure(
            'resolveMapping() failed: ' .
            $e->getMessage()
        );
    }


    /*
    |--------------------------------------------------------------------------
    | 16. Dependent Mapping Test
    |--------------------------------------------------------------------------
    */

    section('16. DEPENDENT MAPPINGS');

    try {

        /*
         * Try the real example from the external portal:
         *
         * Faculty = FLAW
         */
        $dependentMappings =
            $engine->getDependentMappings(
                $portalName,
                'faculty',
                'FLAW'
            );

        info(
            'Mappings depending on faculty=FLAW: ' .
            count($dependentMappings)
        );

        if ($dependentMappings) {

            foreach (
                $dependentMappings
                as $mapping
            ) {

                line(
                    sprintf(
                        '#%d | %s | %s',
                        (int)$mapping['id'],
                        (string)$mapping['source_field'],
                        (string)$mapping['portal_field']
                    )
                );
            }

            success(
                'getDependentMappings() is working.'
            );

        } else {

            warning(
                'No dependent mappings found for faculty=FLAW.'
            );
        }

    } catch (Throwable $e) {

        failure(
            'Dependent mappings test failed: ' .
            $e->getMessage()
        );
    }


    /*
    |--------------------------------------------------------------------------
    | 17. Build Payload Test
    |--------------------------------------------------------------------------
    */

    section('17. BUILD PAYLOAD');

    try {

        /*
         * Example SmartApply application data.
         *
         * These names must correspond to source_field
         * values configured in application_mappings.
         */
        $applicationData = [

            'first_name' => 'Christian',

            'last_name' => 'Manzi',

            'gender' => 'M',

            'country' => 'Rwanda',

            'country_code' => 'RW',

            'phone' => '+250788123456',

            'email' => 'test@example.com',

            'faculty' => 'FLAW',

            'programme' => 'LAW-BC',

            'option' => 'LAW',

            'campus' => 'KGL',

            'time_study' => 'D',

            'date_of_birth' => '23/08/2000'
        ];

        $payload = $engine->buildPayload(
            $portalName,
            $applicationData
        );

        info(
            'Payload items: ' .
            count($payload)
        );

        if ($payload) {

            success(
                'buildPayload() is working.'
            );

            line();

            foreach ($payload as $item) {

                line(
                    sprintf(
                        '#%d | %s => %s = %s',
                        (int)$item['mapping_id'],
                        (string)$item['source_field'],
                        (string)$item['portal_field'],
                        is_scalar($item['value'])
                            ? (string)$item['value']
                            : json_encode(
                                $item['value'],
                                JSON_UNESCAPED_UNICODE
                            )
                    )
                );
            }

        } else {

            warning(
                'buildPayload() returned an empty payload.'
            );

            info(
                'This may simply mean that the source_field names '
                . 'in application_mappings differ from the test data.'
            );
        }

    } catch (Throwable $e) {

        failure(
            'buildPayload() failed: ' .
            $e->getMessage()
        );
    }


    /*
    |--------------------------------------------------------------------------
    | 18. Validation Test
    |--------------------------------------------------------------------------
    */

    section('18. VALIDATION');

    try {

        $validationErrors =
            $engine->validate(
                $portalName,
                $applicationData
            );

        if (!$validationErrors) {

            success(
                'Validation passed. No required fields are missing.'
            );

        } else {

            warning(
                'Validation returned ' .
                count($validationErrors) .
                ' error(s).'
            );

            foreach (
                $validationErrors
                as $error
            ) {

                line(
                    '- ' .
                    (string)$error['source_field'] .
                    ': ' .
                    (string)$error['message']
                );
            }
        }

    } catch (Throwable $e) {

        failure(
            'Validation failed: ' .
            $e->getMessage()
        );
    }


    /*
    |--------------------------------------------------------------------------
    | 19. Direct SQL Test
    |--------------------------------------------------------------------------
    */

    section('19. DIRECT SQL TEST');

    try {

        $stmt = $pdo->prepare("
            SELECT
                id,
                portal_name,
                source_field,
                portal_field,
                portal_value,
                is_active,
                mapping_order
            FROM application_mappings
            ORDER BY id ASC
            LIMIT 10
        ");

        $stmt->execute();

        $rows = $stmt->fetchAll(
            PDO::FETCH_ASSOC
        );

        info(
            'Direct SQL returned ' .
            count($rows) .
            ' row(s).'
        );

        foreach ($rows as $row) {

            line(
                sprintf(
                    '#%d | %s | %s -> %s | %s',
                    (int)$row['id'],
                    (string)$row['portal_name'],
                    (string)$row['source_field'],
                    (string)$row['portal_field'],
                    (string)($row['portal_value'] ?? '')
                )
            );
        }

        success(
            'Direct SQL test passed.'
        );

    } catch (Throwable $e) {

        failure(
            'Direct SQL test failed: ' .
            $e->getMessage()
        );
    }


    /*
    |--------------------------------------------------------------------------
    | 20. Final Result
    |--------------------------------------------------------------------------
    */

    section('FINAL RESULT');

    success(
        'Database connection is working.'
    );

    success(
        'MappingEngine initialization is working.'
    );

    success(
        'application_mappings table is accessible.'
    );

    success(
        'MappingEngine methods executed successfully.'
    );

    line();

    line(
        'SMARTAPPLY MAPPING ENGINE TEST COMPLETED.'
    );

    line();

} catch (Throwable $e) {

    section('FATAL TEST ERROR');

    failure(
        $e->getMessage()
    );

    line();

    line(
        'FILE: ' . $e->getFile()
    );

    line(
        'LINE: ' . $e->getLine()
    );

    line();

    line(
        'TRACE:'
    );

    line(
        $e->getTraceAsString()
    );

    exit(1);
}

