اريد ان يتم تعطيل زر التسجيل في حال كان الزر في الاعلى في حالة طلب التتفعيل واريد ان يعمل في حال كان في وضع تم التفعيل وبعدها ينفذ امر جلب الرخصة وتخزينها بعد اول تخزين اريد ان يعمل الزر في حال كان رخصة الراوتر الحالي هو نفس رخصة الراوتر المخزن
للعلم الكود
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'dart:math';
import 'dart:typed_data';
import 'package:encrypt/encrypt.dart' as encrypt;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'package:micro_panel/HomePage.dart';
import 'package:micro_panel/SQLITE_HELPER/secure_storage_db.dart';
import 'package:micro_panel/main.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:router_os_client/router_os_client.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
import 'package:sqflite/sqflite.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:sqflite_common_ffi/sqflite_ffi.dart';

void main() async {
// تهيئة binding قبل أي شيء آخر
WidgetsFlutterBinding.ensureInitialized();

if (!kIsWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
sqfliteFfiInit();
databaseFactory = databaseFactoryFfi;
}

// الحصول على المسار هنا
final maindirectory = await getApplicationDocumentsDirectory();
final saveDirectory = Directory('${maindirectory.path}/MicropanelSecrt');
if (!await saveDirectory.exists()) {
await saveDirectory.create(recursive: true);
}

final appDocumentsPath = saveDirectory.path;
runApp(MicroPanelApp(documentsPath: appDocumentsPath));
}

class MicroPanelApp extends StatelessWidget {
final String documentsPath;
const MicroPanelApp({super.key, required this.documentsPath});

@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Micro Panel',
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: 'Tajawal',
useMaterial3: true,
),
home: const SplashScreen(),
);
}
}

class SplashScreen extends StatefulWidget {
const SplashScreen({super.key});

@override
State

class _SplashScreenState extends State

@override
void initState() {
super.initState();
_initAnimations();
_navigateToLogin();
}

void _initAnimations() {
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 800),
);
_scaleAnimation = Tween

void navigateToLogin() {
Future.delayed(const Duration(seconds: 1), () {
if (mounted) {
Navigator.pushReplacement(
context,
PageRouteBuilder(
pageBuilder: (, __, __) => const Login(),
transitionsBuilder:
(, animation, __, child) =>
FadeTransition(opacity: animation, child: child),
transitionDuration: const Duration(milliseconds: 500),
),
);
}
});
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF0A192F),
body: Stack(
children: [_buildBackgroundCircles(), Center(child: _buildContent())],
),
);
}

Widget _buildBackgroundCircles() {
return Stack(
children: [
Positioned(
top: -50,
right: -50,
child: Container(
width: 200,
height: 200,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF1E3A8A).withOpacity(0.2),
),
),
),
Positioned(
bottom: -100,
left: -100,
child: Container(
width: 300,
height: 300,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF1E3A8A).withOpacity(0.1),
),
),
),
],
);
}

Widget _buildContent() {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Directionality(
textDirection: TextDirection.rtl,
child: ScaleTransition(
scale: _scaleAnimation,
child: FadeTransition(
opacity: _fadeAnimation,
child: SlideTransition(
position: _slideAnimation,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildAppLogo(),
const SizedBox(height: 20),
_buildAppTitle(),
const SizedBox(height: 5),
_buildAppSubtitle(),
const SizedBox(height: 30),
_buildLoadingIndicator(),
const SizedBox(height: 15),
_buildLoadingText(),
],
),
),
),
),
),
);
}

Widget _buildAppLogo() {
return Hero(
tag: 'app-logo',
child: Container(
height: 150,
width: 150, // تأكد من أن العرض مساوٍ للارتفاع للحفاظ على الشكل الدائري
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
shape: BoxShape.circle,
border: Border.all(color: Colors.white.withOpacity(0.2), width: 1.5),
),
child: ClipOval(
child: Image.asset(
'assets/img/MicroPanel.ico',
fit: BoxFit.cover, // يجعل الصورة تملأ الحاوية بشكل متناسق
),
),
),
);
}

Widget _buildAppTitle() {
return const Text(
'Micro Panel',
style: TextStyle(
fontFamily: 'Tajawal',
fontSize: 30,
fontWeight: FontWeight.bold,
color: Colors.white,
letterSpacing: 1.2,
),
);
}

Widget _buildAppSubtitle() {
return const Text(
'نظام إدارة أجهزة الميكروتك',
style: TextStyle(
fontFamily: 'Tajawal',
color: Colors.white70,
fontSize: 14,
),
);
}

Widget _buildLoadingIndicator() {
return SizedBox(
width: 180,
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: LinearProgressIndicator(
valueColor: AlwaysStoppedAnimation

Widget _buildLoadingText() {
return const Text(
'جاري التحميل...',
style: TextStyle(
fontFamily: 'Tajawal',
color: Colors.white,
fontSize: 14,
),
);
}
}

class MikroTikApi {
Socket? _socket;
final String host;
final int port;
final String user, pass;
final _recvBuffer =

MikroTikApi({
required this.host,
this.port = 8728,
required this.user,
required this.pass,
});

Future

void _onData(Uint8List chunk) {
_recvBuffer.addAll(chunk);
}

void _onError(e) => throw 'خطأ في الاتصال';
void _onDone() => _socket = null;

Uint8List _encodeWord(String s) {
final b = utf8.encode(s);
final len = b.length;
final header =

Future<Map<String, String>?> fetchLicenseData() async {
await sendSentence(['/system/license/print']);
final resp = await _readSentences();


Map<String, String> licenseData = {};

for (var sentence in resp) {
  if (sentence.isNotEmpty && sentence.first == '!re') {
    for (var token in sentence.skip(1)) {
      if (token.startsWith('=')) {
        final parts = token.substring(1).split('=');
        if (parts.length >= 2) {
          final key = parts[0];
          final value = parts.sublist(1).join('=');
          licenseData[key] = value;
        }
      }
    }
    break;
  }
}

if (licenseData.isEmpty) return null;
return licenseData;
}

Future


final softwareId = await storage.read('license_software_id');
// افعل نفس الشيء لمفاتيح أخرى إذا تريد

return softwareId != null;
}

String encryptAES(String plainText, String keyString, String ivString) {
final key = encrypt.Key.fromUtf8(keyString);
final iv = encrypt.IV.fromUtf8(ivString);
final encrypter = encrypt.Encrypter(
encrypt.AES(key, mode: encrypt.AESMode.cbc),
);


final encrypted = encrypter.encrypt(plainText, iv: iv);
return encrypted.base64;
}

Future


for (var sentence in resp) {
  if (sentence.isNotEmpty && sentence.first == '!re') {
    for (var token in sentence.skip(1)) {
      if (token.startsWith('=version=')) {
        final fullVersion = token.substring('=version='.length);
        final majorVersion = fullVersion.split('.').first;

        final data = {'router_version': majorVersion};
        final jsonString = jsonEncode(data);

        final filePath = '$appDocumentsPath/router_version.json';
        final file = File(filePath);

        await file.writeAsString(jsonString);
        print('تم حفظ إصدار الراوتر بنجاح في: $filePath');

        return majorVersion;
      }
    }
  }
}

throw Exception('لم يتم العثور على إصدار الراوتر في الاستجابة');
}

Future<List<Map<String, dynamic>>> fetchUserManagerUsers(
String appDocumentsPath,
) async {
// جلب إصدار الراوتر أولاً
final versionString = await fetchRouterVersion(appDocumentsPath);
final version = versionString.startsWith('7') ? 7 : 6;
print('$version');


// جلب بيانات المستخدمين الأساسية
if (version == 7) {
  await sendSentence(['/user-manager/user/print']);
} else {
  await sendSentence(['/tool/user-manager/user/print']);
}

final usersResp = await _readSentences();
final users = <Map<String, dynamic>>[];

// معالجة بيانات المستخدمين الأساسية
for (var sentence in usersResp) {
  if (sentence.isNotEmpty && sentence.first == '!re') {
    final user = <String, dynamic>{};
    for (var token in sentence.skip(1)) {
      if (token.startsWith('=')) {
        final parts = token.split('=');
        if (parts.length >= 3) {
          user[parts[1]] = parts.sublist(2).join('=');
        }
      }
    }
    if (user.isNotEmpty) {
      final filteredUser =
          version == 7
              ? {
                '.id': user['.id'],
                'name': user['name'],
                'password': user['password'],
                'otp-secret': user['otp-secret'],
                'group': user['group'],
                'shared-users': user['shared-users'],
                'attributes': user['attributes'],
                'disabled': user['disabled'],
              }
              : {
                '.id': user['.id'],
                'actual-profile': user['actual-profile'] ?? '',
                'username': user['name'] ?? user['username'] ?? '',
                'password': user['password'] ?? '',
                'uptime-used': user['uptime-used'] ?? '',
                'download-used': user['download-used'] ?? '',
                'upload-used': user['upload-used'] ?? '',
                'last-seen': user['last-seen'] ?? '',
                'disabled':
                    user['disabled'] ??
                    (user['flags']?.contains('X') ?? false),
                'active': user['flags']?.contains('A') ?? false,
                'incomplete': user['flags']?.contains('I') ?? false,
              };
      users.add(filteredUser);
    }
  }
}

if (version == 7) {
  await sendSentence(['/user-manager/user-profile/print']);
  final profilesResp = await _readSentences();
  final profiles = <Map<String, String>>[];

  for (var sentence in profilesResp) {
    if (sentence.isNotEmpty && sentence.first == '!re') {
      final profile = <String, String>{};
      for (var token in sentence.skip(1)) {
        if (token.startsWith('=')) {
          final parts = token.split('=');
          if (parts.length >= 3) {
            profile[parts[1]] = parts.sublist(2).join('=');
          }
        }
      }
      if (profile.isNotEmpty) {
        profiles.add(profile);
      }
    }
  }

  for (var user in users) {
    final userName = user['name'];
    if (userName != null) {
      final matchingProfile = profiles.firstWhere(
        (profile) => profile['user'] == userName,
        orElse: () => {},
      );

      if (matchingProfile.isNotEmpty) {
        user.addAll({
          'profile': matchingProfile['profile'] ?? '',
          'state': matchingProfile['state'] ?? '',
          'end-time': matchingProfile['end-time'] ?? '',
        });
      }
    }
  }
}

final file = File('$appDocumentsPath/user_manager_users.json');
await file.writeAsString(jsonEncode(users));

return users;
}

Future

Future<List<List


  if (_recvBuffer.isEmpty) continue;

  final buf = List<int>.from(_recvBuffer);
  try {
    var i = 0;
    while (i < buf.length) {
      final sentence = <String>[];
      while (i < buf.length && buf[i] != 0) {
        int len = buf[i++];
        int bytesCount = 1;

        if (len & 0x80 != 0) {
          bytesCount =
              (len & 0xC0) == 0x80
                  ? 2
                  : (len & 0xE0) == 0xC0
                  ? 3
                  : 4;
          len = ((len & 0x7F) << ((bytesCount - 1) * 8));

          for (int j = 0; j < bytesCount - 1 && i < buf.length; j++) {
            len |= buf[i++] << ((bytesCount - 2 - j) * 8);
          }
        }

        if (i + len > buf.length) {
          i -= bytesCount;
          break;
        }

        sentence.add(utf8.decode(buf.sublist(i, i + len)));
        i += len;
      }

      if (i < buf.length && buf[i] == 0) {
        i++; // تخطي حرف الإنهاء
        sentences.add(sentence);

        // إذا كانت الجملة تحتوي على !done، نخرج
        if (sentence.isNotEmpty && sentence.first == '!done') {
          _recvBuffer.removeRange(0, i);
          return sentences;
        }
      } else {
        break; // بيانات غير مكتملة
      }
    }

    _recvBuffer.removeRange(0, i);

    // إذا وجدنا جملة !done نعود بالنتيجة
    if (sentences.any((s) => s.isNotEmpty && s.first == '!done')) {
      return sentences;
    }
  } catch (e) {
    print('Error parsing sentences');
    break;
  }
}
return sentences;
}

Future

Future<List<Map<String, String>>> fetchHotspotUsers() async {
await sendSentence(['/ip/hotspot/user/print']);
final resp = await _readSentences();
return resp.where((s) => s.first == '!re').map((s) {
final m = <String, String>{};
for (var tok in s.skip(1)) {
if (tok.startsWith('=')) {
final idx = tok.indexOf('=', 1);
if (idx != -1) {
final key = tok.substring(1, idx);
final value = tok.substring(idx + 1);
m[key] = value;
}
}
}


  return m;
}).toList();
}

void close() => _socket?.destroy();
}

enum UserType { hotspot, userManager }

class Login extends StatefulWidget {
const Login({super.key});

@override
State

class _LoginState extends State

Map<String, dynamic>? _savedCredentials;
UserType _selectedUserType = UserType.hotspot;

final PageController _pageController = PageController();
int _currentPage = 0;

bool _showText = false;

final List<Map<String, dynamic>> _slides = [
{
'image': 'assets/img/1.png',
'title': 'إدارة متكاملة',
'description': 'تحكم كامل في جميع اصدارات الميكروتك من نظام واحد',
},
{
'image': 'assets/img/2.png',
'title': 'تقارير مفصلة',
'description': 'احصل على تقارير أداء دقيقة لشبكتك',
},
];

final String firebaseUrl =
'https://micropanel-b67e0-default-rtdb.firebaseio.com/information.json';

// المفتاح (32 بايت = 256 بت)
final String aesKey =
'my32lengthsupersecretnooneknows1'; // يجب أن يكون 32 حرفًا
final String aesIV = '8bytesiv12345678'; // يجب أن يكون 16 حرفًا

String? phone;
String? Urlapi;

String decryptAES(String encryptedText, String keyString, String ivString) {
try {
final key = encrypt.Key.fromUtf8(keyString);
final iv = encrypt.IV.fromUtf8(ivString);
final encrypter = encrypt.Encrypter(
encrypt.AES(key, mode: encrypt.AESMode.cbc),
);


  // تنظيف النص المشفر من الأحرف غير الصالحة
  final cleanedEncryptedText = encryptedText
      .replaceAll('\n', '')
      .replaceAll('\r', '')
      .replaceAll(' ', '');

  final encrypted = encrypt.Encrypted.fromBase64(cleanedEncryptedText);
  final decrypted = encrypter.decrypt(encrypted, iv: iv);
  return decrypted;
} catch (e) {
  print('Error decrypting AES: $e');
  rethrow;
}
}

Future


  if (response.statusCode == 200) {
    final Map<String, dynamic> data =
        jsonDecode(response.body) as Map<String, dynamic>;

    // التحقق من وجود البيانات قبل فك التشفير
    if (data['phone'] != null && data['Urlapi'] != null) {
      phone = decryptAES(data['phone'].toString(), aesKey, aesIV);
      Urlapi = decryptAES(data['Urlapi'].toString(), aesKey, aesIV);
      print('phone: $phone');
      print('Urlapi:$Urlapi');
    } else {
      throw Exception('بيانات التشفير غير موجودة في الاستجابة');
    }
  } else {
    throw http.ClientException(
      'حدث خطأ أثناء جلب البيانات: ${response.statusCode}',
    );
  }
} catch (e, st) {
  print('Error fetching or decrypting config from Firebase: $e\n$st');
  throw Exception('فشل في جلب أو فك تشفير الإعدادات: $e');
}
}

@override
void initState() {
super.initState();
_requestFilePickerPermissions();
_loadSavedCredentials();
_startAutoSlide();
Future.delayed(Duration(milliseconds: 150), () {
fetchJsonFromFirebase();
});
checkActivation().then((isActivated) {
if (mounted) {
setState(() {
_isActivated = isActivated;
});
}
});


@override
void didPopNext() {
  checkActivation().then((isActivated) {
    if (mounted) {
      setState(() {
        _isActivated = isActivated;
      });
    }
  });
}

Future.delayed(Duration(seconds: 1), () {
  if (!mounted) return;
  setState(() => _showText = true);

  Future.delayed(Duration(seconds: 4), () {
    if (!mounted) return;
    setState(() => _showText = false);
  });
});
}

Future


  final permissions = [
    Permission.storage,
    if (Platform.isAndroid && Platform.version.contains("11"))
      Permission.manageExternalStorage,
  ];

  final statuses = await permissions.request();

  if (statuses[Permission.storage]?.isDenied ?? false) {
    // التخزين مرفوض
    print('تم رفض إذن التخزين');
  }

  if (statuses[Permission.manageExternalStorage]?.isDenied ?? false) {
    // إدارة التخزين مرفوضة
    print('تم رفض إذن إدارة التخزين');
  }

  if (statuses[Permission.manageExternalStorage]?.isPermanentlyDenied ??
      false) {
    // يجب توجيه المستخدم للإعدادات
    await openAppSettings();
  }
}
}

Future


    if (encryptedContent.isEmpty) return;

    final key = encrypt.Key.fromUtf8(aesKey);
    final iv = encrypt.IV.fromUtf8(aesIV);

    final encrypter = encrypt.Encrypter(
      encrypt.AES(key, mode: encrypt.AESMode.cbc),
    );

    final decrypted = encrypter.decrypt64(encryptedContent, iv: iv);

    final credentials = jsonDecode(decrypted);

    if (credentials != null && credentials is Map) {
      setState(() {
        _savedCredentials = Map<String, dynamic>.from(credentials);
        _hasSavedCredentials = true;
        _ipController.text = _savedCredentials?['ip'] ?? '';
        _usernameController.text = _savedCredentials?['username'] ?? '';
        _passwordController.text = _savedCredentials?['password'] ?? '';
        _portController.text = _savedCredentials?['port'] ?? '8728';

        int selectedTabIndex = _savedCredentials?['selectedTab'] ?? 0;
        _selectedUserType =
            selectedTabIndex == 0 ? UserType.hotspot : UserType.userManager;
      });
    }
  }
} catch (e) {
  print('Error loading encrypted credentials: $e');
}
}

Future


  final credentials = {
    'ip': _ipController.text,
    'username': _usernameController.text,
    'password': _passwordController.text,
    'port': _portController.text,
    'selectedTab': _selectedUserType == UserType.hotspot ? 0 : 1,
  };

  // تحويل إلى JSON String
  final plainText = jsonEncode(credentials);

  // مفاتيح مسبقة (يفضل توليدها بشكل ثابت وآمن)
  final key = encrypt.Key.fromUtf8(aesKey); // يجب أن تكون 32 بايت
  final iv = encrypt.IV.fromUtf8(aesIV); // 16 بايت

  final encrypter = encrypt.Encrypter(
    encrypt.AES(key, mode: encrypt.AESMode.cbc),
  );

  final encrypted = encrypter.encrypt(plainText, iv: iv);

  await file.writeAsString(encrypted.base64);

  setState(() {
    _hasSavedCredentials = true;
    _savedCredentials = credentials;
  });
} catch (e) {
  print('Error saving encrypted credentials: $e');
}
}

@override
void dispose() {
_ipController.dispose();
_ipFocusNode.dispose();
_usernameController.dispose();
_passwordController.dispose();
_portController.dispose();
_pageController.dispose();
super.dispose();
}

void _startAutoSlide() {
Future.delayed(Duration(seconds: 5), () {
if (_pageController.hasClients) {
final nextPage =
_currentPage < _slides.length - 1 ? _currentPage + 1 : 0;
_pageController.animateToPage(
nextPage,
duration: Duration(milliseconds: 800),
curve: Curves.easeInOutCubic,
);
_startAutoSlide();
}
});
}

@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
final isMobile = size.width < 600;
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
backgroundColor: const Color(0xFF0A192F),
body: SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 800) {
return Row(
children: [
Expanded(
flex: 5,
child: Stack(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 40),
color: const Color(0xFF0A192F),
child: Center(child: _buildLoginForm(isMobile)),
),
Positioned(
top: 10,
right: 10,
child: InkWell(
onTap:
_isActivated
? null
: () => showSerialNumberDialog(context),
child: AnimatedContainer(
height: 40,
duration: Duration(milliseconds: 800),
curve: Curves.easeInOutQuad,
decoration: BoxDecoration(
color:
_isActivated
? Colors.blue.withOpacity(0.95)
: Colors.amberAccent[400]!
.withOpacity(0.95),
borderRadius: BorderRadius.circular(25.0),
),
padding: EdgeInsets.symmetric(horizontal: 10),
alignment: Alignment.center,
child: AnimatedSize(
duration: Duration(milliseconds: 1000),
curve: Curves.easeInOutBack,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
_isActivated
? Icons.verified
: Icons.touch_app_outlined,
color: Colors.white,
size: 20,
),
AnimatedSwitcher(
duration: Duration(milliseconds: 800),
switchInCurve: Curves.easeOutCubic,
switchOutCurve: Curves.easeInCubic,
transitionBuilder:
(child, animation) =>
ScaleTransition(
scale: animation,
child: FadeTransition(
opacity: animation,
child: child,
),
),
child:
_showText || _isActivated
? Padding(
padding: EdgeInsets.only(
left: 8,
),
key: ValueKey(
_isActivated
? 'activated'
: 'text',
),
child: Text(
_isActivated
? ' تم التفعيل'
: ' طلب التفعيل',
style: TextStyle(
color: Colors.white,
fontFamily: 'Tajawal',
fontSize: 13,
fontWeight:
FontWeight.bold,
shadows: [
Shadow(
color: Colors.black
.withOpacity(0.2),
blurRadius: 2,
offset: Offset(1, 1),
),
],
),
),
)
: SizedBox.shrink(
key: ValueKey('empty'),
),
),
],
),
),
),
),
),
],
),
),
Expanded(
flex: 6,
child: Container(
decoration: const BoxDecoration(
color: Color(0xFF0A192F),
),
child: Stack(
children: [
// Image Slider
PageView.builder(
controller: _pageController,
itemCount: _slides.length,
onPageChanged: (index) {
setState(() {
_currentPage = index;
});
},
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
_slides[index]['image'],
),
fit: BoxFit.contain,
),
),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.transparent,
Colors.black.withOpacity(0.7),
],
),
),
),
);
},
),


                        // Content Overlay
                        Positioned(
                          bottom: 80,
                          left: 0,
                          right: 0,
                          child: AnimatedSwitcher(
                            duration: Duration(milliseconds: 500),
                            child: Container(
                              key: ValueKey<int>(_currentPage),
                              padding: EdgeInsets.symmetric(horizontal: 40),
                              child: Column(
                                children: [
                                  Text(
                                    _slides[_currentPage]['title'],
                                    style: TextStyle(
                                      fontFamily: 'Tajawal',
                                      color: Colors.white,
                                      fontSize: 28,
                                      fontWeight: FontWeight.bold,
                                    ),
                                  ),
                                  SizedBox(height: 15),
                                  Text(
                                    _slides[_currentPage]['description'],
                                    textAlign: TextAlign.center,
                                    style: TextStyle(
                                      fontFamily: 'Tajawal',
                                      color: Colors.white70,
                                      fontSize: 18,
                                    ),
                                  ),
                                ],
                              ),
                            ),
                          ),
                        ),

                        Positioned(
                          bottom: 40,
                          left: 0,
                          right: 0,
                          child: Center(
                            child: SmoothPageIndicator(
                              controller: _pageController,
                              count: _slides.length,
                              effect: const ExpandingDotsEffect(
                                dotHeight: 8,
                                dotWidth: 8,
                                activeDotColor: Color(0xFF64FFDA),
                                dotColor: Colors.white54,
                                spacing: 10,
                              ),
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
              ],
            );
          } else {
            return CustomScrollView(
              slivers: [
                SliverFillRemaining(
                  hasScrollBody: false,
                  child: Stack(
                    children: [
                      Container(
                        padding: const EdgeInsets.symmetric(
                          horizontal: 20,
                          vertical: 40,
                        ),
                        child: _buildLoginForm(isMobile),
                      ),
                      Positioned(
                        top: 10,
                        right: 10,
                        child: InkWell(
                          onTap: () => showSerialNumberDialog(context),
                          child: AnimatedContainer(
                            height: 40,
                            duration: Duration(milliseconds: 800),
                            curve: Curves.easeInOutQuad,
                            decoration: BoxDecoration(
                              color: Colors.amberAccent[400]!.withOpacity(
                                0.95,
                              ),
                              borderRadius: BorderRadius.circular(25.0),
                            ),
                            padding: EdgeInsets.symmetric(horizontal: 10),
                            alignment: Alignment.center,
                            child: AnimatedSize(
                              duration: Duration(milliseconds: 1000),
                              curve: Curves.easeInOutBack,
                              child: Row(
                                mainAxisSize: MainAxisSize.min,
                                children: [
                                  Icon(
                                    Icons.touch_app_outlined,
                                    color: Colors.white,
                                    size: 20,
                                  ),
                                  AnimatedSwitcher(
                                    duration: Duration(milliseconds: 800),
                                    switchInCurve: Curves.easeOutCubic,
                                    switchOutCurve: Curves.easeInCubic,
                                    transitionBuilder:
                                        (child, animation) =>
                                            ScaleTransition(
                                              scale: animation,
                                              child: FadeTransition(
                                                opacity: animation,
                                                child: child,
                                              ),
                                            ),
                                    child:
                                        _showText
                                            ? Padding(
                                              padding: EdgeInsets.only(
                                                left: 8,
                                              ),
                                              key: ValueKey('text'),
                                              child: Text(
                                                'طلب التفعيل',
                                                style: TextStyle(
                                                  color: Colors.white,
                                                  fontFamily: 'Tajawal',
                                                  fontSize: 13,
                                                  fontWeight:
                                                      FontWeight.bold,
                                                  shadows: [
                                                    Shadow(
                                                      color: Colors.black
                                                          .withOpacity(0.2),
                                                      blurRadius: 2,
                                                      offset: Offset(1, 1),
                                                    ),
                                                  ],
                                                ),
                                              ),
                                            )
                                            : SizedBox.shrink(
                                              key: ValueKey('empty'),
                                            ),
                                  ),
                                ],
                              ),
                            ),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ],
            );
          }
        },
      ),
    ),
  ),
);
}

void showSerialNumberDialog(BuildContext context) {
final TextEditingController serial1Controller = TextEditingController();
final TextEditingController serial2Controller = TextEditingController();
final FocusNode serial1Focus = FocusNode();
final FocusNode serial2Focus = FocusNode();


showDialog(
  context: context,
  barrierDismissible: false,
  builder: (BuildContext context) {
    return Directionality(
      textDirection: TextDirection.rtl,
      child: Center(
        child: ConstrainedBox(
          constraints: BoxConstraints(maxWidth: 500, minWidth: 300),
          child: Dialog(
            backgroundColor: Colors.transparent,
            elevation: 0,
            child: SingleChildScrollView(
              child: Container(
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(20),
                  boxShadow: [
                    BoxShadow(
                      color: Colors.black.withOpacity(0.15),
                      blurRadius: 24,
                      spreadRadius: 2,
                    ),
                  ],
                ),
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    _buildModernHeader(context),
                    Padding(
                      padding: const EdgeInsets.symmetric(
                        horizontal: 24,
                        vertical: 16,
                      ),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.stretch,
                        children: [
                          const Text(
                            'إدخال أرقام السيريال',
                            style: TextStyle(
                              fontSize: 24,
                              fontWeight: FontWeight.bold,
                              color: Color(0xFF1A3052),
                            ),
                            textAlign: TextAlign.center,
                          ),
                          const SizedBox(height: 8),
                          Text(
                            'الرجاء إدخال رقمي السيريال الموجودين على المنتج',
                            textAlign: TextAlign.center,
                            style: TextStyle(
                              color: Colors.grey[600],
                              fontSize: 15,
                              height: 1.4,
                            ),
                          ),
                          const SizedBox(height: 24),
                          _buildSerialField(
                            controller: serial1Controller,
                            focusNode: serial1Focus,
                            nextFocusNode: serial2Focus,
                            label: 'السيريال الأول',
                            icon: Icons.confirmation_number_outlined,
                            isFirstField: true,
                          ),
                          const SizedBox(height: 16),
                          _buildSerialField(
                            controller: serial2Controller,
                            focusNode: serial2Focus,
                            label: 'السيريال الثاني',
                            icon: Icons.confirmation_number_outlined,
                            isLastField: true,
                          ),
                          const SizedBox(height: 16),
                          Row(
                            children: [
                              Text(
                                'للطلب عبر الوتساب :',
                                textAlign: TextAlign.center,
                                style: TextStyle(
                                  color: Colors.grey[600],
                                  fontSize: 14,
                                ),
                              ),
                              Text(
                                '${phone ?? '+(967)xxxxxx '}',
                                textDirection: TextDirection.ltr,
                                style: TextStyle(
                                  color: Colors.grey[600],
                                  fontSize: 14,
                                ),
                              ),
                            ],
                          ),
                          const SizedBox(height: 24),
                          _buildModernActionButtons(
                            context: context,
                            serial1Controller: serial1Controller,
                            serial2Controller: serial2Controller,
                          ),
                          const SizedBox(height: 8),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ),
        ),
      ),
    );
  },
);
}

Widget _buildModernHeader(BuildContext context) {
return Container(
height: 120,
decoration: BoxDecoration(
color: const Color(0xFF1A3052),
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
spreadRadius: 2,
),
],
),
child: Stack(
children: [
Positioned(
top: 0,
right: 0,
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: const Color(0xFF2A5591).withOpacity(0.2),
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(100),
),
),
),
),
Positioned(
bottom: 0,
left: 0,
child: Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: const Color(0xFF2A5591).withOpacity(0.15),
borderRadius: const BorderRadius.only(
topRight: Radius.circular(80),
),
),
),
),
Positioned(
top: 16,
left: 16,
child: IconButton(
icon: const Icon(Icons.close, color: Colors.white, size: 28),
onPressed: () => Navigator.pop(context),
),
),
const Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.verified_outlined, color: Colors.white, size: 32),
SizedBox(height: 8),
Text(
'التفعيل',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
);
}

Widget buildSerialField({
required TextEditingController controller,
required FocusNode focusNode,
FocusNode? nextFocusNode,
required String label,
required IconData icon,
bool isFirstField = false,
bool isLastField = false,
}) {
return TextFormField(
controller: controller,
focusNode: focusNode,
textInputAction:
isLastField ? TextInputAction.done : TextInputAction.next,
onFieldSubmitted: () {
if (!isLastField && nextFocusNode != null) {
FocusScope.of(focusNode.context!).requestFocus(nextFocusNode);
}
},
textAlign: TextAlign.right,
style: const TextStyle(color: Color(0xFF333333), fontSize: 16),
decoration: InputDecoration(
labelText: label,
labelStyle: const TextStyle(color: Color(0xFF1A3052), fontSize: 15),
prefixIcon: Container(
margin: const EdgeInsets.only(right: 12),
child: Icon(
icon,
color: const Color(0xFF1A3052).withOpacity(0.7),
size: 22,
),
),
filled: true,
fillColor: Colors.grey[50],
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.grey[300]!, width: 1),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Color(0xFF1A3052), width: 1.5),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
),
suffixIcon: IconButton(
icon: Icon(
Icons.paste,
color: const Color(0xFF1A3052).withOpacity(0.6),
size: 20,
),
onPressed: () => _pasteFromClipboard(controller),
),
),
);
}

Widget _buildModernActionButtons({
required BuildContext context,
required TextEditingController serial1Controller,
required TextEditingController serial2Controller,
}) {
return Row(
children: [
Expanded(
child: OutlinedButton(
style: OutlinedButton.styleFrom(
foregroundColor: const Color(0xFF1A3052),
padding: const EdgeInsets.symmetric(vertical: 16),
side: const BorderSide(color: Color(0xFF1A3052)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
backgroundColor: Colors.white,
),
onPressed: () => Navigator.pop(context),
child: const Text(
'إلغاء',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
),
const SizedBox(width: 16),
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF1A3052),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 2,
shadowColor: const Color(0xFF1A3052).withOpacity(0.3),
),
onPressed:
() => _validateAndSubmit(
context: context,
serial1Controller: serial1Controller,
serial2Controller: serial2Controller,
),
child: const Text(
'تأكيد',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
),
],
);
}

Future

Future


if (serial1.isEmpty || serial2.isEmpty) {
  _showValidationError(context, 'يجب إدخال كلا السيريالين');
  return;
}

try {
  showDialog(
    context: context,
    barrierDismissible: false,
    builder: (context) => const Center(child: CircularProgressIndicator()),
  );

  final response = await http.get(
    Uri.parse('$Urlapi/api/MicroSerial/by-parts/$serial1/$serial2'),
    headers: {'accept': '*/*'},
  );

  Navigator.pop(context);

  if (response.statusCode == 200) {
    final data = json.decode(response.body);
    if (data['serialPart1'] == serial1 && data['serialPart2'] == serial2) {
      // حفظ بيانات التفعيل بشكل آمن
      final storageDb = SecureStorageDB();
      await storageDb.write('serialPart1', serial1);
      await storageDb.write('serialPart2', serial2);
      Navigator.pop(context);
      _showSuccessDialog(context);
    } else {
      _showValidationError(
        context,
        'أرقام السيريال غير متطابقة مع السجلات',
      );
    }
  } else if (response.statusCode == 404) {
    _showValidationError(context, 'أرقام السيريال غير صحيحة');
  } else {
    _showValidationError(
      context,
      'حدث خطأ أثناء التحقق، يرجى المحاولة لاحقاً',
    );
  }
} catch (e) {
  Navigator.pop(context);
  _showValidationError(
    context,
    'تعذر الاتصال بالخادم، يرجى التأكد من الاتصال بالإنترنت',
  );
  print('$e');
}
}

void _showValidationError(BuildContext context, String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
const Icon(Icons.error_outline, color: Colors.white),
const SizedBox(width: 8),
Text(message),
],
),
backgroundColor: const Color(0xFFD32F2F),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
margin: const EdgeInsets.all(16),
duration: const Duration(seconds: 3),
),
);
}

void _showSuccessDialog(BuildContext context) {
showDialog(
context: context,
builder:
(context) => Directionality(
textDirection: TextDirection.rtl,
child: Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: const Color(0xFF4CAF50).withOpacity(0.2),
shape: BoxShape.circle,
),
child: const Icon(
Icons.check_circle,
color: Color(0xFF4CAF50),
size: 60,
),
),
const SizedBox(height: 24),
const Text(
'تم التفعيل بنجاح',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Color(0xFF1A3052),
),
),
const SizedBox(height: 16),
const Text(
'تم تفعيل المنتج بنجاح، يمكنك الآن استخدام جميع الميزات',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey, fontSize: 15),
),
const SizedBox(height: 24),
SizedBox(
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF1A3052),
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: () => Navigator.pop(context),
child: const Text(
'حسناً',
style: TextStyle(color: Colors.white),
),
),
),
],
),
),
),
),
);
}

Future

Widget _buildLoginForm(bool isMobile) {
return SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(
children: [
_buildAppHeader(isMobile),
const SizedBox(height: 20),
_buildUserTypeSelection(),
const SizedBox(height: 5),
_buildIpFields(),
const SizedBox(height: 12),
_buildUsernameField(),
const SizedBox(height: 12),
_buildPasswordField(),
const SizedBox(height: 12),
_buildPortField(),
const SizedBox(height: 25),
_buildLoginButton(),
const SizedBox(height: 15),
],
),
),
);
}

Widget _buildAppHeader(bool isMobile) => Column(
children: [
SizedBox(height: MediaQuery.of(context).size.width > 768 ? 60 : 50),
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Hero(
tag: 'app-icon',
child: Icon(
Icons.network_check,
size: isMobile ? 55 : 60,
color: Colors.white,
),
),


      const SizedBox(width: 10),
      Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text(
            'Micro Panel',
            style: TextStyle(
              fontFamily: 'Tajawal',
              fontSize: 25,
              fontWeight: FontWeight.bold,
              color: Colors.white,
            ),
          ),
          const Text(
            'نظام إدارة أجهزة الميكروتك',
            style: TextStyle(
              fontFamily: 'Tajawal',
              color: Colors.white70,
              fontSize: 11,
            ),
          ),
        ],
      ),
    ],
  ),
  SizedBox(height: MediaQuery.of(context).size.width > 768 ? 30 : 50),
],
);

Widget _buildIpFields() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
const Padding(
padding: EdgeInsets.only(bottom: 8.0, right: 8.0),
child: Text(
'عنوان IP',
style: TextStyle(fontFamily: 'Tajawal', color: Colors.white70),
),
),
TextFormField(
controller: _ipController,
validator: (value) => _validateIpAddress(value),
style: const TextStyle(fontFamily: 'Tajawal', color: Colors.white),
decoration: InputDecoration(
labelStyle: const TextStyle(
fontFamily: 'Tajawal',
color: Colors.white70,
),
filled: true,
fillColor: Colors.white.withOpacity(0.1),
contentPadding: const EdgeInsets.symmetric(
vertical: 14,
horizontal: 16,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.white.withOpacity(0.2)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Color(0xFF3B82F6)),
),
hintText: 'مثال: 192.168.1.1',
hintStyle: TextStyle(
fontFamily: 'Tajawal',
color: Colors.white.withOpacity(0.5),
),
),
keyboardType: const TextInputType.numberWithOptions(decimal: true),
textInputAction: TextInputAction.next,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[0-9.]')),
LengthLimitingTextInputFormatter(15),
],
focusNode: _ipFocusNode,
onTap: () {
// نقل المؤشر لنهاية النص
final text = _ipController.text;
_ipController.selection = TextSelection.fromPosition(
TextPosition(offset: text.length),
);
},
onChanged: (value) {
// إزالة النقطة الثانية إذا أدخل المستخدم ".."
if (value.endsWith('..')) {
_ipController.text = value.substring(0, value.length - 1);
_ipController.selection = TextSelection.fromPosition(
TextPosition(offset: _ipController.text.length),
);
}
},
),
],
);
}

// دالة التحقق من صحة عنوان IP كامل
String? _validateIpAddress(String? value) {
if (value == null || value.isEmpty) return 'يرجى إدخال عنوان IP';


final ipParts = value.split('.');
if (ipParts.length != 4) return 'يجب أن يتكون عنوان IP من 4 أجزاء';

for (int i = 0; i < ipParts.length; i++) {
  final part = ipParts[i];
  if (part.isEmpty) return 'جزء ${i + 1} من الـ IP فارغ';
  if (!RegExp(r'^\d+$').hasMatch(part))
    return 'يجب أن يحتوي جزء ${i + 1} على أرقام فقط';

  final num = int.tryParse(part) ?? 0;
  if (num < 0 || num > 255) return 'يجب أن يكون جزء ${i + 1} بين 0 و 255';
}

return null;
}

Widget _buildUsernameField() {
return TextFormField(
controller: _usernameController,
validator: (value) => _validateEnglishOnly(value, 'اسم المستخدم'),
style: const TextStyle(fontFamily: 'Tajawal', color: Colors.white),
decoration: InputDecoration(
labelText: 'اسم المستخدم',
labelStyle: const TextStyle(
fontFamily: 'Tajawal',
color: Colors.white70,
),
prefixIcon: const Icon(Icons.person_outline, color: Colors.white70),
filled: true,
fillColor: Colors.white.withOpacity(0.1),
contentPadding: const EdgeInsets.symmetric(
vertical: 14,
horizontal: 16,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.white.withOpacity(0.2)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Color(0xFF3B82F6)),
),
),
textInputAction: TextInputAction.next,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9]')),
],
onTap: () {
// تأكد أن المؤشر في نهاية النص
final text = _usernameController.text;
usernameController.selection = TextSelection.fromPosition(
TextPosition(offset: text.length),
);
},
onFieldSubmitted:
() => FocusScope.of(
context,
).requestFocus(_passwordController as FocusNode?),
);
}

Widget _buildPasswordField() {
return TextFormField(
controller: _passwordController,
obscureText: _obscurePassword,
validator: _validatePassword,
style: const TextStyle(fontFamily: 'Tajawal', color: Colors.white),
decoration: InputDecoration(
labelText: 'كلمة المرور',
labelStyle: const TextStyle(
fontFamily: 'Tajawal',
color: Colors.white70,
),
prefixIcon: const Icon(Icons.lock_outline, color: Colors.white70),
suffixIcon: IconButton(
icon: Icon(
_obscurePassword ? Icons.visibility_off : Icons.visibility,
color: Colors.white70,
),
onPressed: () => setState(() => _obscurePassword = !obscurePassword),
),
filled: true,
fillColor: Colors.white.withOpacity(0.1),
contentPadding: const EdgeInsets.symmetric(
vertical: 14,
horizontal: 16,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.white.withOpacity(0.2)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Color(0xFF3B82F6)),
),
),
textInputAction: TextInputAction.next,
inputFormatters: [
FilteringTextInputFormatter.allow(
RegExp(r'[a-zA-Z0-9!@#$%^&*()+-=[]{};:"\|,.<>/?]'),
),
],
onTap: () {
// نقل المؤشر لنهاية النص
final text = _passwordController.text;
passwordController.selection = TextSelection.fromPosition(
TextPosition(offset: text.length),
);
},
onFieldSubmitted: () => _connectToMikrotik(),
);
}

String? validatePassword(String? value) {
if (value == null || value.isEmpty) {
return 'يرجى إدخال كلمة المرور';
}
final passwordRegex = RegExp(
r'^[a-zA-Z0-9!@#$%^&*()+-=[]{};:"\|,.<>/?]+$',
);
if (!passwordRegex.hasMatch(value)) {
return 'يمكن أن تحتوي كلمة المرور على أحرف وأرقام ورموز خاصة';
}
return null;
}

// أضف هذه الدالة في كلاس _LoginState
String? _validateEnglishOnly(String? value, String fieldName) {
if (value == null || value.isEmpty) {
return 'يرجى إدخال $fieldName';
}
final englishRegex = RegExp(r'^[a-zA-Z0-9]+$');
if (!englishRegex.hasMatch(value)) {
return 'يجب أن يحتوي $fieldName على أحرف إنجليزية وأرقام فقط';
}
return null;
}

Widget _buildPortField() {
return TextFormField(
controller: _portController,
validator: _validatePort,
style: const TextStyle(fontFamily: 'Tajawal', color: Colors.white),
decoration: InputDecoration(
labelText: 'المنفذ',
labelStyle: const TextStyle(
fontFamily: 'Tajawal',
color: Colors.white70,
),
prefixIcon: const Icon(Icons.settings_ethernet, color: Colors.white70),
filled: true,
fillColor: Colors.white.withOpacity(0.1),
contentPadding: const EdgeInsets.symmetric(
vertical: 14,
horizontal: 16,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Colors.white.withOpacity(0.2)),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Color(0xFF3B82F6)),
),
),
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(5),
],
onTap: () {
// نقل المؤشر لنهاية النص
final text = _portController.text;
portController.selection = TextSelection.fromPosition(
TextPosition(offset: text.length),
);
},
onFieldSubmitted: () => _connectToMikrotik(),
);
}

// يمكنك تحديث دالة التحقق إذا كنت تريد ضوابط إضافية
String? _validatePort(String? value) {
if (value == null || value.isEmpty) {
return 'يرجى إدخال رقم المنفذ';
}


final port = int.tryParse(value);
if (port == null) {
  return 'يجب أن يتكون المنفذ من أرقام فقط';
}

if (port <= 0 || port > 65535) {
  return 'يجب أن يكون المنفذ بين 1 و 65535';
}

return null;
}

Widget _buildUserTypeSelection() {
return Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: DefaultTabController(
length: 2,
initialIndex: _selectedUserType == UserType.hotspot ? 0 : 1,
child: Builder(
// لإمكانية الوصول للسياق الصحيح للتاب
builder: (context) {
final tabController = DefaultTabController.of(context);
if (tabController != null) {
// مزامنة تغيير المتغير مع controller عند تغير القيمة
tabController.addListener(() {
if (!tabController.indexIsChanging) {
final idx = tabController.index;
final selectedType =
idx == 0 ? UserType.hotspot : UserType.userManager;
if (selectedType != _selectedUserType) {
setState(() {
_selectedUserType = selectedType;
});
_saveCredentials();
}
}
});
}


        return Container(
          decoration: BoxDecoration(
            color: Colors.white.withOpacity(0.05),
            borderRadius: BorderRadius.circular(12),
            boxShadow: [
              BoxShadow(
                color: Colors.black.withOpacity(0.1),
                blurRadius: 6,
                offset: Offset(0, 2),
              ),
            ],
          ),
          child: Padding(
            padding: const EdgeInsets.all(4.0),
            child: TabBar(
              tabs: const [
                Tab(
                  text: 'مستخدمي الهوتسبوت',
                  icon: Icon(Icons.wifi, size: 18),
                ),
                Tab(
                  text: 'مستخدمي اليوزر مانجر',
                  icon: Icon(Icons.people_alt, size: 18),
                ),
              ],
              indicator: BoxDecoration(
                borderRadius: BorderRadius.circular(8),
                gradient: LinearGradient(
                  colors: [Color(0xFF3B82F6), Color(0xFF1D4ED8)],
                ),
              ),
              labelColor: Colors.white,
              unselectedLabelColor: Colors.white.withOpacity(0.7),
              labelStyle: TextStyle(
                fontSize: 14,
                fontWeight: FontWeight.w500,
                fontFamily: 'Tajawal',
              ),
              unselectedLabelStyle: TextStyle(
                fontFamily: 'Tajawal',
                fontSize: 13,
                fontWeight: FontWeight.normal,
              ),
              indicatorSize: TabBarIndicatorSize.tab,
              indicatorPadding: EdgeInsets.zero,
            ),
          ),
        );
      },
    ),
  ),
);
}

Widget _buildLoginButton() {
return SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
onPressed:
_isLoading
? null
: () async {
await SecureStorageDB().closeDB();
await _connectToMikrotik();
},
style: ElevatedButton.styleFrom(
backgroundColor: _isLoading ? Colors.grey : const Color(0xFF3B82F6),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
child:
_isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 3,
),
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(Icons.login, color: Colors.white, size: 20),
SizedBox(width: 8),
Text(
'تسجيل الدخول',
style: TextStyle(
fontFamily: 'Tajawal',
fontSize: 15,
color: Colors.white,
),
),
],
),
),
);
}

Future


setState(() => _isLoading = true);

final ip = _ipController.text;
final port = int.tryParse(_portController.text) ?? 8728;

// إعداد timeout لمحاولة الاتصال
final timeoutDuration = Duration(seconds: 3);

try {
  // استخدام Future.any للجمع بين عملية الاتصال و timeout
  final client = await Future.any([
    _attemptLogin(ip, port),
    Future.delayed(
      timeoutDuration,
      () => throw TimeoutException('تجاوز الوقت المحدد للاتصال'),
    ),
  ]);

  await _saveCredentials();
  if (mounted) {
    Navigator.pushReplacement(
      context,
      MaterialPageRoute(
        builder:
            (context) => DataLoadingScreen(
              client: client,
              host: ip,
              port: port,
              username: _usernameController.text,
              password: _passwordController.text,
              userType: _selectedUserType == UserType.hotspot ? 0 : 1,
            ),
      ),
    );
  }
} on TimeoutException {
  _showErrorDialog(
    'تجاوز الوقت المحدد',
    'لم يتم الاتصال بالخادم خلال الوقت المحدد. يرجى التحقق من اتصال الشبكة وإعادة المحاولة.',
  );
} on SocketException {
  _showErrorDialog(
    'خطأ في الاتصال',
    'تعذر الاتصال بالخادم. يرجى التحقق من عنوان IP والمنفذ وإعادة المحاولة.',
  );
} on Exception catch (e) {
  _showErrorDialog('خطأ في تسجيل الدخول', e.toString());
} finally {
  if (mounted) {
    setState(() => _isLoading = false);
  }
}
}

Future


final isLoggedIn = await client.login();
if (!isLoggedIn) {
  throw Exception(
    "فشل تسجيل الدخول: اسم المستخدم أو كلمة المرور غير صحيحة",
  );
}

return client;
}

void _showErrorDialog(String title, String message) {
if (!mounted) return;


// تنظيف الرسالة من كلمة Exception إن وجدت
String cleanedMessage = message.replaceAll('Exception:', '').trim();

showDialog(
  context: context,
  builder:
      (context) => Directionality(
        textDirection: TextDirection.rtl,
        child: Dialog(
          backgroundColor: const Color(0xFF0A1A3A),
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(16.0),
          ),
          elevation: 10,
          child: Padding(
            padding: const EdgeInsets.symmetric(
              horizontal: 24.0,
              vertical: 20.0,
            ),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                Align(
                  alignment: Alignment.centerRight,
                  child: Text(
                    title
                        .replaceAll('Exception:', '')
                        .trim(), // تنظيف العنوان أيضاً
                    textAlign: TextAlign.right,
                    style: const TextStyle(
                      fontFamily: 'Tajawal',
                      fontSize: 22,
                      fontWeight: FontWeight.bold,
                      color: Colors.white,
                      height: 1.5,
                    ),
                  ),
                ),

                const SizedBox(height: 12),

                Text(
                  cleanedMessage, // استخدام الرسالة المنظفة
                  textAlign: TextAlign.right,
                  style: const TextStyle(
                    fontFamily: 'Tajawal',
                    fontSize: 16,
                    color: Colors.white70,
                    height: 1.6,
                  ),
                ),

                const SizedBox(height: 24),

                Align(
                  alignment: Alignment.centerLeft,
                  child: Directionality(
                    textDirection: TextDirection.rtl,
                    child: TextButton(
                      onPressed: () => Navigator.pop(context),
                      style: TextButton.styleFrom(
                        backgroundColor: const Color(0xFF1A3A6E),
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(8.0),
                        ),
                        padding: const EdgeInsets.symmetric(
                          horizontal: 32,
                          vertical: 12,
                        ),
                      ),
                      child: Text(
                        'حسناً',
                        style: const TextStyle(
                          fontFamily: 'Tajawal',
                          fontSize: 16,
                          fontWeight: FontWeight.bold,
                          color: Colors.white,
                        ),
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
);
}

String? _validateRequired(String? value, String fieldName) {
if (value == null || value.isEmpty) return 'يرجى إدخال $fieldName';
final regex = RegExp(r'^[a-zA-Z0-9]+$');
if (!regex.hasMatch(value))
return 'يجب أن يحتوي $fieldName على أحرف إنجليزية وأرقام فقط';
return null;
}

String? _validateIpPart(String? value, int part) {
if (value == null || value.isEmpty) return 'يرجى إدخال جزء $part من الـ IP';
if (!RegExp(r'^\d+$').hasMatch(value))
return 'يجب أن يحتوي جزء $part على أرقام فقط';
final num = int.tryParse(value) ?? 0;
if (num < 0 || num > 255) return 'يجب أن يكون جزء $part بين 0 و 255';
return null;
}
}

class DataLoadingScreen extends StatefulWidget {
final MikroTikApi? api; // يمكن إزالته لاحقًا إذا لم تعد تحتاج إليه
final String host;
final int port;
final String username;
final String password;
final RouterOSClient client;
final int userType;

const DataLoadingScreen({
super.key,
required this.client,
required this.host,
required this.port,
required this.username,
required this.password,
required this.userType,
this.api,
});

@override
State

class _DataLoadingScreenState extends State

@override
void initState() {
super.initState();
_progressController = StreamController

Future

Future

void _handleIsolateMessage(dynamic message) {
try {
if (message is _ProgressUpdate) {
if (!_realDataStarted) {
_startSimulation(message.total);
if (!mounted) return;
setState(() => _realDataStarted = true);
}
_updateProgress(message.current, message.total);
} else if (message is Map) {
if (message.containsKey('type') && message.containsKey('users')) {
final users = List<Map<String, dynamic>>.from(message['users']);
// تمرير كافة البيانات المستقبلة إلى دالة _handleSuccess
_handleSuccess(
users,
additionalData: {
'type': message['type'],
if (message.containsKey('version')) 'version': message['version'],
if (message.containsKey('timestamp'))
'timestamp': message['timestamp'],
},
);
} else {
_handleError('صيغة البيانات غير صحيحة');
}
} else if (message is String) {
_handleError(message);
} else if (message is List) {
if (message.isNotEmpty && message.first is Map) {
_handleSuccess(message.cast<Map<String, dynamic>>());
}
} else {
_handleError('استلم رسالة غير معروفة من النوع: ${message.runtimeType}');
}
} catch (e, stack) {
_handleError('حدث خطأ أثناء معالجة الرسالة: \n$stack');
}
}

void _startSimulation(int totalUsers) {
_simulationTimer?.cancel();
_totalUsers = totalUsers;
_currentProgress = 0;
_simulationTimer = Timer.periodic(Duration(milliseconds: 300), (timer) {
if (_currentProgress < _totalUsers) {
_currentProgress += 1 + _random.nextInt(40);
if (_currentProgress > _totalUsers) {
_currentProgress = _totalUsers;
}
_progressController.add(_currentProgress);
} else {
timer.cancel();
}
});
}

void _updateProgress(int current, int total) {
if (!mounted) return;
setState(() {
_currentProgress = current;
_totalUsers = total;
});
_progressController.add(current);
}

void _handleError(String message) {
if (!mounted) return;
_simulationTimer?.cancel();
setState(() {
_hasError = true;
_errorMessage = message;
_loading = false;
});
_cleanUpResources();
}

void _cleanUpResources() {
_simulationTimer?.cancel();
_progressController.close();
_isolate?.kill(priority: Isolate.immediate);
_receivePort?.close();
}

static Future


  await api.connect();
  bool loggedIn = await api.login();

  if (!loggedIn) {
    sendPort.send("فشل تسجيل الدخول إلى جهاز الميكروتك");
    return;
  }

  // --- البروتوكول الجديد: جلب الترخيص وتخزينه مشفر (مرة واحدة فقط) ---
  final storageDb = SecureStorageDB();
  String? existingSoftwareId = await storageDb.read('license_software_id');
  if (existingSoftwareId == null) {
    // لم يتم التخزين مسبقًا
    final licenseData = await api.fetchLicenseData();

    if (licenseData != null && licenseData.isNotEmpty) {
      final aesKey = 'my32lengthsupersecretnooneknows1';
      final aesIV = '8bytesiv12345678';

      String encryptAES(String plainText) {
        final key = encrypt.Key.fromUtf8(aesKey);
        final iv = encrypt.IV.fromUtf8(aesIV);
        final encrypter = encrypt.Encrypter(
          encrypt.AES(key, mode: encrypt.AESMode.cbc),
        );
        return encrypter.encrypt(plainText, iv: iv).base64;
      }

      await storageDb.write(
        'license_software_id',
        encryptAES(licenseData['software-id'] ?? ''),
      );
      await storageDb.write(
        'license_nlevel',
        encryptAES(licenseData['nlevel'] ?? ''),
      );

      print("تم تخزين بيانات الترخيص المشفرة في قاعدة البيانات");
    }
  }

  final versionString = await api.fetchRouterVersion(
    isolateData.appDocumentsPath,
  );

  final version = versionString.startsWith('7') ? 7 : 6;

  if (isolateData.userType == UserType.hotspot.index) {
    final hotspotUsers = await api.fetchHotspotUsers();
    sendPort.send(
      _ProgressUpdate(hotspotUsers.length, hotspotUsers.length),
    );
    sendPort.send({
      'type': 'hotspot',
      'users': hotspotUsers,
      'timestamp': DateTime.now().toIso8601String(),
    });
  } else {
    final userManagerUsers = await api.fetchUserManagerUsers(
      isolateData.appDocumentsPath,
    );
    sendPort.send(
      _ProgressUpdate(userManagerUsers.length, userManagerUsers.length),
    );
    sendPort.send({
      'type': 'user_manager',
      'users': userManagerUsers,
      'version': version,
      'timestamp': DateTime.now().toIso8601String(),
    });
  }
} catch (e, stackTrace) {
  sendPort.send('حدث خطأ أثناء جلب البيانات:\n \n$stackTrace');
  print('$e');
}
}

Future


  // تحضير البيانات الأساسية للحفظ
  final encoder = JsonEncoder.withIndent('  ');
  final formattedJson = encoder.convert(users);
  await file.writeAsString(formattedJson);

  if (mounted) {
    setState(() {
      _userCount = users.length;
      _loading = false;
    });
    Navigator.pushReplacement(
      context,
      MaterialPageRoute(
        builder: (context) => RouterDetailsPage(client: widget.client),
      ),
    );
  }
} catch (e) {
  _handleError('فشل في حفظ البيانات');
}
}

@override
void dispose() {
_cleanUpResources();
_receivePort?.close();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF0A192F),
body: Center(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (_hasError)
_buildErrorWidget()
else if (_loading)
_buildLoadingWidget()
else
_buildSuccessWidget(),
],
),
),
),
);
}

Widget _buildErrorWidget() {
return Column(
children: [
const Icon(Icons.error_outline, color: Colors.red, size: 40),
const SizedBox(height: 16),
Text(
_errorMessage,
style: const TextStyle(
fontFamily: 'Tajawal',
color: Colors.white,
fontSize: 15,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () => Navigator.pop(context),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF3B82F6),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 10),
),
child: const Text(
'العودة',
style: TextStyle(fontFamily: 'Tajawal', fontSize: 15),
),
),
],
);
}

Widget _buildLoadingWidget() {
int displayedProgress = 0;
int displayedTotal = _totalUsers;
double displayedPercentage = 0.0;


void updateDisplayedValues() {
  final progressDiff = _currentProgress - displayedProgress;
  final totalDiff = _totalUsers - displayedTotal;
  if (progressDiff > 0) {
    displayedProgress += (progressDiff / 5).ceil();
    if (displayedProgress > _currentProgress) {
      displayedProgress = _currentProgress;
    }
  }
  if (totalDiff > 0) {
    displayedTotal += (totalDiff / 3).ceil();
    if (displayedTotal > _totalUsers) {
      displayedTotal = _totalUsers;
    }
  }
  if (displayedTotal > 0) {
    displayedPercentage = (displayedProgress / displayedTotal * 100);
  }
}

return Column(
  children: [
    const SizedBox(
      width: 30,
      height: 30,
      child: CircularProgressIndicator(color: Colors.white, strokeWidth: 3),
    ),
    const SizedBox(height: 16),
    Text(
      _realDataStarted
          ? 'جاري جلب بيانات المستخدمين...'
          : 'جاري تهيئة عملية الجلب...',
      style: const TextStyle(
        fontFamily: 'Tajawal',
        color: Colors.white,
        fontSize: 16,
      ),
    ),
    const SizedBox(height: 8),
    StreamBuilder<int>(
      stream: _progressStream,
      builder: (context, snapshot) {
        updateDisplayedValues();
        return Column(
          children: [
            Text(
              '$displayedProgress من $displayedTotal (${displayedPercentage.toStringAsFixed(1)}%)',
              style: const TextStyle(
                fontFamily: 'Tajawal',
                color: Colors.white70,
                fontSize: 13,
              ),
            ),
            const SizedBox(height: 16),
            ClipRRect(
              borderRadius: BorderRadius.circular(12),
              child: LinearProgressIndicator(
                value:
                    displayedTotal > 0
                        ? displayedProgress / displayedTotal
                        : 0,
                backgroundColor: Colors.white.withOpacity(0.1),
                valueColor: const AlwaysStoppedAnimation<Color>(
                  Colors.blueAccent,
                ),
                minHeight: 10,
              ),
            ),
          ],
        );
      },
    ),
  ],
);
}

Widget _buildSuccessWidget() {
return Column(
children: [
const Icon(Icons.check_circle_outline, color: Colors.green, size: 40),
const SizedBox(height: 16),
Text(
'تم جلب $_userCount مستخدم بنجاح',
style: const TextStyle(
fontFamily: 'Tajawal',
color: Colors.white,
fontSize: 16,
),
),
const SizedBox(height: 8),
const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
color: Colors.white70,
strokeWidth: 2,
),
),
const SizedBox(height: 8),
const Text(
'جاري التوجيه إلى لوحة التحكم...',
style: TextStyle(
fontFamily: 'Tajawal',
color: Colors.white70,
fontSize: 13,
),
),
],
);
}
}

class _IsolateData {
final String host;
final int port;
final String username;
final String password;
final SendPort sendPort;
final int userType;
final String appDocumentsPath;

_IsolateData({
required this.host,
required this.port,
required this.username,
required this.password,
required this.sendPort,
required this.userType,
required this.appDocumentsPath,
});
}

class _ProgressUpdate {
final int current;
final int total;

_ProgressUpdate(this.current, this.total);
}

// Custom clipper for the curved header
class _DialogHeaderClipper extends CustomClipper

@override
bool shouldReclip(CustomClipper

اما بالنسبة لكود قاعدة البيانات
import 'dart:convert';
import 'package:encrypt/encrypt.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';

// إدارة DB مع تشفير AES-256 للبيانات

class SecureStorageDB {
static final SecureStorageDB _instance = SecureStorageDB._internal();
factory SecureStorageDB() => _instance;
SecureStorageDB._internal();

static Database? _db;

// مفتاح و IV للتشفير يجب أن يكونوا ثابتين أو يتم توليدهم وتأمينهم بطريقة آمنة
final _key = Key.fromUtf8('my32lengthsupersecretnooneknows1'); // 32 BYTE
final _iv = IV.fromUtf8('8bytesiv12345678'); // 16 BYTE

Future

Future


return await openDatabase(path, version: 1, onCreate: _onCreate);
}

Future

// تشفير النص
String _encrypt(String plainText) {
final encrypter = Encrypter(AES(_key, mode: AESMode.cbc));
final encrypted = encrypter.encrypt(plainText, iv: _iv);
return encrypted.base64;
}

// فك التشفير
String _decrypt(String encryptedText) {
final encrypter = Encrypter(AES(_key, mode: AESMode.cbc));
final decrypted = encrypter.decrypt64(encryptedText, iv: _iv);
return decrypted;
}

// تخزين قيمة جديدة أو تحديثها
Future

Future<String?> read(String key) async {
try {
if (key.isEmpty) {
print('🔴 المفتاح فارغ، لن يتم تنفيذ القراءة.');
return null;
}


  print('🔍 محاولة قراءة المفتاح: $key');

  final database = await db;

  final result = await database.query(
    'secure_storage',
    columns: ['value'],
    where: 'key = ?',
    whereArgs: [key],
    limit: 1,
  );

  if (result.isEmpty) {
    print('ℹ️ لا توجد قيمة مخزنة لهذا المفتاح: $key');
    return null;
  }

  final encryptedValue = result.first['value'] as String;
  return _decrypt(encryptedValue);
} catch (e) {
  print('❌ خطأ أثناء قراءة القيم من القاعدة: $e');
  return null;
}
}

Future

// حذف قيمة
Future

ملاحظة اعطيني الجزء السابق والجزء المراد استبدالة بة