<?php

namespace App\Http\Utils;

use SelfPhp\Auth;
use Carbon\Carbon;
use App\Models\Language;
use App\Models\AuthModel;

/**
 * Class Utils
 * Utility class for user-related operations such as user registration and checking.
 */
class Utils
{
    /**
     * Utils constructor.
     * Constructor method (currently empty).
     */
    public function __construct()
    {
        // Constructor logic (if any).
    }

    /**
     * Alphanumeric generator util func.
     *
     * @param int $length Desired length of the UUID (default 16)
     * @return string
     */
    function uuidGenerator(int $length = 16): string
    {
        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $charactersLength = strlen($characters);

        $bytes = random_bytes($length);
        $result = '';

        for ($i = 0; $i < $length; $i++) {
            $result .= $characters[ord($bytes[$i]) % $charactersLength];
        }

        return strtolower($result);
    }

    /**
     * Returns the html decoded string
     *
     * @param int $business_id
     * @return array
     */
    public function decode_string_component($string)
    {
        $output = preg_replace("/%u([0-9a-f]{3,4})/i", "&#x\\1;", urldecode($string));
        return html_entity_decode($output);
    }

    /**
     * Registers a new user in the system.
     *
     * @param array $data User registration data.
     * @return bool True if the registration is successful, false otherwise.
     */
    public function registerUser(array $data)
    {
        $dbData = [
            'uuid'        => $this->uuidGenerator(),
            'name'        => $data['name'] ?? null,
            'username'    => $data['username'] ?? null,
            'email'       => $data['email'] ?? null,
            'password'    => isset($data['password']) ? password_hash($data['password'], PASSWORD_DEFAULT) : null,
            'contact'     => $data['tel'] ?? null,
            'role'        => 'user', // default role
            'accountType' => $data['accountType'] ?? 'individual',
            'is_verified' => null,      // default unverified
            'terms'       => isset($data['terms']) && $data['terms'] ? 1 : 0,
            'created_at'  => Carbon::now(),
            'updated_at'  => Carbon::now(),
        ];

        // Create a new AuthModel instance and attempt to save user data.
        // Return true if the save operation is successful, false otherwise.
        return AuthModel::query()->insert($dbData);
    }

    /**
     * Checks if a user with the given email exists.
     *
     * @param array $data User data containing email for checking.
     * @return mixed|null The user data if found, null otherwise.
     */
    public function checkUser(array $data)
    {
        // Create a new AuthModel instance and query for a user by email.
        // Return the user data if found, null otherwise.
        $user = AuthModel::query()
            ->where('email', $data['email'])
            ->first();

        return $user;
    }


    /**
     * Convert a field or identifier into a nice readable format.
     *
     * Examples:
     *  - "urlField"        => "Url Field"
     *  - "URLFieldName"   => "Url Field Name"
     *  - "payment_status" => "Payment Status"
     *  - "user-id"        => "User Id"
     *
     * @param string $value
     * @return string
     */
    function formatCsOrPcStringToReadableWord(string $value): string
    {
        // Replace underscores and hyphens with spaces
        $value = str_replace(['_', '-'], ' ', $value);

        // Insert space before capital letters (camelCase / PascalCase)
        $value = preg_replace('/(?<!^)([A-Z])/', ' $1', $value);

        // Normalize spacing and apply title case
        return ucwords(trim(preg_replace('/\s+/', ' ', strtolower($value))));
    }

    /**
     * Parses a date string using Carbon and returns it in the desired format.
     *
     * @param string $dateString The date string to parse.
     * @param string $outputFormat Optional. The desired output format. Default is 'd M Y, H:i'.
     * @return string|null Returns the formatted date, or null if the date is invalid.
     */
    function format_date($dateString, $outputFormat = 'd M Y, H:i')
    {
        if (!$dateString) {
            return null;
        }

        try {
            // Parse the date with Carbon
            $date = Carbon::parse($dateString);
            return $date->format($outputFormat);
        } catch (\Exception $e) {
            return null; // invalid date
        }
    }

    /**
     * IPA phonetic conversion patterns.
     *
     * This ruleset defines regex-based mappings from English graphemes
     * to approximate IPA (International Phonetic Alphabet) symbols.
     *
     * NOTE:
     * - This mapping is heuristic-based and does not fully cover
     *   all English pronunciation edge cases.
     * - The pattern rules and phonetic approximations were generated
     *   by DeepSeek AI.
     * - Rules are applied sequentially and therefore ordering matters.
     */
    private $ipaPatterns = [
        // Vowels
        '/a(?=l[^aeiou]|lm|lf)/' => 'ɔ',
        '/a(?=r[aeiou])/' => 'ɛ',
        '/a(?=tion$)/' => 'eɪ',
        '/a(?=ther$)/' => 'ɑ',
        '/a(?=ss|st|sk|sp)/' => 'æ',
        '/a$/' => 'ə',
        '/a/' => 'æ',

        '/e(?=\\b|d$|s$)/' => 'i',
        '/e(?=r\\b)/' => 'ɚ',
        '/e/' => 'ɛ',

        '/i(?=ght|nd|ld)/' => 'aɪ',
        '/i(?=\\b|e\\b)/' => 'i',
        '/i/' => 'ɪ',

        '/o(?=u)/' => 'aʊ',
        '/o(?=i)/' => 'ɔɪ',
        '/o(?=\\b)/' => 'oʊ',
        '/o/' => 'ɑ',

        '/u(?=\\b)/' => 'u',
        '/u/' => 'ʌ',

        // Consonants
        '/c(?=e|i|y)/' => 's',
        '/c/' => 'k',

        '/g(?=e|i|y)/' => 'dʒ',
        '/g/' => 'ɡ',

        '/th/' => 'θ',
        '/ph/' => 'f',
        '/sh/' => 'ʃ',
        '/ch/' => 'tʃ',
        '/wh/' => 'hw',

        '/s(?=ion|ure)/' => 'ʒ',
        '/s/' => 's',

        '/x/' => 'ks',

        // Special cases
        '/ough/' => 'ʌf',
        '/tion/' => 'ʃən',
        '/ture/' => 'tʃər',
    ];

    /**
     * Explicit pronunciation exceptions.
     *
     * This list overrides the regex-based IPA generation for words
     * whose pronunciations are irregular or historically derived
     * and therefore unsuitable for pattern-based conversion.
     *
     * These entries take precedence over all phonetic rules.
     */
    private $exceptions = [
        'algorithm' => 'ælgərɪðəm',
        'through'   => 'θru',
        'though'    => 'ðoʊ',
        'thought'   => 'θɔt',
        'colonel'   => 'kɝnəl',
        'queue'     => 'kju',
        'island'    => 'aɪlənd',
        'debt'      => 'dɛt',
        'subtle'    => 'sʌtəl',
        'knight'    => 'naɪt',
    ];

    /**
     * Convert an English word into an approximate IPA transcription.
     *
     * Processing steps:
     * 1. Normalizes the input word (lowercase and trimmed).
     * 2. Checks for an exact match in the exception list.
     * 3. Applies IPA regex patterns sequentially.
     * 4. Inserts simplified syllable separators.
     * 5. Marks primary stress on the first syllable.
     *
     * Limitations:
     * - This method provides an approximation, not a linguistically
     *   complete IPA transcription.
     * - Stress placement and syllabification are simplified.
     *
     * @param string $word The English word to convert.
     * @return string The IPA representation of the word.
     */
    public function toIPA($word)
    {
        $word = strtolower(trim($word));

        // Check exceptions first
        if (isset($this->exceptions[$word])) {
            return $this->exceptions[$word];
        }

        $ipa = $word;

        // Apply ipaPatterns
        foreach ($this->ipaPatterns as $pattern => $replacement) {
            $ipa = preg_replace($pattern, $replacement, $ipa);
        }

        // Add syllable separators (simplified heuristic)
        $ipa = preg_replace(
            '/([aeiouəɑɔɪʊɛæʌɚɝ])([^aeiouəɑɔɪʊɛæʌɚɝ\\.])([aeiouəɑɔɪʊɛæʌɚɝ])/',
            '$1.$2$3',
            $ipa
        );

        // Add primary stress to the first syllable
        $ipa = preg_replace('/^([^\.]+)/', 'ˈ$1', $ipa);

        return $ipa;
    }

    /**
     * Fetches all languages based on provided scopes.
     */
    public function getAllLanguages($scopes, $toArray=false) { 
        $query = Language::query()
            ->when(!empty($scopes['language_id']), function ($q) use ($scopes) {
                $q->where('id', $scopes['language_id']);
            })
            ->when(!empty($scopes['language_code']), function ($q) use ($scopes) {
                $q->where('language_code', $scopes['language_code']);
            })
            ->when(!empty($scopes['language_name']), function ($q) use ($scopes) {
                $q->where('language_name', 'LIKE', '%' . $scopes['language_name'] . '%');
            })
            ->when(!empty($scopes['native_name']), function ($q) use ($scopes) {
                $q->where('native_name', 'LIKE', '%' . $scopes['native_name'] . '%');
            })
            ->when(!empty($scopes['language_family']), function ($q) use ($scopes) {
                $q->where('language_family', 'LIKE', '%' . $scopes['language_family'] . '%');
            })
            ->when(!empty($scopes['total_speakers_min']), function ($q) use ($scopes) {
                $q->where('total_speakers', '>=', $scopes['total_speakers_min']);
            })
            ->orderBy('language_name', 'ASC')
            ->get();

        return $toArray ? $query->toArray() : $query;
    }
}
