<?php

namespace App\Classes;

use App\Models\Currency;
use App\Models\Lang;
use App\Models\Settings;
use App\Models\SubscriptionPlan;
use App\Models\StaffMember;
use App\Scopes\CompanyScope;
use Carbon\Carbon;
use Carbon\CarbonTimeZone;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Nwidart\Modules\Facades\Module;
use Vinkla\Hashids\Facades\Hashids;

class Common
{
    public static function getFolderPath($type = null)
    {
        $paths = [
            'companyLogoPath' => 'companies',
            'userImagePath' => 'user',
            'assetImagePath' => 'assets',
            'langImagePath' => 'langs',
            'expenseBillPath' => 'expenses',
            'policyDocumentPath' => 'policyDocuments',
            'applicationResumePath' => 'application',
            'accountingFilePath' => 'accountings',
            'appreciationImagePath' => 'appreciation',
            'applicationImagePath' => 'application',
            'frontJobDetailImagePath' => 'jobDetail',
            'proffOfDocumentPath' => 'complaint',
            'pdfFontFilePath' => 'fonts',
            'websiteImagePath' => 'website',
            'offlineRequestDocumentPath' => 'offline-requests',
            'userDocumentPath' => 'userDocument',
            'DocumentPath' => 'documents',
        ];

        return ($type == null) ? $paths : $paths[$type];
    }

    /**
     * Get allowed file types and size limits for each folder type
     */
    public static function getAllowedFileTypes($folderType)
    {
        $allowedTypes = [
            'userImagePath' => ['mimes' => ['image/jpeg', 'image/png', 'image/gif', 'image/webp'], 'extensions' => ['jpg', 'jpeg', 'png', 'gif', 'webp'], 'max_size' => 5120], // 5MB
            'companyLogoPath' => ['mimes' => ['image/jpeg', 'image/png', 'image/gif', 'image/webp'], 'extensions' => ['jpg', 'jpeg', 'png', 'gif', 'webp'], 'max_size' => 5120], // 5MB
            'langImagePath' => ['mimes' => ['image/jpeg', 'image/png', 'image/gif', 'image/webp'], 'extensions' => ['jpg', 'jpeg', 'png', 'gif', 'webp'], 'max_size' => 5120], // 5MB
            'expenseBillPath' => ['mimes' => ['image/jpeg', 'image/png', 'application/pdf'], 'extensions' => ['jpg', 'jpeg', 'png', 'pdf'], 'max_size' => 10240], // 10MB
            'policyDocumentPath' => ['mimes' => ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'], 'extensions' => ['pdf', 'doc', 'docx'], 'max_size' => 10240], // 10MB
            'assetImagePath' => ['mimes' => ['image/jpeg', 'image/png', 'image/gif'], 'extensions' => ['jpg', 'jpeg', 'png', 'gif'], 'max_size' => 5120], // 5MB
            'applicationResumePath' => ['mimes' => ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'], 'extensions' => ['pdf', 'doc', 'docx'], 'max_size' => 10240], // 10MB
            'accountingFilePath' => ['mimes' => ['application/pdf', 'image/jpeg', 'image/png'], 'extensions' => ['pdf', 'jpg', 'jpeg', 'png'], 'max_size' => 10240], // 10MB
            'appreciationImagePath' => ['mimes' => ['image/jpeg', 'image/png', 'image/gif'], 'extensions' => ['jpg', 'jpeg', 'png', 'gif'], 'max_size' => 5120], // 5MB
            'applicationImagePath' => ['mimes' => ['image/jpeg', 'image/png', 'image/gif'], 'extensions' => ['jpg', 'jpeg', 'png', 'gif'], 'max_size' => 5120], // 5MB
            'frontJobDetailImagePath' => ['mimes' => ['image/jpeg', 'image/png', 'image/gif'], 'extensions' => ['jpg', 'jpeg', 'png', 'gif'], 'max_size' => 5120], // 5MB
            'proffOfDocumentPath' => ['mimes' => ['image/jpeg', 'image/png', 'application/pdf'], 'extensions' => ['jpg', 'jpeg', 'png', 'pdf'], 'max_size' => 10240], // 10MB
            'pdfFontFilePath' => ['mimes' => ['application/x-font-ttf', 'application/x-font-truetype', 'font/ttf'], 'extensions' => ['ttf'], 'max_size' => 2048], // 2MB
            'websiteImagePath' => ['mimes' => ['image/jpeg', 'image/png', 'image/gif', 'image/webp'], 'extensions' => ['jpg', 'jpeg', 'png', 'gif', 'webp'], 'max_size' => 5120], // 5MB
            'offlineRequestDocumentPath' => ['mimes' => ['application/pdf', 'image/jpeg', 'image/png'], 'extensions' => ['pdf', 'jpg', 'jpeg', 'png'], 'max_size' => 10240], // 10MB
            'userDocumentPath' => ['mimes' => ['application/pdf', 'image/jpeg', 'image/png', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'], 'extensions' => ['pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx'], 'max_size' => 10240], // 10MB
            'DocumentPath' => ['mimes' => ['application/pdf', 'image/jpeg', 'image/png', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'], 'extensions' => ['pdf', 'jpg', 'jpeg', 'png', 'doc', 'docx'], 'max_size' => 10240], // 10MB
        ];

        return $allowedTypes[$folderType] ?? ['mimes' => ['application/octet-stream'], 'extensions' => [], 'max_size' => 5120];
    }

    public static function uploadFile($request)
    {
        $folder = $request->folder;
        $folderString = "";

        if ($folder == "user") {
            $folderString = "userImagePath";
        } else if ($folder == "company") {
            $folderString = "companyLogoPath";
        } else if ($folder == "langs") {
            $folderString = "langImagePath";
        } else if ($folder == "expenses") {
            $folderString = "expenseBillPath";
        } else if ($folder == "policyDocuments") {
            $folderString = "policyDocumentPath";
        } else if ($folder == "assets") {
            $folderString = "assetImagePath";
        } else if ($folder == "application") {
            $folderString = "applicationResumePath";
        } else if ($folder == "accountings") {
            $folderString = "accountingFilePath";
        } else if ($folder == "appreciation") {
            $folderString = "appreciationImagePath";
        } else if ($folder == "application") {
            $folderString = "applicationImagePath";
        } else if ($folder == "jobDetail") {
            $folderString = "frontJobDetailImagePath";
        } else if ($folder == "complaint") {
            $folderString = "proffOfDocumentPath";
        } else if ($folder == "fonts") {
            $folderString = "pdfFontFilePath";
        } else if ($folder == "website") {
            $folderString = "websiteImagePath";
        } else if ($folder == "offline-requests") {
            $folderString = "offlineRequestDocumentPath";
        }else if ($folder == "userDocument") {
            $folderString = "userDocumentPath";
        }else if ($folder == "documents") {
            $folderString = "DocumentPath";
        }

        $folderPath = self::getFolderPath($folderString);
        $allowedTypes = self::getAllowedFileTypes($folderString);

        if ($request->hasFile('image') || $request->hasFile('file')) {
            $largeLogo  = $request->hasFile('image') ? $request->file('image') : $request->file('file');

            // Validate file extension
            $originalExtension = strtolower($largeLogo->getClientOriginalExtension());
            if (!in_array($originalExtension, $allowedTypes['extensions'])) {
                throw new \Exception('File type not allowed. Allowed types: ' . implode(', ', $allowedTypes['extensions']));
            }

            // Validate MIME type
            $mimeType = $largeLogo->getMimeType();
            if (!in_array($mimeType, $allowedTypes['mimes'])) {
                throw new \Exception('File MIME type not allowed. Detected: ' . $mimeType);
            }

            // Validate file size (in KB)
            $maxSizeBytes = $allowedTypes['max_size'] * 1024;
            if ($largeLogo->getSize() > $maxSizeBytes) {
                throw new \Exception('File size exceeds maximum allowed size of ' . $allowedTypes['max_size'] . ' KB');
            }

            // Sanitize filename - remove any potentially dangerous characters
            $sanitizedFolder = preg_replace('/[^a-z0-9_-]/i', '', $folder);
            $fileName = $sanitizedFolder . '_' . strtolower(Str::random(20)) . '.' . $originalExtension;
            
            $largeLogo->storePubliclyAs($folderPath, $fileName);
        } else {
            throw new \Exception('No file provided');
        }

        return [
            'file' => $fileName,
            'file_url' => self::getFileUrl($folderPath, $fileName),
        ];
    }

    public static function checkFileExists($folderString, $fileName)
    {
        $folderPath = self::getFolderPath($folderString);

        $fullPath = $folderPath . '/' . $fileName;

        return Storage::exists($fullPath);
    }

    public static function getFileUrl($folderPath, $fileName, $pathType = 'asset')
    {
        if (config('filesystems.default') == 's3') {
            $path = $folderPath . '/' . $fileName;

            return Storage::url($path);
        } else {
            $path =  'uploads/' . $folderPath . '/' . $fileName;

            return $pathType == 'public' ? public_path($path) : asset($path);
        }
    }

    public static function moduleInformations()
    {
        $allModules = Module::all();
        $allEnabledModules = Module::allEnabled();
        $installedModules = [];
        $enabledModules = [];

        foreach ($allModules as $key => $allModule) {
            $modulePath = $allModule->getPath();
            $versionFileName = app_type() == 'saas' ? 'superadmin_version.txt' : 'version.txt';
            $version = File::get($modulePath . '/' . $versionFileName);

            $installedModules[] = [
                'verified_name' => $key,
                'current_version' => preg_replace("/\r|\n/", "", $version)
            ];
        }

        foreach ($allEnabledModules as $allEnabledModuleKey => $allEnabledModule) {
            $enabledModules[] = $allEnabledModuleKey;
        }

        return [
            'installed_modules' => $installedModules,
            'enabled_modules' => $enabledModules,
        ];
    }

    public static function getIdFromHash($hash)
    {
        if ($hash != "") {
            $convertedId = Hashids::decode($hash);
            $id = $convertedId[0];

            return $id;
        }

        return $hash;
    }

    public static function getHashFromId($id)
    {
        $id = Hashids::encode($id);

        return $id;
    }

    public static function formatSizeUnits($bytes)
    {
        if ($bytes >= 1073741824) {
            $bytes = number_format($bytes / 1073741824, 2) . ' GB';
        } elseif ($bytes >= 1048576) {
            $bytes = number_format($bytes / 1048576, 2) . ' MB';
        } elseif ($bytes >= 1024) {
            $bytes = number_format($bytes / 1024, 2) . ' KB';
        } elseif ($bytes > 1) {
            $bytes = $bytes . ' bytes';
        } elseif ($bytes == 1) {
            $bytes = $bytes . ' byte';
        } else {
            $bytes = '0 bytes';
        }

        return $bytes;
    }

    public static function formatDate($date = null, $company)
    {
        $timezone = new CarbonTimeZone($company->timezone);
        $format = $company->date_format;

        return $date
            ? Carbon::parse($date)->setTimezone($timezone)->format('d-m-Y')
            : Carbon::now($timezone)->format('d-m-Y');
    }

    public static function formatAmountCurrency($currency, $amount)
    {
        if (!isset($currency['symbol']) || !isset($currency['position'])) {
            return $amount;
        }

        $symbol = $currency['symbol'];
        $position = $currency['position'];

        // Get formatting parameters from currency
        $thousandSep = $currency['thousand_separator'] ?? ',';
        $decimalSep = $currency['decimal_separator'] ?? '.';
        $decimalDigits = $currency['decimal_digits'] ?? 2;
        $hideEmptyDecimals = $currency['hide_empty_decimals'] ?? false;

        // Format the amount
        $formattedAmount = number_format(abs($amount), $decimalDigits, $decimalSep, $thousandSep);

        // Hide empty decimals if enabled and amount is a whole number
        if ($hideEmptyDecimals && floor($amount) == $amount) {
            $formattedAmount = number_format(abs($amount), 0, $decimalSep, $thousandSep);
        }

        // Add currency symbol based on position
        $formattedAmount = $position === 'front' ? $symbol . $formattedAmount : $formattedAmount . $symbol;

        return $amount < 0 ? "-{$formattedAmount}" : $formattedAmount;
    }

    public static function calculateTotalUsers($companyId, $update = false)
    {
        $totalUsers =  StaffMember::withoutGlobalScope(CompanyScope::class)
            ->where('company_id', $companyId)
            ->count('id');

        if ($update) {
            DB::table('companies')
                ->where('id', $companyId)
                ->update([
                    'total_users' => $totalUsers
                ]);
        }


        return $totalUsers;
    }


    public static function addWebsiteImageUrl($settingData, $keyName)
    {
        if ($settingData && array_key_exists($keyName, $settingData)) {
            if ($settingData[$keyName] != '') {
                $imagePath = self::getFolderPath('websiteImagePath');

                $settingData[$keyName . '_url'] = Common::getFileUrl($imagePath, $settingData[$keyName]);
            } else {
                $settingData[$keyName] = null;
                $settingData[$keyName . '_url'] = asset('images/website.png');
            }
        }

        return $settingData;
    }

    public static function convertToCollection($data)
    {
        $data = collect($data)->map(function ($item) {
            return (object) $item;
        });

        return $data;
    }

    public static function addCurrencies($company)
    {
        $newCurrency = new Currency();
        $newCurrency->company_id = $company->id;
        $newCurrency->name = 'Dollar';
        $newCurrency->code = 'USD';
        $newCurrency->symbol = '$';
        $newCurrency->position = 'front';
        $newCurrency->is_deletable = false;
        $newCurrency->save();

        $rupeeCurrency = new Currency();
        $rupeeCurrency->company_id = $company->id;
        $rupeeCurrency->name = 'Rupee';
        $rupeeCurrency->code = 'INR';
        $rupeeCurrency->symbol = '₹';
        $rupeeCurrency->position = 'front';
        $rupeeCurrency->is_deletable = false;
        $rupeeCurrency->save();

        $enLang = Lang::where('key', 'en')->first();

        $company->currency_id = $newCurrency->id;
        $company->lang_id = $enLang->id;
        $company->save();

        return $company;
    }

    public static function checkSubscriptionModuleVisibility($itemType)
    {
        $visible = true;

        if (app_type() == 'saas') {
            if ($itemType == 'user') {
                $userCounts = StaffMember::count();
                $company = company();

                $visible = $company && $company->subscriptionPlan && $userCounts < $company->subscriptionPlan->max_users ? true : false;
            }
        }

        return $visible;
    }

    public static function calculateLeaveDays($leave, string $startDate, string $endDate)
    {
        $start = Carbon::parse($startDate)->startOfDay();
        $end = Carbon::parse($endDate)->startOfDay();

        if (($leave->is_half_day == 1 || $leave->is_half_day == true) && $start->diffInDays($end) == 0.0) {

            $days = (0.5);
            return $days;
        } else {

            return $start->diffInDays($end) + 1; // inclusive count
        }
    }

    public static function allVisibleSubscriptionModules()
    {
        $visibleSubscriptionModules = [];

        if (self::checkSubscriptionModuleVisibility('user')) {
            $visibleSubscriptionModules[] = 'user';
        }

        return $visibleSubscriptionModules;
    }

    public static function insertInitSettings($company)
    {
        if ((app_type() == 'saas' && $company->is_global == 1) || (app_type() == 'non-saas' && $company->is_global == 0)) {
            $storageLocalSettingCount = Settings::withoutGlobalScope(CompanyScope::class)
                ->where('setting_type', 'storage')
                ->where('name_key', 'local')
                ->where('is_global', $company->is_global)
                ->where('company_id', $company->id)
                ->count();
            if ($storageLocalSettingCount == 0) {
                $local = new Settings();
                $local->company_id = $company->id;
                $local->setting_type = 'storage';
                $local->name = 'Local';
                $local->name_key = 'local';
                $local->status = true;
                $local->is_global = $company->is_global;
                $local->save();
            }

            $storageAwsSettingCount = Settings::withoutGlobalScope(CompanyScope::class)
                ->where('setting_type', 'storage')
                ->where('name_key', 'aws')
                ->where('is_global', $company->is_global)
                ->where('company_id', $company->id)
                ->count();
            if ($storageAwsSettingCount == 0) {
                $aws = new Settings();
                $aws->company_id = $company->id;
                $aws->setting_type = 'storage';
                $aws->name = 'AWS';
                $aws->name_key = 'aws';
                $aws->credentials = [
                    'driver' => 's3',
                    'key' => '',
                    'secret' => '',
                    'region' => '',
                    'bucket' => '',

                ];
                $aws->is_global = $company->is_global;
                $aws->save();
            }

            $smtpEmailSettingCount = Settings::withoutGlobalScope(CompanyScope::class)
                ->where('setting_type', 'email')
                ->where('name_key', 'smtp')
                ->where('is_global', $company->is_global)
                ->where('company_id', $company->id)
                ->count();
            if ($smtpEmailSettingCount == 0) {
                $smtp = new Settings();
                $smtp->company_id = $company->id;
                $smtp->setting_type = 'email';
                $smtp->name = 'SMTP';
                $smtp->name_key = 'smtp';
                $smtp->credentials = [
                    'from_name' => '',
                    'from_email' => '',
                    'host' => '',
                    'port' => '',
                    'encryption' => '',
                    'username' => '',
                    'password' => '',

                ];
                $smtp->is_global = $company->is_global;
                $smtp->save();
            }
        }

        if ($company->is_global == 0) {

            $sendMailSettingCount = Settings::withoutGlobalScope(CompanyScope::class)
                ->where('setting_type', 'send_mail_settings')
                ->where('name_key', 'company')
                ->where('is_global', 0)
                ->where('company_id', $company->id)
                ->count();
            if ($sendMailSettingCount == 0) {
                $sendMailSettings = new Settings();
                $sendMailSettings->company_id = $company->id;
                $sendMailSettings->setting_type = 'send_mail_settings';
                $sendMailSettings->name = 'Send mail to company';
                $sendMailSettings->name_key = 'company';
                $sendMailSettings->credentials = [];
                $sendMailSettings->save();
            }

            $shortcutMenuSettingCount = Settings::withoutGlobalScope(CompanyScope::class)
                ->where('setting_type', 'shortcut_menus')
                ->where('name_key', 'shortcut_menus')
                ->where('is_global', 0)
                ->where('company_id', $company->id)
                ->count();
            if ($shortcutMenuSettingCount == 0) {
                // Create Menu Setting
                $setting = new Settings();
                $setting->company_id = $company->id;
                $setting->setting_type = 'shortcut_menus';
                $setting->name = 'Add Menu';
                $setting->name_key = 'shortcut_menus';
                $setting->credentials = [
                    'user',
                    'currency',
                    'language',
                    'role',
                ];
                $setting->status = 1;
                $setting->save();
            }

            // Seed for quotations
            NotificationSeed::seedAllModulesNotifications($company->id);
        }
    }

    public static function assignCompanyForNonSaas($company)
    {
        $adminUser = StaffMember::first();
        $company->admin_id = $adminUser->id;
        // Setting Trial Plan
        if (app_type() == 'saas') {
            $trialPlan = SubscriptionPlan::where('default', 'trial')->first();
            if ($trialPlan) {
                $company->subscription_plan_id = $trialPlan->id;
                // set company license expire date
                $company->licence_expire_on = Carbon::now()->addDays($trialPlan->duration)->format('Y-m-d');
            }
        }
        $company->save();

        // Insert records in settings table
        // For inital settings like email, storage
        Common::insertInitSettings($company);
    }

    public static function lightenHexColor($hex, $percent)
    {
        // Remove the hash symbol if it exists
        $hex = ltrim($hex, '#');

        // Convert the hex code to RGB
        $r = hexdec(substr($hex, 0, 2));
        $g = hexdec(substr($hex, 2, 2));
        $b = hexdec(substr($hex, 4, 2));

        // Calculate the new RGB values
        $r = min(255, round($r + (255 - $r) * ($percent / 100)));
        $g = min(255, round($g + (255 - $g) * ($percent / 100)));
        $b = min(255, round($b + (255 - $b) * ($percent / 100)));

        // Convert the RGB back to hex
        $lightenedHex = sprintf("#%02x%02x%02x", $r, $g, $b);

        return $lightenedHex;
    }

    public static function getIdArrayFromHash($values)
    {
        $convertedArray = [];

        foreach ($values as $value) {
            $convertedArray[] = Common::getIdFromHash($value);
        }

        return $convertedArray;
    }

    public static function generateAvatarUrl($name, $size = 128)
    {
        $name = trim((string) $name);
        $initials = self::makeInitials($name);
        $fg = self::pickColorForName($name); // hex like '33B5E5'
        $bg = $fg.'50'; // contrast text color

        // Basic sanitization for embedding into XML
        $initialsXml = htmlspecialchars($initials, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');

        $fontSize = (int) floor($size * 0.42);     // scales nicely
        $radius   = (int) floor($size / 2);
        $center   = $radius;

        $svg =  <<<SVG
                    <svg xmlns="http://www.w3.org/2000/svg" width="{$size}" height="{$size}" viewBox="0 0 {$size} {$size}" role="img" aria-label="{$initialsXml}">
                        <title>{$initialsXml}</title>
                        <defs>
                            <clipPath id="circleClip">
                                <circle cx="{$center}" cy="{$center}" r="{$radius}"/>
                            </clipPath>
                        </defs>
                        <g clip-path="url(#circleClip)">
                            <rect width="100%" height="100%" fill="#{$bg}"/>
                        </g>
                        <text x="50%" y="50%" dy="0.36em"
                                text-anchor="middle"
                                font-family="Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif"
                                font-size="{$fontSize}"
                                font-weight="700"
                                fill="#{$fg}">{$initialsXml}</text>
                    </svg>
                SVG;

        $base64 = base64_encode($svg);
        return "data:image/svg+xml;base64,{$base64}";
    }


    public static function makeInitials($name)
    {
        if ($name === '') {
            return 'U';
        }

        // Take first letter of up to first 2 words
        $words = preg_split('/\s+/u', $name, -1, PREG_SPLIT_NO_EMPTY) ?: [];
        $parts = array_slice($words, 0, 2);

        $initials = '';
        foreach ($parts as $w) {
            // Use mb_substr to support UTF-8 initials
            $initials .= mb_strtoupper(mb_substr($w, 0, 1, 'UTF-8'), 'UTF-8');
        }

        // If we still somehow have nothing, default
        return $initials !== '' ? $initials : 'U';
    }

    /**
     * Deterministic color picker based on name hash.
     */
    public static function pickColorForName($name)
    {
        // Nice, distinct palette (no leading '#')
        $palette = [
            'EF476F', // Vibrant Pink
            'FFD166', // Warm Yellow
            '06D6A0', // Aqua Green
            '118AB2', // Deep Teal
            '073B4C', // Dark Navy
            'F72585', // Magenta
            'B5179E', // Purple
            '7209B7', // Deep Violet
            '560BAD', // Indigo
            '4361EE', // Royal Blue
            '4895EF', // Sky Blue
            '4CC9F0', // Light Cyan
            'FF6B6B', // Coral Red
            'FF922B', // Orange
            'FFC300', // Golden Yellow
            '2ECC71', // Emerald Green
            '1ABC9C', // Turquoise
            '3498DB', // Modern Blue
            '9B59B6', // Soft Purple
            '34495E', // Slate Gray
        ];


        // Stable index from CRC32 (works fine here)
        $idx = abs(crc32($name !== '' ? $name : 'default')) % count($palette);
        return $palette[$idx];
    }
    
    public static function generateApiToken(): string
    {
        // Pick a random length between 20–30
        $length = rand(20, 30);

        // Generate random string
        return Str::random($length);
    } 
}
