<?php
namespace App\Http\Services;


use App\Models\Core\Setting;
use App\Enums\StatusEnum;
use App\Models\Core\File as CoreFile;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use App\Traits\Fileable;
use Illuminate\Validation\Rule;
class SettingService
{

    use Fileable;

    /**
     * update  settings
     * @param array $request_data
     */
    public function updateSettings(array $request_data) :void {


        $json_keys = Arr::get(config('settings'),'json_object' ,[]);

        foreach(($request_data) as $key=>$value){

            if(in_array($key , $json_keys)){
                $value = json_encode($value);
            }

            try {
                Setting::updateOrInsert(
                    ['key'    => $key],
                    ['value'  => $value]
                );
            } catch (\Throwable $th) {

            }
        }

        Cache::forget('site_settings');

    }


    /**
     * logo settings
     *
     * @param Request $request
     * @return void
     */
    public function logoSettings(array $request) :void{


        $logoSections =  Arr::get(config('settings'),'logo_keys' ,[]);

        foreach($logoSections as $key){

            if(isset($request['site_settings'][$key]) && is_file($request['site_settings'][$key]->getPathname())){
                $setting   = Setting::with('file')
                                     ->where('key',$key)
                                     ->first();
                if(!$setting){
                    $setting = Setting::create([
                        "key" => $key
                    ]);
                }
                $oldFile   = $setting->file()?->where('type',$key)->first();

                $response  = $this->storeFile(
                    file        : $request['site_settings'][$key],
                    location    : config("settings")['file_path'][$key]['path'],
                    removeFile  : $oldFile ?? $oldFile
                );

                if(isset($response['status'])){

                    $image = new CoreFile([
                        'name'      => Arr::get($response, 'name', '#'),
                        'disk'      => Arr::get($response, 'disk', 'local'),
                        'type'      => $key,
                        'size'      => Arr::get($response, 'size', ''),
                        'extension' => Arr::get($response, 'extension', ''),
                    ]);
                    $setting->value = Arr::get( $response ,'name',"#");
                    $setting->save();
                    $setting->file()->save($image);
                }

            }
        }

    }


    /**
     * settings validations
     * @return array
     */
    public function validationRules(array $request_data ,string $key = 'site_settings') :array{

        $rules      = [];
        $message    = [];

        $numreicKey = ["expired_data_delete_after","pagination_number",'vistors','web_route_rate_limit','api_route_rate_limit' ,'max_login_attemtps','otp_expired_in','default_max_result','ai_result_length'];

        foreach(array_keys($request_data) as $data){

            if(in_array($data ,$numreicKey)){

                $rules[$key.".".$data] =  $data == "default_max_result" ? ['required','numeric','gt:-2','max:50000'] :['required','numeric','gt:0','max:50000'];
            }
            else{
                $rules[$key.".".$data] = ['required'];
            }

            $message[$key.".".$data.'.required'] = ucfirst(str_replace('_',' ',$data)).' '.translate('Feild is Required');
        }


        return [

            'rules'   => $rules,
            'message' => $message
        ];
    }

    /**
     * Update Status
     *
     * @param $request
     * @return array
     */
    public function statusUpdate(Request $request) :array{


        $request->validate([
            "key"              => ["required","max:155"],
            "status"           => ["required",Rule::in(StatusEnum::toArray())],
        ]);

        if( $request->input('key') =='force_ssl' &&   $request->input("status") == (StatusEnum::true)->status()  && !$request->secure()){

            return [
                'status'  => false,
                'message' => translate('Your request is not secure!! to enable this module'),
            ];

        }

        $response['status']    = false;
        $response['message']   = translate('Some thing went wrong!!');

        try {

            Setting::updateOrInsert(
                ['key'   => $request->input('key')],
                ['value' =>  $request->input("status")]
            );

            if($request->input('key') == 'app_debug'){
                if($request->input("status") ==  (StatusEnum::true)->status()){
                    update_env('APP_DEBUG',"true");
                }
                else{
                    update_env('APP_DEBUG',"false");
                }
            }

            $response['status']  = true;
            $response['message'] = translate('Status Updated Successfully');

        } catch (\Exception $ex) {
            $response['message']  = $ex->getMessage();
        }

        Cache::forget('site_settings');
        return $response;
    }



    /**
     *
     * @param $request
     *
     */
    public function customPrompt(Request $request ,string $key = "ticket_settings") :array{

        $status             =  false;
        $promptInputs       = [];
        foreach ($request->input('custom_inputs') as $index => $field) {
            $newField = $field;
            if (is_null($field['name'])) {
                $newField['name'] = t2k($newField['labels']);
            }
            $promptInputs[$index] = $newField;
        }

        $request->merge(['custom_inputs' => $promptInputs]);

        try {
            $status   =  true;
            $message  =  translate("Setting has been updated");

            Setting::updateOrInsert(
                ['key'   =>  $key],
                ['value' =>  json_encode($promptInputs)]
            );

          } catch (\Exception $exception) {

            $message = $exception->getMessage();
         }

         return [
            'status'=>  $status,
            'message'=>  $message,
         ];
    }

    /**
     * Test email configuration
     * @param array $emailConfig
     * @return void
     * @throws \Exception
     */
    public function testEmailConfiguration(array $emailConfig): void
    {
        // Configure mail settings temporarily for testing
        config([
            'mail.default' => 'smtp',
            'mail.mailers.smtp.transport' => 'smtp',
            'mail.mailers.smtp.host' => $emailConfig['smtp_host'],
            'mail.mailers.smtp.port' => (int)$emailConfig['smtp_port'],
            'mail.mailers.smtp.username' => $emailConfig['smtp_username'],
            'mail.mailers.smtp.password' => $emailConfig['smtp_password'],
            'mail.mailers.smtp.encryption' => $emailConfig['smtp_encryption'] === 'none' ? null : $emailConfig['smtp_encryption'],
            'mail.from.address' => $emailConfig['mail_from_address'],
            'mail.from.name' => $emailConfig['mail_from_name'] ?? 'Test Sender',
        ]);

        // Set timeout for SMTP connection
        config([
            'mail.mailers.smtp.timeout' => 30,
            'mail.mailers.smtp.auth_mode' => null,
        ]);

        // Send test email
        $testEmail = $emailConfig['test_email'];

        try {
            // Try using the Mailable class first
            $testMail = new \App\Mail\TestEmail();

            \Illuminate\Support\Facades\Mail::to($testEmail)
                ->send($testMail);

            // If we reach here, email was sent successfully

        } catch (\Swift_TransportException $e) {
            // Handle Swift Mailer transport exceptions
            throw new \Exception('SMTP connection failed: ' . $e->getMessage() . '. Please check your SMTP settings.');
        } catch (\Symfony\Component\Mailer\Exception\TransportException $e) {
            // Handle Symfony Mailer transport exceptions (Laravel 9+)
            throw new \Exception('SMTP connection failed: ' . $e->getMessage() . '. Please check your SMTP settings.');
        } catch (\Exception $e) {
            // Check for common SMTP errors
            $message = $e->getMessage();

            if (stripos($message, 'authentication') !== false) {
                throw new \Exception('SMTP authentication failed. Please check your username and password.');
            } elseif (stripos($message, 'connection') !== false || stripos($message, 'could not establish') !== false) {
                throw new \Exception('Could not connect to SMTP server. Please check host, port, and encryption settings.');
            } elseif (stripos($message, 'ssl') !== false || stripos($message, 'tls') !== false) {
                throw new \Exception('SSL/TLS connection failed. Try changing the encryption setting or port number.');
            } else {
                throw new \Exception('Email sending failed: ' . $message);
            }
        }
    }

    /**
     * Export user data
     * @return string
     * @throws \Exception
     */
    public function exportUserData(): string
    {
        try {
            $user = auth()->user();
            $exportData = [
                'user_info' => [
                    'name' => $user->name,
                    'email' => $user->email,
                    'created_at' => $user->created_at,
                ],
                'settings' => Setting::all()->pluck('value', 'key')->toArray(),
                'export_date' => now(),
            ];

            $filename = 'user_data_export_' . date('Y-m-d_H-i-s') . '.json';
            $filepath = storage_path('app/exports/' . $filename);

            // Create exports directory if it doesn't exist
            if (!file_exists(dirname($filepath))) {
                mkdir(dirname($filepath), 0755, true);
            }

            file_put_contents($filepath, json_encode($exportData, JSON_PRETTY_PRINT));

            return $filepath;
        } catch (\Exception $e) {
            throw new \Exception('Data export failed: ' . $e->getMessage());
        }
    }

    /**
     * Create backup
     * @param string $type
     * @return string
     * @throws \Exception
     */
    public function createBackup(string $type): string
    {
        try {
            $backupDir = storage_path('app/backups');
            if (!file_exists($backupDir)) {
                mkdir($backupDir, 0755, true);
            }

            $timestamp = date('Y-m-d_H-i-s');
            $backupPath = $backupDir . '/' . $type . '_backup_' . $timestamp;

            switch ($type) {
                case 'full':
                    $this->createFullBackup($backupPath);
                    break;
                case 'database':
                    $this->createDatabaseBackup($backupPath);
                    break;
                case 'files':
                    $this->createFilesBackup($backupPath);
                    break;
                case 'settings':
                    $this->createSettingsBackup($backupPath);
                    break;
                default:
                    throw new \Exception('Invalid backup type');
            }

            return $backupPath;
        } catch (\Exception $e) {
            throw new \Exception('Backup creation failed: ' . $e->getMessage());
        }
    }

    /**
     * Restore backup
     * @param string $backupPath
     * @return void
     * @throws \Exception
     */
    public function restoreBackup(string $backupPath): void
    {
        try {
            if (!file_exists($backupPath)) {
                throw new \Exception('Backup file not found: ' . $backupPath);
            }

            $backupDir = storage_path('app/backups');
            $backupName = basename($backupPath);

            // Determine backup type from filename
            if (strpos($backupName, '_db') !== false) {
                $this->restoreDatabaseBackup($backupPath);
            } elseif (strpos($backupName, '_files') !== false) {
                $this->restoreFilesBackup($backupPath);
            } elseif (strpos($backupName, '_settings') !== false) {
                $this->restoreSettingsBackup($backupPath);
            } elseif (strpos($backupName, 'full_backup') !== false) {
                // For full backup, restore all components
                $this->restoreDatabaseBackup($backupPath . '_db.sql');
                $this->restoreFilesBackup($backupPath . '_files.zip');
                $this->restoreSettingsBackup($backupPath . '_settings.json');
            } else {
                throw new \Exception('Unknown backup type. Cannot determine restore method.');
            }

        } catch (\Exception $e) {
            throw new \Exception('Backup restore failed: ' . $e->getMessage());
        }
    }

    /**
     * Delete backup file(s)
     * @param string $backupPath
     * @return void
     * @throws \Exception
     */
    public function deleteBackup(string $backupPath): void
    {
        try {
            if (!file_exists($backupPath)) {
                throw new \Exception('Backup file not found: ' . $backupPath);
            }

            $backupName = basename($backupPath);

            // For full backup, delete all related files
            if (strpos($backupName, 'full_backup') !== false) {
                $relatedFiles = [
                    $backupPath . '_db.sql',
                    $backupPath . '_files.zip',
                    $backupPath . '_settings.json'
                ];

                foreach ($relatedFiles as $file) {
                    if (file_exists($file)) {
                        unlink($file);
                    }
                }
            } else {
                // Delete single backup file
                unlink($backupPath);
            }

        } catch (\Exception $e) {
            throw new \Exception('Backup deletion failed: ' . $e->getMessage());
        }
    }

    /**
     * Get list of available backups
     * @return array
     */
    public function getBackupList(): array
    {
        try {
            $backupDir = storage_path('app/backups');
            $backups = [];

            if (!is_dir($backupDir)) {
                return $backups;
            }

            $files = glob($backupDir . '/*');

            foreach ($files as $file) {
                if (is_file($file)) {
                    $filename = basename($file);
                    $filesize = filesize($file);
                    $created = filemtime($file);

                    // Determine backup type
                    $type = 'unknown';
                    if (strpos($filename, 'full_backup') !== false) {
                        $type = 'full';
                    } elseif (strpos($filename, 'database_backup') !== false) {
                        $type = 'database';
                    } elseif (strpos($filename, 'files_backup') !== false) {
                        $type = 'files';
                    } elseif (strpos($filename, 'settings_backup') !== false) {
                        $type = 'settings';
                    }

                    // Skip component files of full backups (they have _db, _files, _settings suffixes)
                    if (preg_match('/(full_backup_\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2})_(db|files|settings)/', $filename)) {
                        continue;
                    }

                    $backups[] = [
                        'id' => md5($file),
                        'name' => $this->formatBackupName($filename),
                        'path' => $file,
                        'filename' => $filename,
                        'size' => $this->formatFileSize($filesize),
                        'date' => date('Y-m-d H:i:s', $created),
                        'type' => $type,
                        'status' => 'completed'
                    ];
                }
            }

            // Sort by creation time (newest first)
            usort($backups, function($a, $b) {
                return strcmp($b['date'], $a['date']);
            });

            return $backups;

        } catch (\Exception $e) {
            return [];
        }
    }

    /**
     * Run maintenance tasks
     * @return void
     * @throws \Exception
     */
    public function runMaintenanceTasks(): void
    {
        try {
            // Clear cache
            \Artisan::call('cache:clear');
            \Artisan::call('config:clear');
            \Artisan::call('view:clear');
            \Artisan::call('route:clear');

            // Optimize application
            \Artisan::call('optimize');

            // Clean up old log files (older than 30 days)
            $logPath = storage_path('logs');
            if (file_exists($logPath)) {
                $files = glob($logPath . '/*.log');
                foreach ($files as $file) {
                    if (filemtime($file) < time() - (30 * 24 * 60 * 60)) {
                        unlink($file);
                    }
                }
            }

        } catch (\Exception $e) {
            throw new \Exception('Maintenance tasks failed: ' . $e->getMessage());
        }
    }

    /**
     * Create full backup
     * @param string $backupPath
     * @return void
     */
    private function createFullBackup(string $backupPath): void
    {
        $this->createDatabaseBackup($backupPath . '_db');
        $this->createFilesBackup($backupPath . '_files');
        $this->createSettingsBackup($backupPath . '_settings');
    }

    /**
     * Create database backup
     * @param string $backupPath
     * @return void
     */
    private function createDatabaseBackup(string $backupPath): void
    {
        $filename = $backupPath . '.sql';
        $command = sprintf(
            'mysqldump --user=%s --password=%s --host=%s --port=%s %s > %s',
            config('database.connections.mysql.username'),
            config('database.connections.mysql.password'),
            config('database.connections.mysql.host'),
            config('database.connections.mysql.port'),
            config('database.connections.mysql.database'),
            $filename
        );
        exec($command);
    }

    /**
     * Create files backup
     * @param string $backupPath
     * @return void
     */
    private function createFilesBackup(string $backupPath): void
    {
        $filename = $backupPath . '.zip';
        $zip = new \ZipArchive();

        if ($zip->open($filename, \ZipArchive::CREATE) === TRUE) {
            $files = new \RecursiveIteratorIterator(
                new \RecursiveDirectoryIterator(storage_path('app')),
                \RecursiveIteratorIterator::LEAVES_ONLY
            );

            foreach ($files as $name => $file) {
                if (!$file->isDir()) {
                    $filePath = $file->getRealPath();
                    $relativePath = substr($filePath, strlen(storage_path('app')) + 1);
                    $zip->addFile($filePath, $relativePath);
                }
            }
            $zip->close();
        }
    }

    /**
     * Create settings backup
     * @param string $backupPath
     * @return void
     */
    private function createSettingsBackup(string $backupPath): void
    {
        $filename = $backupPath . '.json';
        $settings = Setting::all()->pluck('value', 'key')->toArray();
        file_put_contents($filename, json_encode($settings, JSON_PRETTY_PRINT));
    }

    /**
     * Restore database backup
     * @param string $backupPath
     * @return void
     * @throws \Exception
     */
    private function restoreDatabaseBackup(string $backupPath): void
    {
        if (!file_exists($backupPath)) {
            throw new \Exception('Database backup file not found');
        }

        $command = sprintf(
            'mysql --user=%s --password=%s --host=%s --port=%s %s < %s',
            config('database.connections.mysql.username'),
            config('database.connections.mysql.password'),
            config('database.connections.mysql.host'),
            config('database.connections.mysql.port'),
            config('database.connections.mysql.database'),
            $backupPath
        );

        $output = [];
        $returnVar = 0;
        exec($command, $output, $returnVar);

        if ($returnVar !== 0) {
            throw new \Exception('Database restore failed. Command exit code: ' . $returnVar);
        }
    }

    /**
     * Restore files backup
     * @param string $backupPath
     * @return void
     * @throws \Exception
     */
    private function restoreFilesBackup(string $backupPath): void
    {
        if (!file_exists($backupPath)) {
            throw new \Exception('Files backup file not found');
        }

        $zip = new \ZipArchive();
        if ($zip->open($backupPath) === TRUE) {
            $zip->extractTo(storage_path('app'));
            $zip->close();
        } else {
            throw new \Exception('Failed to open files backup archive');
        }
    }

    /**
     * Restore settings backup
     * @param string $backupPath
     * @return void
     * @throws \Exception
     */
    private function restoreSettingsBackup(string $backupPath): void
    {
        if (!file_exists($backupPath)) {
            throw new \Exception('Settings backup file not found');
        }

        $settingsData = json_decode(file_get_contents($backupPath), true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new \Exception('Invalid settings backup file format');
        }

        foreach ($settingsData as $key => $value) {
            try {
                Setting::updateOrInsert(
                    ['key' => $key],
                    ['value' => $value]
                );
            } catch (\Exception $e) {
                // Log error but continue with other settings
                \Log::warning("Failed to restore setting: {$key}", ['error' => $e->getMessage()]);
            }
        }

        // Clear cache after restoring settings
        Cache::forget('site_settings');
    }

    /**
     * Format backup name for display
     * @param string $filename
     * @return string
     */
    private function formatBackupName(string $filename): string
    {
        // Remove file extension
        $name = pathinfo($filename, PATHINFO_FILENAME);

        // Replace underscores with spaces and capitalize
        $name = str_replace('_', ' ', $name);
        $name = ucwords($name);

        return $name;
    }

    /**
     * Format file size for display
     * @param int $size
     * @return string
     */
    private function formatFileSize(int $size): string
    {
        if ($size >= 1073741824) {
            return number_format($size / 1073741824, 2) . ' GB';
        } elseif ($size >= 1048576) {
            return number_format($size / 1048576, 2) . ' MB';
        } elseif ($size >= 1024) {
            return number_format($size / 1024, 2) . ' KB';
        } else {
            return $size . ' bytes';
        }
    }

}
