<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Ddeboer\Imap\ConnectionInterface;
use Ddeboer\Imap\Server;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cookie;

use function str_replace;

class ZEmail extends Model
{
    use HasFactory;
    use HasFactory;
    public static function connectMailBox($imap = null): ConnectionInterface
    {
        if ($imap === null) {
            $imap = json_decode((string) config('app.settings.imap_settings'), true);
        }
        $flags = $imap['protocol'].'/'.$imap['encryption'];
        $flags = $imap['validate_cert'] ? $flags.'/validate-cert' : $flags.'/novalidate-cert';
        $server = new Server($imap['host'], $imap['port'], $flags);

        return $server->authenticate($imap['username'], $imap['password']);
    }

    public static function getMessages(string $email, $type = 'to', $deleted = []): array
    {
        if (config('app.beta_feature')) {
            return Message::getMessages($email);
        }
        if (config('app.force_db_mail')) {
            return Email::parseEmail($email, $deleted);
        }
        if (config('app.fetch_from_db')) {
            if (Email::mailToDBStatus()) {
                return Email::parseEmail($email, $deleted);
            }
            return Message::fetchMessages($email, $type, $deleted);
        }

        return Message::fetchMessages($email, $type, $deleted);
    }

    public static function deleteMessage(int $id): void
    {
        $connection = ZEmail::connectMailBox();
        $mailbox = $connection->getMailbox('INBOX');
        $mailbox->getMessage($id)->delete();
        $connection->expunge();
    }

    public static function getEmail($generate = false)
    {
        if (Cookie::has('email')) {
            return Cookie::get('email');
        }
        return $generate ? ZEmail::generateRandomEmail() : null;
    }

    public static function getEmails()
    {
        if (Cookie::has('emails')) {
            return unserialize(Cookie::get('emails'));
        }
        return [];
    }

    public static function setEmail($email): void
    {
        $emails = unserialize(Cookie::get('emails'));
        if (is_array($emails) && in_array($email, $emails)) {
            Cookie::queue('email', $email, 43800);
        }
    }

    public static function removeEmail($email): void
    {
        $emails = ZEmail::getEmails();
        $key = array_search($email, $emails);
        if ($key !== false) {
            array_splice($emails, $key, 1);
        }
        if (count($emails) > 0) {
            ZEmail::setEmail($emails[0]);
            Cookie::queue('emails', serialize($emails), 43800);
        } else {
            Cookie::queue('email', '', -1);
            Cookie::queue('emails', serialize([]), -1);
        }
    }

    public static function createCustomEmailFull($email): string
    {
        $data = explode('@', (string) $email);
        $username = $data[0];
        if (strlen($username) < json_decode((string) config('app.settings.configuration_settings'))->custom_username_length_min || strlen($username) > json_decode((string) config('app.settings.configuration_settings'))->custom_username_length_max) {
            $zemail = new ZEmail;
            $username = $zemail->generateRandomUsername();
        }
        $domain = $data[1];

        return ZEmail::createCustomEmail($username, $domain);
    }

    public static function createCustomEmail($username, string $domain): string
    {
        $username = preg_replace('/[^a-zA-Z0-9+.]/', '', strtolower((string) $username));

        $settings = json_decode((string) config('app.settings.configuration_settings'), true);
        $forbidden_ids = $settings['forbidden_ids'] ?? [];
        $gmail_usernames = $settings['gmailUsernames'] ?? [];
        $outlook_usernames = $settings['outlookUsernames'] ?? [];
        $domains = $settings['domains'] ?? [];

        // Check username length limits
        $min_length = $settings['custom_username_length_min'] ?? 3;
        $max_length = $settings['custom_username_length_max'] ?? 20;

        if (strlen((string) $username) < $min_length || strlen((string) $username) > $max_length) {
            $zemail = new ZEmail;
            $username = $zemail->generateRandomUsername();
        }

        if (in_array($username, $forbidden_ids)) {
            return ZEmail::generateRandomEmail(true);
        }

        if ($username === '' && in_array($domain, ['gmail.com', 'googlemail.com'])) {
            return ZEmail::generateRandomGmail(true);
        }

        if ($username === '' && $domain === 'outlook.com') {
            return ZEmail::generateRandomOutlook(true);
        }

        $zemail = new ZEmail;

        if ($username === '' && in_array($domain, $domains)) {
            return $zemail->generateRandomUsername().'@'.$domain;
        }

        if ($domain === 'outlook.com') {
            if (str_contains((string) $username, '+')) {
                [$check_username, $post_username] = explode('+', (string) $username, 2);

                if (in_array($check_username, $outlook_usernames)) {
                    $email = $username.'@'.$domain;
                } else {
                    $email = $zemail->getRandomOutlookUser().'+'.$post_username.'@'.$domain;
                }
            } else {
                $email = $zemail->getRandomOutlookUser().'+'.$username.'@'.$domain;
            }
            ZEmail::storeEmail($email);

            return $email;
        }

        if (in_array($domain, ['gmail.com', 'googlemail.com'])) {
            if (str_contains((string) $username, '+')) {
                [$check_username, $post_username] = explode('+', (string) $username, 2);

                if (in_array($check_username, $gmail_usernames)) {
                    $email = $username.'@'.$domain;
                } else {
                    $email = $zemail->getRandomGmailUser().'+'.$post_username.'@'.$domain;
                }

            } elseif (str_contains((string) $username, '.')) {
                $check_username = str_replace('.', '', $username);

                if (in_array($check_username, $gmail_usernames)) {
                    $email = $username.'@'.$domain;
                } else {
                    $email = $zemail->generateRandomGmail().'@'.$domain;
                }

            } else {
                $email = $zemail->getRandomGmailUser().'+'.$username.'@'.$domain;
            }

            ZEmail::storeEmail($email);

            return $email;
        }

        // Handle other custom domains
        if (! in_array($domain, $domains)) {
            return ZEmail::generateRandomEmail(true);
        }

        $finalDomain = in_array($domain, $domains) ? $domain : ($domains[0] ?? 'example.com');
        $email = $username.'@'.$finalDomain;

        ZEmail::storeEmail($email);

        return $email;
    }

    public static function generateRandomEmail($store = true): string
    {
        $zemail = new ZEmail;
        $domain = $zemail->getRandomDomain();
        if ($domain == 'gmail.com') {
            $rd = mt_rand(0, 1);
            if ($rd === 0) {
                $email = $zemail->generateRandomGmail();
            } else {
                $email = $zemail->getRandomGmailUser().'+'.$zemail->generateRandomUsername().'@gmail.com';
            }
        } elseif ($domain == 'googlemail.com') {
            $rd = mt_rand(0, 1);
            if ($rd === 0) {
                $email = $zemail->generateRandomGmail();
            } else {
                $email = $zemail->getRandomGmailUser().'+'.$zemail->generateRandomUsername().'@googlemail.com';
            }
        } elseif ($domain == 'outlook.com') {
            $email = $zemail->getRandomOutlookUser().'+'.$zemail->generateRandomUsername().'@outlook.com';
        } else {
            $email = $zemail->generateRandomUsername().'@'.$domain;
        }
        if ($store) {
            ZEmail::storeEmail($email);
        }

        return $email;
    }

    public static function generateRandomGmail($store = true): string
    {
        $zemail = new ZEmail;
        $uname = $zemail->getRandomGmailUser();
        $uname_len = strlen((string) $uname);
        $len_power = $uname_len - 1;
        $combination = 2 ** $len_power;
        mt_rand(1, $combination);
        $formatted = implode(' ', str_split((string) $uname));
        $uname_exp = explode(' ', $formatted);

        $bin = intval('');
        for ($i = 0; $i < $len_power; $i++) {
            $bin .= mt_rand(0, 1);
        }
        $bin = explode(' ', implode(' ', str_split(strval($bin))));

        $email = '';
        for ($i = 0; $i < $len_power; $i++) {
            $email .= $uname_exp[$i];
            if ($bin[$i] !== '' && $bin[$i] !== '0') {
                $email .= '.';
            }
        }
        $email .= $uname_exp[$i];
        $gmail_rand = mt_rand(1, 10);
        if ($gmail_rand > 5) {
            $email .= '@gmail.com';
        } else {
            $email .= '@googlemail.com';
        }
        if ($store) {
            ZEmail::storeEmail($email);
        }

        return $email;
    }

    public static function generateRandomOutlook($store = true): string
    {
        $zemail = new ZEmail;
        $email = $zemail->getRandomOutlookUser().'+'.$zemail->generateRandomUsername().'@outlook.com';
        if ($store) {
            ZEmail::storeEmail($email);
        }

        return $email;
    }

    private static function storeEmail(string $email): void
    {
        Log::query()->create([
            'ip' => request()->ip(),
            'email' => $email,
        ]);
        Cookie::queue('email', $email, 43800);
        $emails = Cookie::has('emails') ? unserialize(Cookie::get('emails')) : [];
        if (! in_array($email, $emails)) {
            ZEmail::incrementEmailStats();
            $emails[] = $email;
            Cookie::queue('emails', serialize($emails), 43800);
        }
    }

    /**
     * Stats Handling Functions
     */
    public static function incrementEmailStats($count = 1): void
    {
        Meta::incrementEmailIdsCreated($count);
    }

    public static function incrementMessagesStats($count = 1): void
    {
        Meta::incrementMessagesReceived($count);
    }

    private function generateRandomUsername(): string
    {
        $start = json_decode((string) config('app.settings.configuration_settings'))->random_username_length_min ?? 0;
        $end = json_decode((string) config('app.settings.configuration_settings'))->random_username_length_max ?? 0;
        if ($start == 0 && $end == 0) {
            return $this->generatePronounceableWord();
        }

        return $this->generatedRandomBetweenLength($start, $end);
    }

    protected function generatedRandomBetweenLength($start, $end): string
    {
        $length = random_int($start, $end);

        return $this->generateRandomString($length);
    }

    private function getRandomDomain()
    {
        $domains = json_decode((string) config('app.settings.configuration_settings'))->domains ?? [];
        $count = count($domains);

        return $count > 0 ? $domains[random_int(1, $count) - 1] : '';
    }

    private function getRandomGmailUser()
    {
        $gmailusername = json_decode((string) config('app.settings.configuration_settings'))->gmailUsernames ?? [];

        $count = count($gmailusername);

        return $count > 0 ? $gmailusername[random_int(1, $count) - 1] : '';
    }

    private function getRandomOutlookUser()
    {
        $outlook_username = json_decode((string) config('app.settings.configuration_settings'))->outlookUsernames ?? [];
        $count = count($outlook_username);

        return $count > 0 ? $outlook_username[random_int(1, $count) - 1] : '';
    }

    private function generatePronounceableWord(): string
    {
        $c = 'bcdfghjklmnprstvwz'; // consonants except hard to speak ones
        $v = 'aeiou';              // vowels
        $a = $c.$v;                // both
        $random = '';
        for ($j = 0; $j < 2; $j++) {
            $random .= $c[random_int(0, strlen($c) - 1)];
            $random .= $v[random_int(0, strlen($v) - 1)];
            $random .= $a[random_int(0, strlen($a) - 1)];
        }

        return $random;
    }

    private function generateRandomString(int $length = 10): string
    {
        $characters = '0123456789abcdefghijklmnopqrstuvwxyz';
        $charactersLength = strlen($characters);
        $randomString = '';
        for ($i = 0; $i < $length; $i++) {
            $randomString .= $characters[random_int(0, $charactersLength - 1)];
        }

        return $randomString;
    }
}
