File size: 98,939 Bytes
0bd2c13
da901ba
 
 
 
 
 
 
 
51e08a5
da901ba
bb3d23b
b935d3d
 
 
bb3d23b
 
 
 
da901ba
51e08a5
 
 
da901ba
d16eedb
b935d3d
 
 
bb3d23b
e00d6b1
 
b935d3d
 
bb3d23b
51e08a5
e00d6b1
da901ba
 
 
b935d3d
 
 
 
51e08a5
e14e73d
 
51e08a5
e00d6b1
51e08a5
1f8544d
 
 
51e08a5
9b79926
781de6c
 
 
 
 
d16eedb
51e08a5
da901ba
68579b2
51e08a5
e5167ed
 
51e08a5
e00d6b1
 
 
 
 
 
 
 
 
 
 
51e08a5
e00d6b1
 
 
 
 
 
 
 
51e08a5
e00d6b1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51e08a5
e00d6b1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75280ce
e00d6b1
 
 
51e08a5
81f46a3
e14e73d
 
1f8544d
 
 
51e08a5
1f8544d
b935d3d
 
 
 
 
1f8544d
 
 
b935d3d
51e08a5
1f8544d
 
b935d3d
1f8544d
 
 
b935d3d
51e08a5
1f8544d
51e08a5
1f8544d
b935d3d
 
 
 
 
 
 
 
 
 
 
 
 
1f8544d
b935d3d
1f8544d
e14e73d
e00d6b1
e14e73d
51e08a5
b935d3d
 
 
 
 
 
 
 
e00d6b1
51e08a5
b935d3d
 
 
 
 
 
 
 
 
 
 
 
 
e14e73d
781de6c
b935d3d
 
e00d6b1
 
 
b935d3d
 
 
 
e00d6b1
 
 
 
 
b935d3d
e00d6b1
 
 
 
 
 
 
 
 
b935d3d
 
e00d6b1
 
 
 
b935d3d
 
 
 
e00d6b1
b935d3d
e00d6b1
 
b935d3d
 
e00d6b1
 
 
 
b935d3d
 
 
 
e00d6b1
b935d3d
e00d6b1
 
 
b935d3d
e00d6b1
 
 
 
b935d3d
 
 
 
e00d6b1
b935d3d
e00d6b1
 
 
b935d3d
e00d6b1
 
 
 
 
 
b935d3d
 
e00d6b1
 
 
 
 
 
 
 
 
b935d3d
e00d6b1
 
 
 
 
781de6c
b935d3d
781de6c
 
 
 
 
 
 
e00d6b1
b935d3d
 
e14e73d
68579b2
 
 
 
b935d3d
 
 
 
68579b2
 
b935d3d
 
75280ce
68579b2
 
b935d3d
e00d6b1
68579b2
b935d3d
 
75280ce
b935d3d
 
 
 
42aed02
 
68579b2
51e08a5
b935d3d
51e08a5
b935d3d
b8e4974
b935d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
0a3a574
 
 
b935d3d
0a3a574
 
 
 
51e08a5
1f8544d
b935d3d
 
1f8544d
b935d3d
 
 
1f8544d
b935d3d
 
1f8544d
 
 
b935d3d
1f8544d
b935d3d
 
 
 
 
 
 
 
51e08a5
b935d3d
 
 
 
 
0a3a574
b935d3d
81f46a3
68579b2
 
3b15070
68579b2
81f46a3
575196e
51e08a5
3b15070
b935d3d
51e08a5
3b15070
575196e
3b15070
51e08a5
b935d3d
 
 
 
 
 
 
 
 
 
 
1f8544d
b935d3d
 
 
51e08a5
b935d3d
 
e67f748
51e08a5
b935d3d
 
51e08a5
b935d3d
 
 
51e08a5
68579b2
b935d3d
68579b2
51e08a5
e00d6b1
51e08a5
68579b2
575196e
68579b2
51e08a5
b935d3d
51e08a5
b935d3d
51e08a5
68579b2
51e08a5
81f46a3
575196e
b935d3d
68579b2
81f46a3
3b15070
 
 
b935d3d
 
 
3b15070
 
e67f748
 
575196e
51e08a5
0a3a574
e67f748
 
575196e
e67f748
51e08a5
e67f748
575196e
e67f748
51e08a5
e67f748
 
e00d6b1
e67f748
e00d6b1
e67f748
51e08a5
b935d3d
e67f748
81f46a3
0a3a574
51e08a5
e00d6b1
 
51e08a5
 
 
 
b935d3d
51e08a5
 
 
 
 
 
 
 
b935d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51e08a5
 
b935d3d
 
 
 
 
 
 
 
 
51e08a5
e00d6b1
51e08a5
68579b2
e00d6b1
51e08a5
 
e67f748
 
51e08a5
e67f748
51e08a5
b935d3d
 
e67f748
b935d3d
51e08a5
e67f748
b935d3d
 
 
 
e67f748
b935d3d
51e08a5
e67f748
 
575196e
51e08a5
 
781de6c
 
 
 
42aed02
 
 
 
 
 
 
 
 
 
781de6c
 
 
42aed02
 
 
 
 
 
 
 
 
 
 
 
 
 
781de6c
 
42aed02
 
 
 
 
 
 
 
 
51e08a5
42aed02
 
b935d3d
 
51e08a5
42aed02
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b935d3d
 
 
51e08a5
 
b935d3d
 
 
 
 
 
 
e67f748
42aed02
 
75280ce
51e08a5
 
b935d3d
 
 
 
 
 
51e08a5
e67f748
51e08a5
e67f748
51e08a5
 
e67f748
51e08a5
e67f748
 
b935d3d
0a3a574
575196e
0a3a574
b935d3d
 
 
 
 
 
 
51e08a5
b935d3d
 
0a3a574
b935d3d
 
0a3a574
b935d3d
0a3a574
51e08a5
781de6c
 
 
b935d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
781de6c
 
42aed02
 
781de6c
42aed02
 
b935d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0a3a574
42aed02
 
 
b935d3d
 
0a3a574
51e08a5
b935d3d
 
 
 
 
 
 
0a3a574
51e08a5
0a3a574
51e08a5
b935d3d
0a3a574
 
b935d3d
781de6c
b935d3d
 
 
 
 
781de6c
 
 
 
b935d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0a3a574
 
 
b935d3d
0a3a574
b935d3d
0a3a574
781de6c
b935d3d
 
51e08a5
b935d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42aed02
b935d3d
0a3a574
b935d3d
 
0a3a574
 
b935d3d
0a3a574
 
 
51e08a5
0a3a574
51e08a5
0a3a574
51e08a5
 
 
b935d3d
 
 
 
 
 
 
 
51e08a5
b935d3d
51e08a5
 
b935d3d
 
 
 
 
 
42aed02
51e08a5
0a3a574
51e08a5
0a3a574
 
 
 
 
51e08a5
 
0a3a574
b935d3d
 
51e08a5
 
 
 
b935d3d
51e08a5
 
 
 
 
 
 
 
 
 
b935d3d
 
51e08a5
b935d3d
 
51e08a5
b935d3d
 
 
 
51e08a5
b935d3d
 
 
 
 
51e08a5
b935d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42aed02
b935d3d
42aed02
b935d3d
 
 
 
 
 
 
 
42aed02
 
b935d3d
51e08a5
 
b935d3d
 
 
 
51e08a5
 
 
 
0a3a574
b935d3d
 
51e08a5
0a3a574
 
b935d3d
0a3a574
b935d3d
e00d6b1
68579b2
51e08a5
68579b2
e00d6b1
 
b935d3d
 
e00d6b1
51e08a5
 
 
 
e00d6b1
42aed02
b935d3d
68579b2
42aed02
 
 
 
68579b2
51e08a5
781de6c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68579b2
781de6c
 
 
 
68579b2
781de6c
 
 
51e08a5
68579b2
781de6c
 
 
575196e
68579b2
81f46a3
e00d6b1
b935d3d
68579b2
51e08a5
b935d3d
 
51e08a5
68579b2
e14e73d
b935d3d
 
51e08a5
 
b935d3d
 
51e08a5
 
b935d3d
68579b2
51e08a5
 
 
 
 
 
b935d3d
51e08a5
68579b2
b935d3d
51e08a5
 
 
 
 
b935d3d
 
68579b2
e14e73d
b935d3d
e5167ed
51e08a5
575196e
51e08a5
68579b2
b935d3d
e00d6b1
68579b2
b935d3d
51e08a5
e00d6b1
b935d3d
 
 
 
 
 
51e08a5
68579b2
51e08a5
 
b935d3d
51e08a5
 
b935d3d
 
51e08a5
 
 
b935d3d
51e08a5
b935d3d
51e08a5
b935d3d
51e08a5
b935d3d
 
 
51e08a5
 
 
 
 
 
b935d3d
 
51e08a5
 
 
b935d3d
 
 
51e08a5
 
 
 
 
b935d3d
51e08a5
b935d3d
51e08a5
 
 
 
b935d3d
 
 
 
 
 
51e08a5
 
 
b935d3d
51e08a5
 
 
a5530c4
 
51e08a5
 
a5530c4
 
b935d3d
51e08a5
575196e
51e08a5
 
b935d3d
 
 
51e08a5
e00d6b1
b935d3d
 
68579b2
51e08a5
b935d3d
 
68579b2
b935d3d
68579b2
b935d3d
 
e00d6b1
68579b2
 
b935d3d
 
 
 
 
68579b2
 
b935d3d
68579b2
 
42aed02
b935d3d
 
e5167ed
b935d3d
 
51e08a5
 
e5167ed
b935d3d
 
51e08a5
e5167ed
 
 
51e08a5
b935d3d
 
 
e5167ed
b935d3d
51e08a5
 
e5167ed
b935d3d
e5167ed
51e08a5
 
b935d3d
 
51e08a5
b935d3d
51e08a5
b935d3d
e5167ed
 
51e08a5
e5167ed
 
 
 
 
b935d3d
 
 
e5167ed
781de6c
 
 
 
b935d3d
 
 
 
 
 
 
 
 
 
 
 
51e08a5
42aed02
 
75280ce
b935d3d
 
51e08a5
 
 
42aed02
b935d3d
 
 
51e08a5
 
575196e
51e08a5
e5167ed
51e08a5
575196e
e5167ed
51e08a5
e5167ed
750f622
e5167ed
 
51e08a5
b935d3d
 
51e08a5
b935d3d
 
 
 
51e08a5
b935d3d
 
 
 
51e08a5
575196e
51e08a5
750f622
51e08a5
750f622
51e08a5
b935d3d
51e08a5
750f622
 
 
 
e5167ed
51e08a5
 
 
 
 
b935d3d
51e08a5
781de6c
e5167ed
51e08a5
750f622
51e08a5
 
42aed02
b935d3d
51e08a5
 
e5167ed
b935d3d
51e08a5
 
 
 
781de6c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51e08a5
781de6c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51e08a5
 
b935d3d
51e08a5
e5167ed
51e08a5
750f622
e5167ed
b935d3d
42aed02
b935d3d
 
781de6c
 
 
b935d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
51e08a5
e5167ed
075d7cd
b935d3d
e5167ed
b935d3d
 
 
 
75280ce
b935d3d
 
075d7cd
b935d3d
51e08a5
 
 
 
 
 
 
 
 
 
 
 
 
 
b935d3d
51e08a5
 
 
 
 
b935d3d
51e08a5
e5167ed
51e08a5
750f622
075d7cd
51e08a5
 
 
 
b935d3d
750f622
51e08a5
 
e00d6b1
b935d3d
51e08a5
75280ce
 
 
51e08a5
b935d3d
 
51e08a5
 
 
 
 
42aed02
51e08a5
b935d3d
 
 
42aed02
51e08a5
 
b935d3d
 
42aed02
51e08a5
 
b935d3d
51e08a5
42aed02
51e08a5
 
 
 
68579b2
51e08a5
b935d3d
51e08a5
b935d3d
42aed02
b935d3d
 
42aed02
b935d3d
51e08a5
b935d3d
51e08a5
 
 
b935d3d
68579b2
575196e
51e08a5
 
 
 
 
68579b2
 
 
 
 
575196e
 
68579b2
 
b935d3d
e00d6b1
575196e
68579b2
 
da901ba
51e08a5
0a3a574
b935d3d
 
0a3a574
51e08a5
575196e
51e08a5
1f8544d
 
42aed02
1f8544d
51e08a5
b935d3d
 
 
 
 
 
51e08a5
575196e
e67f748
51e08a5
e67f748
 
51e08a5
42aed02
51e08a5
 
0a3a574
42aed02
51e08a5
1f8544d
51e08a5
575196e
0a3a574
 
51e08a5
0a3a574
b935d3d
0a3a574
 
b935d3d
51e08a5
 
 
b935d3d
781de6c
 
 
 
51e08a5
781de6c
 
 
 
 
 
 
 
 
 
 
 
42aed02
781de6c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b935d3d
51e08a5
 
 
0a3a574
51e08a5
 
750f622
 
51e08a5
e00d6b1
b935d3d
e00d6b1
b935d3d
51e08a5
b935d3d
e00d6b1
 
b935d3d
e00d6b1
b935d3d
 
 
 
0a3a574
b935d3d
0a3a574
51e08a5
 
 
0a3a574
 
 
b935d3d
0a3a574
51e08a5
 
 
0a3a574
 
 
b935d3d
51e08a5
 
 
 
0a3a574
 
51e08a5
0a3a574
b935d3d
0a3a574
51e08a5
 
 
0a3a574
 
 
b935d3d
51e08a5
0a3a574
 
51e08a5
 
 
 
b935d3d
51e08a5
 
 
 
 
 
 
0a3a574
 
 
51e08a5
0a3a574
575196e
3b15070
51e08a5
3b15070
b935d3d
3b15070
 
 
51e08a5
3b15070
51e08a5
3b15070
51e08a5
3b15070
781de6c
b935d3d
3b15070
 
575196e
3b15070
 
 
575196e
42aed02
3b15070
51e08a5
3b15070
575196e
3b15070
51e08a5
575196e
51e08a5
3b15070
 
51e08a5
0a3a574
b935d3d
 
 
 
 
 
 
 
b8e4974
781de6c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
# whale_news_data.py
import os
import asyncio
import httpx
import json
import traceback
import time
from datetime import datetime, timedelta
from collections import defaultdict, deque
import ccxt.pro as ccxt # ✅ ملاحظة: هذا هو ccxt.pro (غير متزامن)
import numpy as np
import logging
import ssl # ✅ استيراد مكتبة ssl للتحقق
from botocore.exceptions import ClientError # ✅ استيراد ClientError
import base58 # ✅ استيراد base58 للتحقق من عناوين Solana

# تعطيل تسجيل HTTP المزعج
logging.getLogger("httpx").setLevel(logging.WARNING)
logging.getLogger("httpcore").setLevel(logging.WARNING)

# ✅ الإصلاح: تعريف ثابت لتوقيع حدث التحويل ERC20
TRANSFER_EVENT_SIGNATURE = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"

class EnhancedWhaleMonitor:
    def __init__(self, contracts_db=None, r2_service=None):
        # ✅ الإصلاح: إنشاء سياق SSL آمن للتحقق من الشهادات
        self.ssl_context = ssl.create_default_context()

        self.http_client = httpx.AsyncClient(
            timeout=30.0,
            limits=httpx.Limits(max_connections=100, max_keepalive_connections=20),
            follow_redirects=True,
            verify=self.ssl_context # ✅ استخدام سياق SSL للتحقق
        )

        # الاحتفاظ بالمفاتيح المطلوبة فقط
        self.moralis_key = os.getenv("MORALIS_KEY")
        self.etherscan_key = os.getenv("ETHERSCAN_KEY")
        self.infura_key = os.getenv("INFURA_KEY")
        # ✅ إضافة مفاتيح اختيارية للمستكشفات الأخرى
        self.bscscan_key = os.getenv("BSCSCAN_KEY")
        self.polygonscan_key = os.getenv("POLYGONSCAN_KEY")


        # التحقق من صحة المفاتيح
        self._validate_api_keys()

        self.whale_threshold_usd = 50000

        # تحسين تخزين العقود والشبكات
        self.contracts_db = {}
        self._initialize_contracts_db(contracts_db or {})

        self.data_manager = None
        # ✅ الإصلاح: إضافة منظم طلبات لـ CoinGecko
        self.coingecko_semaphore = asyncio.Semaphore(1) # 1 طلب متزامن فقط لـ CoinGecko
        # ✅ الإصلاح: إضافة منظم طلبات لـ RPC
        self.rpc_semaphore = asyncio.Semaphore(5) # 5 طلبات RPC متزامنة كحد أقصى (لجميع الشبكات)

        self.r2_service = r2_service

        self.address_labels = {}
        self._initialize_comprehensive_exchange_addresses()

        self.token_price_cache = {}
        self.token_decimals_cache = {}

        # إضافة جميع الشبكات المدعومة مع Solana
        self.supported_networks = {
            'ethereum': {
                'name': 'Ethereum',
                'chain_id': '0x1',
                'native_coin': 'ETH',
                'explorer': 'etherscan',
                'rpc_endpoints': self._get_ethereum_rpc_endpoints(),
                'type': 'evm'
            },
            'bsc': {
                'name': 'Binance Smart Chain',
                'chain_id': '0x38',
                'native_coin': 'BNB',
                'explorer': 'bscscan',
                'rpc_endpoints': self._get_bsc_rpc_endpoints(),
                'type': 'evm'
            },
            'polygon': {
                'name': 'Polygon',
                'chain_id': '0x89',
                'native_coin': 'MATIC',
                'explorer': 'polygonscan',
                'rpc_endpoints': self._get_polygon_rpc_endpoints(),
                'type': 'evm'
            },
            'arbitrum': {
                'name': 'Arbitrum',
                'chain_id': '0xa4b1',
                'native_coin': 'ETH',
                'explorer': 'arbiscan',
                'rpc_endpoints': self._get_arbitrum_rpc_endpoints(),
                'type': 'evm'
            },
            'optimism': {
                'name': 'Optimism',
                'chain_id': '0xa',
                'native_coin': 'ETH',
                'explorer': 'optimistic',
                'rpc_endpoints': self._get_optimism_rpc_endpoints(),
                'type': 'evm'
            },
            'avalanche': {
                'name': 'Avalanche',
                'chain_id': '0xa86a',
                'native_coin': 'AVAX',
                'explorer': 'snowtrace',
                'rpc_endpoints': self._get_avalanche_rpc_endpoints(),
                'type': 'evm'
            },
            'fantom': {
                'name': 'Fantom',
                'chain_id': '0xfa',
                'native_coin': 'FTM',
                'explorer': 'ftmscan',
                'rpc_endpoints': self._get_fantom_rpc_endpoints(),
                'type': 'evm'
            },
            'solana': {
                'name': 'Solana',
                'chain_id': 'mainnet-beta',
                'native_coin': 'SOL',
                'explorer': 'solscan',
                'rpc_endpoints': self._get_solana_rpc_endpoints(), # ✅ استخدام الدالة المحدثة
                'type': 'solana'
            }
        }

        if self.r2_service:
            asyncio.create_task(self._load_contracts_from_r2())

    def _initialize_contracts_db(self, initial_contracts):
        """تهيئة قاعدة بيانات العقود مع تحسين تخزين الشبكات"""
        print("🔄 تهيئة قاعدة بيانات العقود...")

        for symbol, contract_data in initial_contracts.items():
            symbol_lower = symbol.lower() # استخدام lower لتوحيد المفاتيح
            if isinstance(contract_data, dict) and 'address' in contract_data and 'network' in contract_data:
                self.contracts_db[symbol_lower] = contract_data
            elif isinstance(contract_data, str):
                self.contracts_db[symbol_lower] = {
                    'address': contract_data,
                    'network': self._detect_network_from_address(contract_data)
                }
            # تجاهل الإدخالات غير الصالحة

        print(f"✅ تم تحميل {len(self.contracts_db)} عقد في قاعدة البيانات")


    def _detect_network_from_address(self, address):
        """اكتشاف الشبكة من عنوان العقد"""
        if not isinstance(address, str):
            return 'ethereum'

        address_lower = address.lower()

        if address_lower.startswith('0x') and len(address_lower) == 42:
             # هنا يمكن إضافة منطق أدق مستقبلاً للتمييز بين شبكات EVM
             # حاليًا، الافتراضي هو Ethereum
             return 'ethereum'
        elif len(address_lower) >= 32 and len(address_lower) <= 44: # طول عناوين Solana Base58
             base58_chars = set("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
             if all(char in base58_chars for char in address):
                 try:
                      base58.b58decode(address)
                      return 'solana'
                 except ValueError:
                      return 'ethereum' # ليس Base58 صالحًا
             else:
                  return 'ethereum' # يحتوي أحرف غير صالحة
        else:
            return 'ethereum' # افتراضي

    def _validate_api_keys(self):
        """التحقق من صحة مفاتيح API المطلوبة فقط"""
        print("🔍 التحقق من صحة مفاتيح API...")

        # إضافة المفاتيح الاختيارية الجديدة
        api_keys_to_check = {
            'MORALIS_KEY': 'moralis_key',
            'ETHERSCAN_KEY': 'etherscan_key',
            'INFURA_KEY': 'infura_key',
            'BSCSCAN_KEY': 'bscscan_key',
            'POLYGONSCAN_KEY': 'polygonscan_key'
            # يمكن إضافة مفاتيح أخرى هنا لمستكشفات أخرى
        }

        for env_var, attr_name in api_keys_to_check.items():
            # التأكد من أن الكلاس لديه الخاصية قبل الوصول إليها
            if hasattr(self, attr_name):
                 key_value = getattr(self, attr_name, None)
                 if key_value:
                    if isinstance(key_value, str) and len(key_value) >= 10:
                        print(f"✅ مفتاح {env_var} موجود وصالح مبدئيًا.")
                    else:
                        print(f"❌ مفتاح {env_var} موجود ولكنه غير صالح. سيتم التعطيل.")
                        setattr(self, attr_name, None)
                 else:
                    print(f"⚠️ مفتاح {env_var} غير متوفر أو فارغ.")
                    setattr(self, attr_name, None)
            else:
                 # إذا لم تكن الخاصية موجودة أصلاً
                 print(f"ℹ️ خاصية المفتاح {attr_name} (لـ {env_var}) غير معرفة في الكلاس.")
                 setattr(self, attr_name, None)

    def _get_ethereum_rpc_endpoints(self):
        """الحصول على نقاط RPC لشبكة Ethereum"""
        endpoints = []
        if self.infura_key:
            endpoints.append(f'https://mainnet.infura.io/v3/{self.infura_key}')
        endpoints.extend([
            'https://rpc.ankr.com/eth',
            'https://cloudflare-eth.com',
            'https://eth.llamarpc.com',
            'https://ethereum.publicnode.com',
            'https://1rpc.io/eth',
        ])
        return endpoints

    def _get_bsc_rpc_endpoints(self):
        """الحصول على نقاط RPC لشبكة BSC"""
        return [
            'https://bsc-dataseed.binance.org/',
            'https://bsc-dataseed1.defibit.io/',
            'https://bsc.publicnode.com',
            'https://binance.llamarpc.com',
            'https://1rpc.io/bnb',
            'https://rpc.ankr.com/bsc'
        ]

    def _get_polygon_rpc_endpoints(self):
        """الحصول على نقاط RPC لشبكة Polygon"""
        endpoints = []
        if self.infura_key:
            endpoints.append(f'https://polygon-mainnet.infura.io/v3/{self.infura_key}')
        endpoints.extend([
            'https://polygon-rpc.com',
            'https://polygon.llamarpc.com',
            'https://polygon-bor.publicnode.com',
            'https://1rpc.io/matic',
            'https://rpc.ankr.com/polygon'
        ])
        return endpoints

    def _get_arbitrum_rpc_endpoints(self):
        """الحصول على نقاط RPC لشبكة Arbitrum"""
        endpoints = []
        if self.infura_key:
            endpoints.append(f'https://arbitrum-mainnet.infura.io/v3/{self.infura_key}')
        endpoints.extend([
            'https://arb1.arbitrum.io/rpc',
            'https://arbitrum.llamarpc.com',
            'https://arbitrum.publicnode.com',
            'https://1rpc.io/arb',
            'https://rpc.ankr.com/arbitrum'
        ])
        return endpoints

    def _get_optimism_rpc_endpoints(self):
        """الحصول على نقاط RPC لشبكة Optimism"""
        endpoints = []
        if self.infura_key:
            endpoints.append(f'https://optimism-mainnet.infura.io/v3/{self.infura_key}')
        endpoints.extend([
            'https://mainnet.optimism.io',
            'https://optimism.llamarpc.com',
            'https://optimism.publicnode.com',
            'https://1rpc.io/op',
            'https://rpc.ankr.com/optimism'
        ])
        return endpoints

    def _get_avalanche_rpc_endpoints(self):
        """الحصول على نقاط RPC لشبكة Avalanche"""
        return [
            'https://api.avax.network/ext/bc/C/rpc',
            'https://avalanche.publicnode.com',
            'https://avax.llamarpc.com',
            'https://1rpc.io/avax/c',
            'https://rpc.ankr.com/avalanche'
        ]

    def _get_fantom_rpc_endpoints(self):
        """الحصول على نقاط RPC لشبكة Fantom"""
        return [
            'https://rpc.ftm.tools/',
            'https://fantom.publicnode.com',
            'https://fantom.llamarpc.com',
            'https://1rpc.io/ftm',
            'https://rpc.ankr.com/fantom'
        ]

    def _get_solana_rpc_endpoints(self):
        """الحصول على نقاط RPC مجانية لشبكة Solana (بدون مفاتيح API) - محدثة"""
        endpoints = [
            'https://api.mainnet-beta.solana.com',      # الرسمي (قد يكون محدود الطلبات بشدة)
            'https://solana-mainnet.publicnode.com',    # PublicNode (جديد وموثوق)
            'https://ssc-dao.genesysgo.net/',           # GenesysGo (قديم نسبياً)
            'https://solana-mainnet.rpc.extrnode.com',  # Extrnode (مزود عام)
            'https://solana-mainnet.phantom.tech/',     # Phantom Wallet RPC (قد يكون للاستخدام المحدود)
            'https://rpc.ankr.com/solana',            # Ankr (يسبب 403 غالباً بدون مفتاح)
            # 'https://mainnet.rpc.solana.pyth.network',  # تمت إزالته (خطأ DNS)
        ]
        print(f"ℹ️ قائمة Solana RPC Endpoints (مجانية وبدون مفاتيح) المستخدمة: {endpoints}")
        return endpoints

    def _initialize_comprehensive_exchange_addresses(self):
        """تهيئة قاعدة بيانات شاملة لعناوين كل المنصات"""
        self.exchange_addresses = {
            'kucoin': [
                '0x2b5634c42055806a59e9107ed44d43c426e58258', '0x689c56aef474df92d44a1b70850f808488f9769c',
                '0xa1d8d972560c2f8144af871db508f0b0b10a3fbf', '0x4ad64983349c49defe8d7a4686202d24b25d0ce8',
                '0x1692e170361cefd1eb7240ec13d048fd9af6d667', '0xd6216fc19db775df9774a6e33526131da7d19a2c',
                '0xe59cd29be3be4461d79c0881a238c467a2b3775c', '0x899b5d52671830f567bf43a14684eb14e1f945fe'
            ],
            'binance': [
                '0x3f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be', '0xd551234ae421e3bcba99a0da6d736074f22192ff',
                '0x564286362092d8e7936f0549571a803b203aaced', '0x0681d8db095565fe8a346fa0277bffde9c0edbbf',
                '0xfe9e8709d3215310075d67e3ed32a380ccf451c8', '0x28c6c06298d514db089934071355e5743bf21d60'
            ],
            'coinbase': [
                '0x71660c4005ba85c37ccec55d0c4493e66fe775d3', '0x503828976d22510aad0201ac7ec88293211d23da',
                '0xddfabcdc4d8ffc6d5beaf154f18b778f892a0740'
            ],
            'kraken': [ '0x2910543af39aba0cd09dbb2d50200b3e800a63d2', '0xa160cdab225685da1d56aa342ad8841c3b53f291' ],
            'okx': [ '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b', '0x2c8fbb630289363ac80705a1a61273f76fd5a161' ],
            'gate': [ '0x0d0707963952f2fba59dd06f2b425ace40b492fe', '0xc6f9741f5a5a676b91171804cf3c500ab438bb6e' ],
            'uniswap': [ '0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad', '0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45' ],
            'pancakeswap': [ '0x10ed43c718714eb63d5aa57b78b54704e256024e', '0x13f4ea83d0bd40e75c8222255bc855a974568dd4' ],
            'solana_kucoin': ['F3a5ZLPKUCrCj6CGKP2m9wS9Y2L8RcUBoJq9L2D2sUYi', '2AQdpHJ2JpcEgPiATFnAKfV9hPMvouWJAhKvamQ2Krqy' ],
            'solana_binance': ['5tzFkiKscXHK5ZXCGbXZJpXaWuBtZ6RrH8eL2a7Yi7Vn', '9WzDXwBbmkg8ZTbNMqUxvQRApF22xwsgMj2SUZrchK2E'],
            'solana_coinbase': ['CeBiLnAE3UAW9mCDSjD1VULKLeQjefjN4d941fVKazSM'],
            'solana_wormhole': ['worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth']
        }

        self.address_categories = {'exchange': set(), 'cex': set(), 'dex': set(), 'bridge': set(), 'whale': set(), 'unknown': set()}

        for category, addresses in self.exchange_addresses.items():
            for address in addresses:
                if not isinstance(address, str): continue
                addr_lower = address.lower()
                self.address_labels[addr_lower] = category
                self.address_categories['exchange'].add(addr_lower)

                if category in ['kucoin', 'binance', 'coinbase', 'kraken', 'okx', 'gate', 'solana_kucoin', 'solana_binance', 'solana_coinbase']:
                    self.address_categories['cex'].add(addr_lower)
                elif category in ['uniswap', 'pancakeswap']:
                    self.address_categories['dex'].add(addr_lower)
                elif 'wormhole' in category:
                     self.address_categories['bridge'].add(addr_lower)

        print(f"✅ تم تهيئة {len(self.address_labels)} عنوان منصة/بروتوكول معروف.")


    async def _load_contracts_from_r2(self):
        """تحميل قاعدة بيانات العقود من R2"""
        if not self.r2_service: return
        try:
            key = "contracts.json"
            response = self.r2_service.s3_client.get_object(Bucket="trading", Key=key)
            contracts_data = json.loads(response['Body'].read())

            loaded_count = 0
            updated_count = 0
            updated_contracts_db = self.contracts_db.copy()
            for symbol, contract_data in contracts_data.items():
                symbol_lower = symbol.lower()
                if isinstance(contract_data, dict) and 'address' in contract_data and 'network' in contract_data:
                    updated_contracts_db[symbol_lower] = contract_data
                    loaded_count += 1
                elif isinstance(contract_data, str):
                    new_format = {
                        'address': contract_data,
                        'network': self._detect_network_from_address(contract_data)
                    }
                    updated_contracts_db[symbol_lower] = new_format
                    loaded_count += 1
                    updated_count +=1

            self.contracts_db = updated_contracts_db

            print(f"✅ تم تحميل {loaded_count} عقد من R2.")
            if updated_count > 0:
                 print(f"   ℹ️ تم تحديث صيغة {updated_count} عقد إلى الصيغة الجديدة.")
                 await self._save_contracts_to_r2()

        except ClientError as e:
            if e.response['Error']['Code'] == 'NoSuchKey':
                print("⚠️ لم يتم العثور على قاعدة بيانات العقود في R2. ستبدأ فارغة.")
            else:
                print(f"❌ خطأ ClientError أثناء تحميل العقود من R2: {e}")
        except Exception as e:
            print(f"❌ خطأ عام أثناء تحميل العقود من R2: {e}")

    async def get_symbol_whale_activity(self, symbol, contract_address=None):
        """
        مراقبة تحركات الحيتان لعملة محددة مع معالجة العملات الأصلية
        """
        try:
            print(f"🔍 بدء مراقبة الحيتان الحقيقية للعملة: {symbol}")

            native_coins = ['BTC', 'LTC', 'ETH', 'BNB', 'ADA', 'DOT', 'XRP', 'DOGE', 'BCH', 'XLM', 'TRX', 'EOS', 'XMR']
            base_symbol = symbol.split("/")[0].upper() if '/' in symbol else symbol.upper()

            if base_symbol in native_coins:
                print(f"ℹ️ {symbol} عملة أصلية - لا توجد بيانات حيتان للتوكنات")
                return self._create_native_coin_response(symbol)

            contract_info = None
            network = None

            if contract_address and isinstance(contract_address, str):
                network = self._detect_network_from_address(contract_address)
                contract_info_from_db = await self._find_contract_address_enhanced(symbol)
                if contract_info_from_db and contract_info_from_db.get('address', '').lower() != contract_address.lower():
                     print(f"   ⚠️ تم توفير عنوان عقد ({contract_address}) يختلف عن المخزن لـ {symbol}. سيتم استخدام العنوان المُقدم.")
                     contract_info = {'address': contract_address, 'network': network}
                elif contract_info_from_db:
                      contract_info = contract_info_from_db
                else:
                     contract_info = {'address': contract_address, 'network': network}
            else:
                 contract_info = await self._find_contract_address_enhanced(symbol)

            if not contract_info or not contract_info.get('address') or not contract_info.get('network'):
                print(f"❌ لم يتم العثور على عقد أو شبكة للعملة {symbol}")
                return self._create_no_contract_response(symbol)

            contract_address_final = contract_info['address']
            network_final = contract_info['network']

            print(f"🌐 البحث في الشبكة المحددة: {network_final} للعقد: {contract_address_final}")

            transfers = await self._get_targeted_transfer_data(contract_address_final, network_final)

            if not transfers:
                print(f"⚠️ لم يتم العثور على تحويلات للعملة {symbol} على شبكة {network_final}")
                return self._create_no_transfers_response(symbol)

            recent_transfers = self._filter_recent_transfers(transfers, max_minutes=120)

            if not recent_transfers:
                print(f"⚠️ لا توجد تحويلات حديثة للعملة {symbol}")
                return self._create_no_recent_activity_response(symbol)

            print(f"📊 تم جلب {len(recent_transfers)} تحويلة حديثة فريدة للعملة {symbol}")

            analysis = await self._analyze_enhanced_whale_impact(recent_transfers, symbol, contract_address_final, network_final)

            return analysis

        except Exception as e:
            print(f"❌ خطأ في مراقبة الحيتان للعملة {symbol}: {e}")
            traceback.print_exc()
            return self._create_error_response(symbol, str(e))

    def _create_native_coin_response(self, symbol):
        """إنشاء رد للعملات الأصلية التي ليس لها tokens"""
        return {
            'symbol': symbol, 'data_available': False, 'error': 'NATIVE_COIN_NO_TOKEN',
            'trading_signal': {'action': 'HOLD', 'confidence': 0.3, 'reason': f'{symbol} عملة أصلية - لا توجد بيانات حيتان للتوكنات'},
            'llm_friendly_summary': {'whale_activity_summary': f'{symbol} عملة أصلية - نظام مراقبة الحيتان الحالي مصمم للتوكنات فقط', 'recommended_action': 'HOLD', 'confidence': 0.3, 'key_metrics': {'whale_movement_impact': 'NOT_APPLICABLE'}}
        }

    async def _get_targeted_transfer_data(self, contract_address, network):
        """جلب بيانات التحويلات من الشبكة المحددة فقط"""
        print(f"🌐 جلب بيانات التحويلات من شبكة {network} للعقد: {contract_address}")

        if isinstance(contract_address, dict):
            contract_address = contract_address.get(network)
            if not contract_address:
                print(f"❌ لم يتم العثور على عنوان للشبكة {network}")
                return []

        if not isinstance(contract_address, str):
            print(f"❌ عنوان العقد غير صالح: {type(contract_address)}")
            return []

        if network in self.supported_networks:
            net_config = self.supported_networks[network]
            if net_config['type'] == 'evm':
                return await self._get_evm_network_transfer_data(contract_address, network)
            elif net_config['type'] == 'solana':
                return await self._get_solana_transfer_data(contract_address, network)

        print(f"⚠️ نوع الشبكة {network} غير مدعوم حاليًا.")
        return []

    async def _get_evm_network_transfer_data(self, contract_address, network):
        """جلب بيانات التحويلات من شبكة EVM محددة (باستخدام RPC و Explorer)"""
        try:
            transfers = []
            rpc_successful = False

            try:
                rpc_transfers = await self._get_rpc_token_data_targeted(contract_address, network)
                if rpc_transfers:
                    transfers.extend(rpc_transfers)
                    rpc_successful = True
                    print(f"✅ RPC {network}: تم جلب {len(rpc_transfers)} تحويلة عبر eth_getLogs")
                else:
                    print(f"⚠️ RPC {network}: لم يتم العثور على تحويلات عبر eth_getLogs")
            except Exception as rpc_error:
                print(f"❌ خطأ في جلب بيانات RPC {network}: {rpc_error}")

            if not rpc_successful or len(transfers) < 5:
                 print(f"ℹ️ محاولة جلب بيانات إضافية من Explorer لـ {network}...")
                 try:
                    explorer_transfers = await self._get_explorer_token_data(contract_address, network)
                    if explorer_transfers:
                        existing_hashes = {f"{t.get('hash')}-{t.get('logIndex','N/A')}" for t in transfers}
                        new_explorer_transfers = []
                        for et in explorer_transfers:
                            explorer_key = f"{et.get('hash')}-{et.get('logIndex','N/A')}"
                            if explorer_key not in existing_hashes:
                                new_explorer_transfers.append(et)

                        if new_explorer_transfers:
                             transfers.extend(new_explorer_transfers)
                             print(f"✅ Explorer {network}: تم إضافة {len(new_explorer_transfers)} تحويلة جديدة.")
                        else:
                             print(f"⚠️ Explorer {network}: لم يتم العثور على تحويلات جديدة.")
                    else:
                        print(f"⚠️ Explorer {network}: لم يتم العثور على تحويلات.")
                 except Exception as explorer_error:
                     print(f"❌ خطأ في جلب بيانات Explorer {network}: {explorer_error}")

            print(f"📊 إجمالي التحويلات المجمعة لـ {network}: {len(transfers)}")
            final_transfers = []
            seen_keys = set()
            for t in transfers:
                 key = f"{t.get('hash')}-{t.get('logIndex','N/A')}"
                 if key not in seen_keys:
                      final_transfers.append(t)
                      seen_keys.add(key)

            return final_transfers

        except Exception as e:
            print(f"❌ خطأ عام في جلب بيانات {network}: {e}")
            return []

    async def _get_rpc_token_data_targeted(self, contract_address, network='ethereum', blocks_to_scan=500):
        """جلب تحويلات التوكن ERC20 عبر RPC باستخدام eth_getLogs"""
        try:
            if network not in self.supported_networks or self.supported_networks[network]['type'] != 'evm':
                print(f"⚠️ شبكة {network} غير مدعومة أو ليست EVM لـ eth_getLogs.")
                return []

            if not isinstance(contract_address, str) or not contract_address.startswith('0x'):
                print(f"❌ عنوان العقد غير صالح لـ EVM: {contract_address}")
                return []
            contract_address_checksum = contract_address

            endpoints = self.supported_networks[network]['rpc_endpoints']
            if not endpoints:
                 print(f"   ❌ لا توجد نقاط RPC معرفة لشبكة {network}.")
                 return []

            transfers = []
            successful_endpoint = None

            for endpoint in endpoints:
                try:
                    endpoint_name = endpoint.split('//')[1].split('/')[0] if '//' in endpoint else endpoint
                    print(f"   🔍 محاولة جلب سجلات التحويلات لـ {network} عبر {endpoint_name}...")

                    # ✅ الإصلاح: استخدام "self.rpc_semaphore"
                    async with self.rpc_semaphore:
                        payload_block = {"jsonrpc": "2.0", "method": "eth_blockNumber", "params": [], "id": int(time.time())}
                        response_block = await self.http_client.post(endpoint, json=payload_block, timeout=15.0)
                    response_block.raise_for_status()
                    json_response_block = response_block.json()
                    result_block = json_response_block.get('result')
                    if not result_block:
                        rpc_error = json_response_block.get('error')
                        error_msg = rpc_error.get('message') if rpc_error else 'No block number result'
                        print(f"     ❌ لم يتم الحصول على رقم أحدث كتلة من {endpoint_name}: {error_msg}")
                        continue
                    latest_block = int(result_block, 16)

                    async with self.rpc_semaphore:
                        payload_latest_block_time = {"jsonrpc": "2.0", "method": "eth_getBlockByNumber", "params": [hex(latest_block), False], "id": int(time.time())+1}
                        response_latest_time = await self.http_client.post(endpoint, json=payload_latest_block_time, timeout=15.0)
                    latest_block_timestamp_approx = int(time.time())
                    if response_latest_time.status_code == 200:
                         latest_block_data = response_latest_time.json().get('result')
                         if latest_block_data and latest_block_data.get('timestamp'):
                              latest_block_timestamp_approx = int(latest_block_data['timestamp'], 16)

                    from_block = max(0, latest_block - blocks_to_scan)
                    print(f"     📦 فحص الكتل من {from_block} إلى {latest_block} (آخر {blocks_to_scan} كتل تقريباً)")

                    payload_logs = {
                        "jsonrpc": "2.0", "method": "eth_getLogs",
                        "params": [{"fromBlock": hex(from_block), "toBlock": hex(latest_block), "address": contract_address_checksum, "topics": [TRANSFER_EVENT_SIGNATURE]}],
                        "id": int(time.time())+2
                    }
                    async with self.rpc_semaphore:
                        response_logs = await self.http_client.post(endpoint, json=payload_logs, timeout=45.0)
                    response_logs.raise_for_status()
                    json_response_logs = response_logs.json()
                    logs = json_response_logs.get('result')

                    if logs is None:
                        rpc_error = json_response_logs.get('error')
                        error_msg = rpc_error.get('message') if rpc_error else 'Invalid logs response'
                        print(f"     ❌ استجابة السجلات غير صالحة من {endpoint_name}: {error_msg}")
                        continue

                    if not logs:
                        print(f"     ✅ لا توجد سجلات تحويل لـ {contract_address} في آخر {blocks_to_scan} كتل عبر {endpoint_name}")
                        successful_endpoint = endpoint_name
                        break

                    print(f"     📊 تم العثور على {len(logs)} سجل تحويل محتمل.")

                    for log in logs:
                        try:
                            topics = log.get('topics', [])
                            data = log.get('data', '0x')
                            block_num_hex = log.get('blockNumber')
                            tx_hash = log.get('transactionHash')
                            log_index_hex = log.get('logIndex', '0x0')

                            if len(topics) == 3 and data != '0x' and block_num_hex and tx_hash:
                                sender = '0x' + topics[1][26:]
                                receiver = '0x' + topics[2][26:]
                                value = str(int(data, 16))
                                block_num = str(int(block_num_hex, 16))
                                log_index = str(int(log_index_hex, 16))

                                transfers.append({
                                    'hash': tx_hash, 'from': sender.lower(), 'to': receiver.lower(),
                                    'value': value, 'timeStamp': str(latest_block_timestamp_approx),
                                    'blockNumber': block_num, 'network': network, 'logIndex': log_index
                                })
                            else:
                                 print(f"       ⚠️ سجل غير متوافق مع Transfer Event: Topics={len(topics)}, Data={data[:10]}...")

                        except (ValueError, TypeError, KeyError, IndexError) as log_parse_error:
                            print(f"       ⚠️ خطأ في تحليل سجل فردي: {log_parse_error} - Log: {log}")
                            continue

                    print(f"     ✅ تم تحليل {len(transfers)} تحويلة بنجاح من {endpoint_name}")
                    successful_endpoint = endpoint_name
                    break

                except ssl.SSLCertVerificationError as ssl_err:
                     print(f"     ❌ خطأ SSL مع {endpoint_name}: {ssl_err}")
                     continue
                except httpx.HTTPStatusError as http_err:
                    print(f"     ❌ خطأ HTTP من {endpoint_name}: {http_err.response.status_code} - {http_err}")
                    continue
                except (httpx.RequestError, asyncio.TimeoutError) as req_err:
                    print(f"     ❌ خطأ اتصال/مهلة بـ {endpoint_name}: {req_err}")
                    continue
                except json.JSONDecodeError as json_err:
                     print(f"     ❌ خطأ في تحليل استجابة JSON من {endpoint_name}: {json_err}")
                     continue
                except Exception as e:
                    if "Cannot reopen a client instance" in str(e):
                         print(f"     ❌ فشل فادح: محاولة استخدام client مغلق. {e}")
                         return transfers
                    print(f"     ❌ فشل غير متوقع مع {endpoint_name}: {e}")
                    traceback.print_exc()
                    continue

            if successful_endpoint:
                print(f"✅ اكتمل جلب بيانات RPC لـ {network} بنجاح باستخدام {successful_endpoint}. إجمالي التحويلات الأولية: {len(transfers)}")
            else:
                 print(f"❌ فشلت جميع نقاط RPC لـ {network} في الاستجابة بشكل صحيح.")

            return transfers

        except Exception as e:
            print(f"❌ فشل عام في جلب بيانات RPC لـ {network}: {e}")
            traceback.print_exc()
            return []


    async def _get_solana_transfer_data(self, token_address, network='solana', limit=50):
        """جلب بيانات التحويلات من شبكة Solana مع تحسين التعامل مع الأخطاء"""
        try:
            print(f"   🔍 جلب تحويلات Solana للتوكن: {token_address}")
            transfers = []
            endpoints = self.supported_networks[network]['rpc_endpoints']
            if not endpoints:
                print(f"   ❌ لا توجد نقاط RPC معرفة لشبكة Solana.")
                return []

            signatures_found = False
            successful_endpoint_name = None

            for endpoint in endpoints:
                endpoint_name = endpoint.split('//')[-1]
                try:
                    payload_signatures = {
                        "jsonrpc": "2.0", "id": int(time.time()),
                        "method": "getSignaturesForAddress",
                        "params": [ token_address, {"limit": limit, "commitment": "confirmed"} ]
                    }

                    # ✅ الإصلاح: استخدام "self.rpc_semaphore"
                    async with self.rpc_semaphore:
                        response_signatures = await self.http_client.post(endpoint, json=payload_signatures, timeout=20.0)

                    if response_signatures.status_code == 403:
                        print(f"     ⚠️ Solana endpoint {endpoint_name} failed: 403 Forbidden")
                        continue
                    elif response_signatures.status_code != 200:
                        print(f"     ⚠️ Solana endpoint {endpoint_name} failed: {response_signatures.status_code}")
                        continue

                    data_signatures = response_signatures.json()

                    if 'error' in data_signatures:
                        print(f"     ❌ خطأ RPC (getSignatures) من {endpoint_name}: {data_signatures['error'].get('message', 'Unknown RPC error')}")
                        continue
                    if 'result' not in data_signatures or data_signatures['result'] is None:
                        print(f"     ✅ Solana: لا توجد معاملات حديثة ({endpoint_name})")
                        successful_endpoint_name = endpoint_name
                        signatures_found = True
                        break

                    signatures_data = data_signatures['result']
                    if not signatures_data:
                        print(f"     ✅ Solana: لا توجد معاملات حديثة ({endpoint_name})")
                        successful_endpoint_name = endpoint_name
                        signatures_found = True
                        break

                    signatures = [tx['signature'] for tx in signatures_data]
                    print(f"     📊 Solana: وجد {len(signatures)} توقيع معاملة محتملة ({endpoint_name})")
                    signatures_found = True
                    successful_endpoint_name = endpoint_name

                    processed_count = 0
                    transaction_tasks = []
                    
                    # ✅ الإصلاح: تمرير "self" إلى الدالة المساعدة
                    for signature in signatures[:20]:
                        transaction_tasks.append(
                            self._get_solana_transaction_detail_with_retry(self, signature, endpoint, self.http_client)
                        )
                    transaction_details = await asyncio.gather(*transaction_tasks)

                    for detail_result in transaction_details:
                        if detail_result and self._is_solana_token_transfer(detail_result, token_address):
                            transfer = await self._parse_solana_transfer(detail_result, token_address)
                            if transfer:
                                transfers.append(transfer)
                                processed_count += 1

                    print(f"     ✅ Solana: تم تحليل {processed_count} تحويلة توكن فعلية من {endpoint_name}")
                    break

                except ssl.SSLCertVerificationError as ssl_err:
                     print(f"     ❌ فشل endpoint في Solana ({endpoint_name}): خطأ في شهادة SSL - {ssl_err}")
                     continue
                except httpx.HTTPStatusError as http_err:
                     if http_err.response.status_code == 403: print(f"     ⚠️ Solana endpoint {endpoint_name} failed: 403 Forbidden")
                     else: print(f"     ❌ خطأ HTTP من Solana ({endpoint_name}): {http_err.response.status_code}")
                     continue
                except (httpx.RequestError, asyncio.TimeoutError) as req_err:
                    print(f"     ❌ خطأ اتصال/مهلة بـ Solana ({endpoint_name}): {req_err}")
                    continue
                except json.JSONDecodeError as json_err:
                     print(f"     ❌ خطأ في تحليل استجابة JSON من Solana ({endpoint_name}): {json_err}")
                     continue
                except Exception as e:
                    if "Cannot reopen a client instance" in str(e):
                         print(f"     ❌ فشل فادح: محاولة استخدام client مغلق. {e}")
                         return transfers
                    print(f"     ❌ فشل غير متوقع مع endpoint Solana ({endpoint_name}): {e}")
                    traceback.print_exc()
                    continue

            if successful_endpoint_name:
                 print(f"✅ اكتمل جلب بيانات Solana بنجاح باستخدام {successful_endpoint_name}. إجمالي التحويلات النهائية: {len(transfers)}")
            elif not signatures_found:
                 print(f"   ❌ فشلت جميع نقاط RPC لـ Solana في الاستجابة بشكل صحيح لجلب التوقيعات.")
            else:
                 print(f"⚠️ تم العثور على توقيعات Solana ولكن حدثت مشاكل في جلب التفاصيل أو تحليلها. التحويلات النهائية: {len(transfers)}")

            return transfers

        except Exception as e:
            print(f"❌ فشل عام في جلب بيانات Solana: {e}")
            traceback.print_exc()
            return []


    # ✅ الإصلاح: إضافة "self" كباراميتر
    async def _get_solana_transaction_detail_with_retry(self, signature, endpoint, client: httpx.AsyncClient, retries=2, delay=0.5):
        """جلب تفاصيل معاملة Solana مع محاولات إعادة بسيطة باستخدام client مشترك"""
        last_exception = None
        for attempt in range(retries):
            try:
                # ✅ الإصلاح: استخدام "self.rpc_semaphore"
                async with self.rpc_semaphore:
                    detail = await self._get_solana_transaction_detail(signature, endpoint, client)
                
                if detail:
                    return detail
                elif detail is None:
                     return None
                print(f"       ⚠️ نتيجة فارغة غير متوقعة لـ {signature[:10]}... (محاولة {attempt + 1}/{retries})")
                last_exception = Exception("Empty result without error")

            except (httpx.RequestError, asyncio.TimeoutError, ssl.SSLError) as e:
                print(f"       ⏳ خطأ مؤقت ({type(e).__name__}) في جلب تفاصيل {signature[:10]}... (محاولة {attempt + 1}/{retries})")
                last_exception = e
                if attempt < retries - 1:
                    await asyncio.sleep(delay * (attempt + 1))
                else:
                    print(f"       ❌ فشل جلب تفاصيل {signature[:10]}... بعد {retries} محاولات.")
                    return None

            except Exception as e:
                 print(f"       ❌ خطأ غير متوقع في جلب تفاصيل {signature[:10]}...: {e}")
                 return None

            if attempt < retries - 1 and not isinstance(last_exception, (httpx.RequestError, asyncio.TimeoutError, ssl.SSLError)):
                 await asyncio.sleep(delay * (attempt + 1))

        return None


    async def _get_solana_transaction_detail(self, signature, endpoint, client: httpx.AsyncClient):
        """جلب تفاصيل معاملة Solana (يستخدم الآن client مُمرر)"""
        try:
            payload = {
                "jsonrpc": "2.0",
                "id": f"gtx-{signature[:8]}-{int(time.time()*1000)}",
                "method": "getTransaction",
                "params": [ signature, {"encoding": "jsonParsed", "maxSupportedTransactionVersion": 0, "commitment": "confirmed"} ]
            }
            # ✅ الإصلاح: لا يوجد semaphore هنا، يتم تطبيقه في الدالة التي تستدعيها
            response = await client.post(endpoint, json=payload, timeout=20.0)
            response.raise_for_status()

            data = response.json()
            if 'error' in data:
                 print(f"       ❌ خطأ RPC في getTransaction لـ {signature[:10]}... : {data['error'].get('message')}")
                 return None
            result = data.get('result')
            if result is None:
                 print(f"       ℹ️ لم يتم العثور على نتيجة للمعاملة {signature[:10]}...")
                 return None
            return result

        except httpx.HTTPStatusError as http_err:
            print(f"       ⚠️ فشل HTTP في getTransaction لـ {signature[:10]}... : {http_err.response.status_code}")
            return None
        except (httpx.RequestError, asyncio.TimeoutError) as req_err:
             print(f"       ⏳ خطأ اتصال/مهلة في getTransaction لـ {signature[:10]}...")
             raise req_err
        except ssl.SSLError as ssl_err:
             print(f"       ❌ خطأ SSL في getTransaction لـ {signature[:10]}... : {ssl_err}")
             raise ssl_err
        except json.JSONDecodeError as json_err:
             print(f"       ❌ خطأ JSON في getTransaction لـ {signature[:10]}... : {response.text[:200]}")
             return None
        except Exception as e:
            print(f"       ❌ استثناء غير متوقع في getTransaction لـ {signature[:10]}... : {e}")
            traceback.print_exc()
            return None


    def _is_solana_token_transfer(self, transaction, token_address):
        """التحقق إذا كانت المعاملة تحويل توكن في Solana"""
        try:
            if not transaction or 'meta' not in transaction or transaction.get('meta') is None:
                return False

            meta = transaction['meta']
            post_balances = meta.get('postTokenBalances')
            pre_balances = meta.get('preTokenBalances')

            if post_balances is None and pre_balances is None:
                inner_instructions = meta.get('innerInstructions', [])
                for inner_inst_set in inner_instructions:
                    for inst in inner_inst_set.get('instructions', []):
                         parsed = inst.get('parsed')
                         if isinstance(parsed, dict) and parsed.get('type') in ['transfer', 'transferChecked'] and parsed.get('info', {}).get('mint') == token_address:
                              return True
                return False

            all_balances = (post_balances or []) + (pre_balances or [])
            for balance in all_balances:
                if isinstance(balance, dict) and balance.get('mint') == token_address:
                    pre_amount = next((pb.get('uiTokenAmount',{}).get('amount','0') for pb in (pre_balances or []) if isinstance(pb, dict) and pb.get('owner') == balance.get('owner') and pb.get('mint') == token_address), '0')
                    post_amount = balance.get('uiTokenAmount',{}).get('amount','0')
                    try:
                         if int(post_amount) != int(pre_amount):
                              return True
                    except (ValueError, TypeError):
                         pass

            return False
        except Exception as e:
            return False

    async def _parse_solana_transfer(self, transaction, token_address):
        """تحليل معاملة Solana لاستخراج بيانات التحويل"""
        try:
            if not transaction or 'transaction' not in transaction or not transaction['transaction'].get('signatures'):
                 return None
            signature = transaction['transaction']['signatures'][0]
            block_time = transaction.get('blockTime')
            timestamp_to_use = str(block_time) if block_time else str(int(time.time()))
            slot = transaction.get('slot', 0)

            sender = None
            receiver = None
            amount_raw = 0

            meta = transaction.get('meta')
            if not meta: return None

            pre_balances = {bal.get('owner'): bal for bal in meta.get('preTokenBalances', []) if isinstance(bal, dict) and bal.get('mint') == token_address}
            post_balances = {bal.get('owner'): bal for bal in meta.get('postTokenBalances', []) if isinstance(bal, dict) and bal.get('mint') == token_address}

            all_owners = set(list(pre_balances.keys()) + list(post_balances.keys()))

            for owner in all_owners:
                pre_bal_data = pre_balances.get(owner, {}).get('uiTokenAmount', {})
                post_bal_data = post_balances.get(owner, {}).get('uiTokenAmount', {})

                pre_amount_str = pre_bal_data.get('amount', '0')
                post_amount_str = post_bal_data.get('amount', '0')

                try:
                    pre_amount_raw = int(pre_amount_str)
                    post_amount_raw = int(post_amount_str)
                    diff_raw = post_amount_raw - pre_amount_raw

                    if diff_raw > 0:
                        receiver = owner
                        amount_raw = diff_raw
                    elif diff_raw < 0:
                        sender = owner

                except (ValueError, TypeError):
                     print(f"       ⚠️ تحذير: فشل تحليل القيمة الخام لـ Solana ({owner}).")
                     pre_ui = pre_bal_data.get('uiAmount', 0.0) or 0.0
                     post_ui = post_bal_data.get('uiAmount', 0.0) or 0.0
                     diff_ui = post_ui - pre_ui
                     if diff_ui > 1e-9:
                          receiver = owner
                          decimals = await self._get_token_decimals(token_address, 'solana')
                          if decimals is not None: amount_raw = int(diff_ui * (10**decimals))
                          else: amount_raw = 0
                     elif diff_ui < -1e-9:
                          sender = owner

            if not sender or not receiver or amount_raw == 0:
                inner_instructions = meta.get('innerInstructions', [])
                for inner_inst_set in inner_instructions:
                    for inst in inner_inst_set.get('instructions', []):
                         parsed = inst.get('parsed')
                         if isinstance(parsed, dict) and parsed.get('type') in ['transfer', 'transferChecked'] and parsed.get('info', {}).get('mint') == token_address:
                              info = parsed.get('info', {})
                              sender_inner = info.get('source') or info.get('authority')
                              receiver_inner = info.get('destination')
                              amount_inner_str = info.get('tokenAmount', {}).get('amount') or info.get('amount')
                              try:
                                   amount_inner_raw = int(amount_inner_str)
                                   if sender_inner and receiver_inner and amount_inner_raw > 0:
                                        sender = sender_inner
                                        receiver = receiver_inner
                                        amount_raw = amount_inner_raw
                                        break
                              except (ValueError, TypeError): continue
                         if sender and receiver and amount_raw > 0: break
                    if sender and receiver and amount_raw > 0: break

            if sender and receiver and amount_raw > 0:
                return {
                    'hash': signature, 'from': sender, 'to': receiver,
                    'value': str(amount_raw), 'timeStamp': timestamp_to_use,
                    'blockNumber': str(slot), 'network': 'solana',
                    'type': 'solana_transfer', 'logIndex': '0'
                }
            else:
                 return None

        except Exception as e:
            sig_short = signature[:10] if 'signature' in locals() and signature else "N/A"
            print(f"❌ خطأ في تحليل معاملة Solana ({sig_short}...): {e}")
            traceback.print_exc()
            return None


    async def _get_explorer_token_data(self, contract_address, network):
        """جلب بيانات التحويلات من Explorer"""
        if network not in self.supported_networks:
            return []

        try:
            explorer_config = {
                'ethereum': {'url': 'https://api.etherscan.io/api', 'key': self.etherscan_key},
                 'bsc': {'url': 'https://api.bscscan.com/api', 'key': self.bscscan_key},
                 'polygon': {'url': 'https://api.polygonscan.com/api', 'key': self.polygonscan_key}
            }

            if network not in explorer_config or not explorer_config[network].get('key'):
                 return []

            config = explorer_config[network]
            address_param = "contractaddress" if network == 'ethereum' else "address"

            params = {
                "module": "account", "action": "tokentx",
                address_param: contract_address,
                "page": 1, "offset": 100, "startblock": 0,
                "endblock": 999999999, "sort": "desc", "apikey": config['key']
            }

            # ✅ الإصلاح: استخدام "self.http_client" بدلاً من إنشاء client جديد
            # ويفضل استخدام semaphore خاص بالمستكشفات إذا كان لديهم حد طلبات صارم
            # حالياً، سنستخدم semaphore الـ RPC العام كإجراء وقائي
            async with self.rpc_semaphore:
                response = await self.http_client.get(config['url'], params=params, timeout=20.0)

            if response.status_code == 200:
                data = response.json()
                status = str(data.get('status', '0'))
                message = data.get('message', '').upper()

                if status == '1' and message == 'OK':
                    transfers = data.get('result', [])
                    processed_transfers = []
                    for tf in transfers:
                        if tf.get('tokenSymbol') and tf.get('contractAddress','').lower() == contract_address.lower():
                             tf['network'] = network
                             if 'from' in tf: tf['from'] = tf['from'].lower()
                             if 'to' in tf: tf['to'] = tf['to'].lower()
                             if 'logIndex' not in tf: tf['logIndex'] = 'N/A'
                             processed_transfers.append(tf)

                    print(f"     ✅ Explorer {network}: تم جلب {len(processed_transfers)} تحويلة توكن للعقد المحدد.")
                    return processed_transfers
                elif status == '0' and "NO TRANSACTIONS FOUND" in message:
                     print(f"     ✅ Explorer {network}: لا توجد تحويلات.")
                     return []
                else:
                    error_message = data.get('result', message)
                    print(f"⚠️ Explorer {network} returned error: Status={status}, Message={message}, Result={error_message}")
                    if "INVALID API KEY" in str(error_message).upper():
                         print(f"🚨 خطأ فادح: مفتاح API الخاص بـ {network.upper()} غير صالح!")
                    return []
            else:
                print(f"⚠️ Explorer {network} request failed: {response.status_code}")
                return []

        except Exception as e:
            if "Cannot reopen a client instance" in str(e):
                 print(f"     ❌ فشل فادح (Explorer): محاولة استخدام client مغلق. {e}")
                 return []
            print(f"❌ فشل جلب بيانات Explorer لـ {network}: {e}")
            return []

    def _filter_recent_transfers(self, transfers, max_minutes=120):
        """ترشيح التحويلات الحديثة فقط"""
        recent_transfers = []
        current_time_dt = datetime.now()
        cutoff_timestamp = int((current_time_dt - timedelta(minutes=max_minutes)).timestamp())
        processed_keys = set()

        for transfer in transfers:
            try:
                tx_hash = transfer.get('hash', 'N/A')
                log_index = transfer.get('logIndex', 'N/A')
                unique_key = f"{tx_hash}-{log_index}"

                if unique_key in processed_keys:
                    continue

                timestamp_str = transfer.get('timeStamp')
                if not timestamp_str or not timestamp_str.isdigit():
                    continue
                timestamp = int(timestamp_str)

                if timestamp >= cutoff_timestamp:
                    transfer_time_dt = datetime.fromtimestamp(timestamp)
                    time_diff = current_time_dt - transfer_time_dt

                    transfer['minutes_ago'] = round(time_diff.total_seconds() / 60, 2)
                    transfer['human_time'] = transfer_time_dt.isoformat()
                    recent_transfers.append(transfer)
                    processed_keys.add(unique_key)

            except (ValueError, TypeError, KeyError) as e:
                 print(f"   ⚠️ خطأ في ترشيح تحويلة: {e} - البيانات: {transfer}")
                 continue

        print(f"   🔍 تم ترشيح {len(recent_transfers)} تحويلة حديثة فريدة (آخر {max_minutes} دقيقة).")
        recent_transfers.sort(key=lambda x: int(x.get('timeStamp', 0)), reverse=True)
        return recent_transfers


    async def _analyze_enhanced_whale_impact(self, transfers, symbol, contract_address, network):
        """تحليل تأثير تحركات الحيتان المحسن (مع إصلاح KeyError)"""
        print(f"📊 تحليل {len(transfers)} تحويلة حديثة للعملة {symbol}")

        exchange_flows = {
            'to_exchanges': [], 'from_exchanges': [], 'other_transfers': [],
            'network_breakdown': defaultdict(lambda: {'to_exchanges': 0, 'from_exchanges': 0, 'other': 0})
        }
        total_volume_usd = 0.0
        whale_transfers_count = 0
        network_stats = defaultdict(int)
        unique_transfers = {}

        decimals = await self._get_token_decimals(contract_address, network)
        if decimals is None:
             print(f"❌ لا يمكن تحليل الحيتان بدون decimals لـ {symbol} على {network}.")
             return self._create_error_response(symbol, f"Failed to get decimals on {network}")

        for transfer in transfers:
            try:
                unique_key = f"{transfer.get('hash', '')}-{transfer.get('logIndex', 'N/A')}"
                if unique_key in unique_transfers: continue
                unique_transfers[unique_key] = True

                from_addr = str(transfer.get('from', '')).lower()
                to_addr = str(transfer.get('to', '')).lower()
                raw_value_str = transfer.get('value', '0')
                transfer_network = transfer.get('network', 'unknown')

                if not raw_value_str or not raw_value_str.isdigit(): continue
                raw_value = int(raw_value_str)
                if raw_value == 0: continue

                value_usd = await self._get_accurate_token_value_optimized(raw_value, decimals, symbol)

                if value_usd is None or value_usd < 0:
                     print(f"   ⚠️ قيمة USD غير صالحة ({value_usd}) للتحويلة {unique_key}. التخطي.")
                     continue

                total_volume_usd += value_usd
                network_stats[transfer_network] += 1

                if value_usd >= self.whale_threshold_usd:
                    whale_transfers_count += 1
                    is_to_exchange = to_addr in self.address_categories['exchange']
                    is_from_exchange = from_addr in self.address_categories['exchange']

                    if is_to_exchange:
                        exchange_flows['to_exchanges'].append({
                            'value_usd': value_usd, 'from_type': self._classify_address(from_addr),
                            'to_exchange': self._classify_address(to_addr),
                            'transaction': {'hash': transfer.get('hash'), 'minutes_ago': transfer.get('minutes_ago')},
                            'network': transfer_network
                        })
                        exchange_flows['network_breakdown'][transfer_network]['to_exchanges'] += 1
                    elif is_from_exchange:
                        exchange_flows['from_exchanges'].append({
                            'value_usd': value_usd, 'from_exchange': self._classify_address(from_addr),
                            'to_type': self._classify_address(to_addr),
                            'transaction': {'hash': transfer.get('hash'), 'minutes_ago': transfer.get('minutes_ago')},
                            'network': transfer_network
                        })
                        exchange_flows['network_breakdown'][transfer_network]['from_exchanges'] += 1
                    else:
                        exchange_flows['other_transfers'].append({
                             'value_usd': value_usd, 'from_type': self._classify_address(from_addr),
                             'to_type': self._classify_address(to_addr),
                             'transaction': {'hash': transfer.get('hash'), 'minutes_ago': transfer.get('minutes_ago')},
                             'network': transfer_network
                        })
                        exchange_flows['network_breakdown'][transfer_network]['other'] += 1
            except Exception as analysis_loop_error:
                 print(f"   ⚠️ خطأ في تحليل حلقة التحويلات: {analysis_loop_error}")
                 continue

        total_to_exchanges_usd = sum(t['value_usd'] for t in exchange_flows['to_exchanges'])
        total_from_exchanges_usd = sum(t['value_usd'] for t in exchange_flows['from_exchanges'])
        deposit_count = len(exchange_flows['to_exchanges'])
        withdrawal_count = len(exchange_flows['from_exchanges'])
        net_flow_usd = total_to_exchanges_usd - total_from_exchanges_usd

        exchange_flows['deposit_count'] = deposit_count
        exchange_flows['withdrawal_count'] = withdrawal_count
        exchange_flows['net_flow_usd'] = net_flow_usd

        print(f"🐋 تحليل الحيتان لـ {symbol}:")
        print(f"   • إجمالي التحويلات الفريدة: {len(unique_transfers)}")
        print(f"   • تحويلات الحيتان (>${self.whale_threshold_usd:,.0f}): {whale_transfers_count}")
        print(f"   • إيداعات للمنصات: {deposit_count} (${total_to_exchanges_usd:,.2f})")
        print(f"   • سحوبات من المنصات: {withdrawal_count} (${total_from_exchanges_usd:,.2f})")
        print(f"   • صافي التدفق (للمنصات): ${net_flow_usd:,.2f}")

        signal = self._generate_enhanced_trading_signal(
            total_to_exchanges_usd, total_from_exchanges_usd, net_flow_usd,
            deposit_count, withdrawal_count, whale_transfers_count, network_stats
        )

        llm_summary = self._create_enhanced_llm_summary(signal, exchange_flows, network_stats, total_volume_usd, whale_transfers_count)

        return {
            'symbol': symbol, 'data_available': True, 'analysis_timestamp': datetime.now().isoformat(),
            'summary': {
                'total_transfers_analyzed': len(unique_transfers), 'whale_transfers_count': whale_transfers_count,
                'total_volume_usd': total_volume_usd, 'time_window_minutes': 120,
                'networks_analyzed': dict(network_stats)
            },
            'exchange_flows': {
                'to_exchanges_usd': total_to_exchanges_usd, 'from_exchanges_usd': total_from_exchanges_usd,
                'net_flow_usd': net_flow_usd, 'deposit_count': deposit_count, 'withdrawal_count': withdrawal_count,
                'network_breakdown': dict(exchange_flows['network_breakdown']),
                'top_deposits': sorted([{'value_usd': t['value_usd'], 'network': t['network'], 'to': t['to_exchange']} for t in exchange_flows['to_exchanges']], key=lambda x: x['value_usd'], reverse=True)[:3],
                'top_withdrawals': sorted([{'value_usd': t['value_usd'], 'network': t['network'], 'from': t['from_exchange']} for t in exchange_flows['from_exchanges']], key=lambda x: x['value_usd'], reverse=True)[:3]
            },
            'trading_signal': signal,
            'llm_friendly_summary': llm_summary
        }


    async def _get_accurate_token_value_optimized(self, raw_value, decimals, symbol):
        """حساب القيمة بالدولار باستخدام decimals مُمررة والسعر الحالي"""
        try:
            if decimals is None or decimals < 0:
                print(f"⚠️ قيمة decimals غير صالحة ({decimals}) لـ {symbol}. لا يمكن حساب القيمة.")
                return 0

            price = await self._get_token_price(symbol)
            if price is None or price <= 0:
                 return 0

            token_amount = raw_value / (10 ** decimals)
            value_usd = token_amount * price
            return value_usd

        except OverflowError:
             print(f"⚠️ خطأ OverflowError أثناء حساب قيمة {symbol} (Raw={raw_value}, Decimals={decimals})")
             return 0
        except Exception as e:
            print(f"❌ خطأ في _get_accurate_token_value_optimized لـ {symbol}: {e}")
            return 0

    async def _get_token_decimals(self, contract_address, network):
        """جلب عدد الكسور العشرية للعملة"""
        try:
            if isinstance(contract_address, dict):
                 addr_for_network = contract_address.get(network)
                 if not addr_for_network: return None
                 contract_address = addr_for_network

            if not isinstance(contract_address, str): return None

            cache_key = f"{contract_address.lower()}_{network}"
            if cache_key in self.token_decimals_cache:
                return self.token_decimals_cache[cache_key]

            if network in self.supported_networks and self.supported_networks[network]['type'] == 'evm':
                endpoints = self.supported_networks[network]['rpc_endpoints']
                for endpoint in endpoints:
                    try:
                        payload = {
                            "jsonrpc": "2.0", "method": "eth_call",
                            "params": [{"to": contract_address, "data": "0x313ce567"}, "latest"],
                            "id": int(time.time())
                        }
                        # ✅ الإصلاح: استخدام "self.rpc_semaphore"
                        async with self.rpc_semaphore:
                            response = await self.http_client.post(endpoint, json=payload, timeout=10.0)
                        
                        if response.status_code == 200:
                            result = response.json().get('result')
                            if result and result != '0x' and result.startswith('0x'):
                                try:
                                    decimals = int(result, 16)
                                    if isinstance(decimals, int) and decimals >= 0:
                                         self.token_decimals_cache[cache_key] = decimals
                                         print(f"   ℹ️ تم جلب Decimals لـ {contract_address[:10]}... على {network}: {decimals}")
                                         return decimals
                                except ValueError:
                                     print(f"     ⚠️ فشل تحويل نتيجة decimals من RPC ({result}) لـ {contract_address} على {network}")
                                     continue
                    except Exception as rpc_decimal_error:
                         if "Cannot reopen a client instance" in str(rpc_decimal_error):
                             print(f"     ❌ فشل فادح (Decimals): محاولة استخدام client مغلق. {rpc_decimal_error}")
                             return None
                         print(f"     ⚠️ خطأ RPC أثناء جلب decimals من {endpoint.split('//')[-1]}: {rpc_decimal_error}")
                         continue

            elif network == 'solana':
                 print(f"ℹ️ جلب decimals لـ Solana ({contract_address}) غير مدعوم حاليًا عبر RPC.")
                 estimated_decimals = 9
                 self.token_decimals_cache[cache_key] = estimated_decimals
                 print(f"   ⚠️ استخدام قيمة decimals تقديرية لـ Solana: {estimated_decimals}")
                 return estimated_decimals

            print(f"❌ فشل جلب الكسور العشرية للعقد {contract_address} على شبكة {network} من جميع المصادر.")
            return None

        except Exception as e:
            print(f"❌ فشل عام في جلب الكسور العشرية للعقد {contract_address} على شبكة {network}: {e}")
            return None


    async def _get_token_price(self, symbol):
        """جلب سعر العملة من CoinGecko مع fallbacks"""
        try:
            base_symbol = symbol.split('/')[0].upper()
            cache_entry = self.token_price_cache.get(base_symbol)
            if cache_entry and time.time() - cache_entry.get('timestamp', 0) < 300:
                return cache_entry['price']

            price = await self._get_token_price_from_coingecko(symbol)
            if price is not None and price > 0:
                self.token_price_cache[base_symbol] = {'price': price, 'timestamp': time.time()}
                return price

            price = await self._get_token_price_from_kucoin(symbol)
            if price is not None and price > 0:
                self.token_price_cache[base_symbol] = {'price': price, 'timestamp': time.time()}
                return price

            print(f"❌ فشل جميع محاولات جلب سعر العملة {symbol}")
            self.token_price_cache[base_symbol] = {'price': 0, 'timestamp': time.time()}
            return 0

        except Exception as e:
            print(f"❌ فشل عام في جلب سعر العملة {symbol}: {e}")
            return 0


    async def _get_token_price_from_coingecko(self, symbol):
        """جلب سعر العملة من CoinGecko مع التعامل مع rate limiting"""
        try:
            symbol_mapping = {
                'BTC': 'bitcoin', 'ETH': 'ethereum', 'BNB': 'binancecoin', 'ADA': 'cardano',
                'DOT': 'polkadot', 'XRP': 'ripple', 'DOGE': 'dogecoin', 'BCH': 'bitcoin-cash',
                'LTC': 'litecoin', 'XLM': 'stellar', 'TRX': 'tron', 'EOS': 'eos', 'XMR': 'monero',
                'SOL': 'solana', 'MATIC': 'matic-network', 'AVAX': 'avalanche-2', 'LINK': 'chainlink',
                'ATOM': 'cosmos', 'UNI': 'uniswap', 'AAVE': 'aave', 'KCS': 'kucoin-shares',
                'MAVIA': 'heroes-of-mavia', 'COMMON': 'commonwealth', 'WLFI': 'wolfi',
                'PINGPONG': 'pingpong', 'YB': 'yourbusd', 'REACT': 'react', 'XMN': 'xmine',
                'ANOME': 'anome', 'ZEN': 'zencash', 'AKT': 'akash-network', 'UB': 'unibit', 'WLD': 'worldcoin'
            }

            base_symbol = symbol.split('/')[0].upper()
            coingecko_id = symbol_mapping.get(base_symbol)
            if not coingecko_id:
                 coingecko_id = base_symbol.lower()
                 print(f"ℹ️ رمز {base_symbol} غير موجود في symbol_mapping، استخدام {coingecko_id} لـ CoinGecko.")


            url = f"https://api.coingecko.com/api/v3/simple/price?ids={coingecko_id}&vs_currencies=usd"
            headers = {'User-Agent': 'Mozilla/5.0', 'Accept': 'application/json'}

            max_retries = 2
            for attempt in range(max_retries):
                async with self.coingecko_semaphore:
                    # ✅ الإصلاح: استخدام "self.http_client"
                    try:
                        response = await self.http_client.get(url, headers=headers, timeout=10.0) # استخدام headers محلي
                        if response.status_code == 429:
                            wait_time = 3 * (attempt + 1)
                            print(f"⏰ Rate limit من CoinGecko لـ {symbol} (محاولة {attempt + 1}) - الانتظار {wait_time}s")
                            await asyncio.sleep(wait_time)
                            continue

                        response.raise_for_status()
                        data = response.json()
                        price_data = data.get(coingecko_id)
                        if price_data and 'usd' in price_data:
                            price = price_data['usd']
                            if isinstance(price, (int, float)) and price > 0:
                                 return price
                            else:
                                 print(f"⚠️ استجابة CoinGecko تحتوي على سعر غير صالح لـ {symbol}: {price}")
                                 return 0
                        else:
                             if attempt == 0:
                                  print(f"⚠️ لم يتم العثور على سعر لـ {coingecko_id} في CoinGecko، محاولة البحث عن المعرف...")
                                  await asyncio.sleep(1.1)
                                  # ✅ الإصلاح: استخدام "self.http_client" للبحث
                                  found_id = await self._find_coingecko_id_via_search(base_symbol, self.http_client)
                                  if found_id and found_id != coingecko_id:
                                       print(f"   🔄 تم العثور على معرف بديل: {found_id}. إعادة المحاولة...")
                                       coingecko_id = found_id
                                       url = f"https://api.coingecko.com/api/v3/simple/price?ids={coingecko_id}&vs_currencies=usd"
                                       continue
                                  else:
                                       print(f"   ❌ لم يتم العثور على معرف بديل لـ {base_symbol}.")
                                       return 0
                             else:
                                  print(f"⚠️ لم يتم العثور على سعر لـ {coingecko_id} في استجابة CoinGecko بعد البحث.")
                                  return 0
                    
                    except Exception as inner_e:
                        if "Cannot reopen a client instance" in str(inner_e):
                             print(f"     ❌ فشل فادح (CoinGecko): محاولة استخدام client مغلق. {inner_e}")
                             return 0 # لا يمكن المتابعة
                        # التعامل مع الأخطاء الأخرى
                        if isinstance(inner_e, httpx.HTTPStatusError):
                            print(f"❌ خطأ HTTP من CoinGecko لـ {symbol}: {inner_e.response.status_code}")
                        elif isinstance(inner_e, httpx.RequestError):
                            print(f"❌ خطأ اتصال بـ CoinGecko لـ {symbol}: {inner_e}")
                        else:
                             print(f"❌ خطأ غير متوقع أثناء جلب السعر من CoinGecko لـ {symbol}: {inner_e}")
                        return 0 # فشل في هذه المحاولة

            print(f"❌ فشل جلب السعر من CoinGecko لـ {symbol} بعد {max_retries} محاولات.")
            return 0

        except Exception as e:
            print(f"❌ فشل عام في _get_token_price_from_coingecko لـ {symbol}: {e}")
            return 0

    async def _find_coingecko_id_via_search(self, symbol, client: httpx.AsyncClient):
        """دالة مساعدة للبحث عن معرف CoinGecko"""
        try:
            search_url = f"https://api.coingecko.com/api/v3/search?query={symbol}"
            # ✅ الإصلاح: استخدام "self.coingecko_semaphore"
            async with self.coingecko_semaphore:
                response = await client.get(search_url, timeout=10.0)
            response.raise_for_status()
            data = response.json()
            coins = data.get('coins', [])
            if coins:
                 search_symbol_lower = symbol.lower()
                 for coin in coins:
                      if coin.get('symbol', '').lower() == search_symbol_lower:
                           return coin.get('id')
                 return coins[0].get('id')
            return None
        except Exception as e:
             print(f"   ⚠️ خطأ أثناء البحث عن معرف CoinGecko لـ {symbol}: {e}")
             return None


    async def _get_token_price_from_kucoin(self, symbol):
        """جلب سعر العملة من KuCoin كبديل (بشكل غير متزامن)"""
        exchange = None
        try:
            exchange = ccxt.kucoin({'enableRateLimit': True})
            markets = await exchange.load_markets()
            if symbol not in markets:
                 print(f"⚠️ الرمز {symbol} غير موجود في أسواق KuCoin.")
                 await exchange.close()
                 return 0

            ticker = await exchange.fetch_ticker(symbol)
            price = ticker.get('last')

            if price is not None:
                try:
                     price_float = float(price)
                     if price_float > 0:
                         return price_float
                     else:
                          print(f"⚠️ KuCoin أعاد سعراً غير موجب لـ {symbol}: {price}")
                          return 0
                except (ValueError, TypeError):
                     print(f"⚠️ KuCoin أعاد سعراً غير رقمي لـ {symbol}: {price}")
                     return 0
            else:
                print(f"⚠️ KuCoin لم يُعد مفتاح 'last' في ticker لـ {symbol}")
                return 0

        except ccxt.NetworkError as e:
             print(f"❌ خطأ شبكة أثناء جلب السعر من KuCoin لـ {symbol}: {e}")
             return 0
        except ccxt.ExchangeError as e:
             print(f"❌ خطأ منصة KuCoin ({type(e).__name__}) لـ {symbol}: {e}")
             return 0
        except Exception as e:
            print(f"❌ خطأ غير متوقع أثناء جلب السعر من KuCoin لـ {symbol}: {e}")
            return 0
        finally:
            if exchange:
                try:
                    await exchange.close()
                except Exception:
                    pass


    def _generate_enhanced_trading_signal(self, to_exchanges_usd, from_exchanges_usd, net_flow_usd, deposit_count, withdrawal_count, whale_transfers_count, network_stats):
        """توليد إشارة تداول محسنة بناء على تحركات الحيتان"""
        network_strength = min(len(network_stats) / 3.0, 1.0)
        abs_net_flow = abs(net_flow_usd)
        
        # ✅ الإصلاح: استخدام أسماء الباراميترات الصحيحة
        total_flow_volume = to_exchanges_usd + from_exchanges_usd

        confidence_multiplier = min( (abs_net_flow / 500000.0) + (whale_transfers_count / 5.0) , 1.5)
        base_confidence = 0.5 + (network_strength * 0.1)

        action = 'HOLD'
        reason = f'نشاط حيتان عبر {len(network_stats)} شبكة: {whale_transfers_count} تحويلة كبيرة بقيمة إجمالية ${total_flow_volume:,.0f}. صافي التدفق ${net_flow_usd:,.0f}'
        critical_alert = False

        if net_flow_usd > 500000 and deposit_count >= 2:
            action = 'STRONG_SELL'
            confidence = min(0.7 + (base_confidence * confidence_multiplier * 0.3), 0.95)
            reason = f'ضغط بيعي قوي عبر {len(network_stats)} شبكة: ${net_flow_usd:,.0f} إيداع للمنصات ({deposit_count} تحويلة).'
            critical_alert = abs_net_flow > 1000000
        elif net_flow_usd > 150000 and (deposit_count >= 1 or whale_transfers_count > 0):
            action = 'SELL'
            confidence = min(0.6 + (base_confidence * confidence_multiplier * 0.2), 0.85)
            reason = f'ضغط بيعي محتمل عبر {len(network_stats)} شبكة: ${net_flow_usd:,.0f} إيداع للمنصات.'
            critical_alert = abs_net_flow > 750000
        elif net_flow_usd < -500000 and withdrawal_count >= 2:
            action = 'STRONG_BUY'
            confidence = min(0.7 + (base_confidence * confidence_multiplier * 0.3), 0.95)
            reason = f'تراكم شرائي قوي عبر {len(network_stats)} شبكة: ${abs_net_flow:,.0f} سحب من المنصات ({withdrawal_count} تحويلة).'
            critical_alert = abs_net_flow > 1000000
        elif net_flow_usd < -150000 and (withdrawal_count >= 1 or whale_transfers_count > 0):
            action = 'BUY'
            confidence = min(0.6 + (base_confidence * confidence_multiplier * 0.2), 0.85)
            reason = f'تراكم شرائي محتمل عبر {len(network_stats)} شبكة: ${abs_net_flow:,.0f} سحب من المنصات.'
            critical_alert = abs_net_flow > 750000
        else:
            if whale_transfers_count > 0 and abs_net_flow < 100000:
                 confidence = min(base_confidence * 1.1, 0.6)
                 reason += " (صافي التدفق منخفض نسبياً)"
            elif whale_transfers_count > 0 and abs_net_flow < 50000:
                 confidence = max(0.4, base_confidence * 0.9)
                 reason += " (صافي التدفق شبه متعادل)"
            elif whale_transfers_count == 0:
                 confidence = 0.3
                 reason = 'لا توجد تحركات حيتان كبيرة في الساعات الأخيرة'
            else:
                 confidence = base_confidence

        final_confidence = max(0.3, min(confidence, 0.95))

        return {'action': action, 'confidence': final_confidence, 'reason': reason, 'critical_alert': critical_alert}

    def _create_enhanced_llm_summary(self, signal, exchange_flows, network_stats, total_volume, whale_count):
        """إنشاء ملخص محسن للنموذج الضخم (مع إصلاح KeyError)"""
        deposit_count_val = exchange_flows.get('deposit_count', 0)
        withdrawal_count_val = exchange_flows.get('withdrawal_count', 0)
        exchange_involvement_str = f"{deposit_count_val + withdrawal_count_val} معاملة مع المنصات"

        return {
            'whale_activity_summary': signal['reason'],
            'recommended_action': signal['action'],
            'confidence': signal['confidence'],
            'key_metrics': {
                'total_whale_transfers': whale_count,
                'total_volume_analyzed': f"${total_volume:,.0f}",
                'net_flow_direction': 'TO_EXCHANGES' if signal['action'] in ['SELL', 'STRONG_SELL'] else 'FROM_EXCHANGES' if signal['action'] in ['BUY', 'STRONG_BUY'] else 'BALANCED',
                'whale_movement_impact': 'HIGH' if signal['confidence'] > 0.8 else 'MEDIUM' if signal['confidence'] > 0.6 else 'LOW',
                'exchange_involvement': exchange_involvement_str,
                'network_coverage': f"{len(network_stats)} شبكات",
                'data_quality': 'REAL_TIME'
            }
        }


    async def _find_contract_address_enhanced(self, symbol):
        """بحث متقدم عن عقد العملة مع تحديد الشبكة المناسبة"""
        base_symbol = symbol.split("/")[0].upper() if '/' in symbol else symbol.upper()
        symbol_lower = base_symbol.lower()

        print(f"🔍 البحث عن عقد للعملة: {symbol}")

        if symbol_lower in self.contracts_db:
            contract_info = self.contracts_db[symbol_lower]
            if isinstance(contract_info, str):
                network = self._detect_network_from_address(contract_info)
                contract_info = {'address': contract_info, 'network': network}
                self.contracts_db[symbol_lower] = contract_info
            if 'address' in contract_info and 'network' in contract_info:
                 print(f"   ✅ وجد في قاعدة البيانات المحلية: {contract_info}")
                 return contract_info
            else:
                 print(f"   ⚠️ بيانات العقد غير مكتملة في القاعدة المحلية لـ {symbol}: {contract_info}")

        print(f"   🔍 البحث في CoinGecko عن {base_symbol}...")
        coingecko_result = await self._find_contract_via_coingecko(base_symbol)

        if coingecko_result:
            address, network = coingecko_result
            contract_info = {'address': address, 'network': network}
            self.contracts_db[symbol_lower] = contract_info
            print(f"   ✅ تم العثور على عقد {symbol} عبر CoinGecko على شبكة {network}: {address}")

            if self.r2_service:
                await self._save_contracts_to_r2()

            return contract_info

        print(f"   ❌ فشل العثور على عقد لـ {symbol} في جميع المصادر")
        return None


    async def _find_contract_via_coingecko(self, symbol):
        """البحث عن عقد العملة عبر CoinGecko مع التعامل مع الأخطاء ومعدل الطلبات"""
        try:
            search_url = f"https://api.coingecko.com/api/v3/search?query={symbol}"
            headers = {'User-Agent': 'Mozilla/5.0', 'Accept': 'application/json'}

            max_retries = 2
            for attempt in range(max_retries):
                async with self.coingecko_semaphore:
                    # ✅ الإصلاح: استخدام "self.http_client"
                    try:
                        print(f"     🔍 CoinGecko Search API Call (Attempt {attempt + 1}) for {symbol}")
                        response = await self.http_client.get(search_url, headers=headers, timeout=15.0)

                        if response.status_code == 429:
                            wait_time = 3 * (attempt + 1)
                            print(f"     ⏰ Rate limit (Search) for {symbol} - Waiting {wait_time}s")
                            await asyncio.sleep(wait_time)
                            continue

                        response.raise_for_status()
                        data = response.json()
                        coins = data.get('coins', [])

                        if not coins:
                            print(f"     ❌ No matching coins found for {symbol} on CoinGecko")
                            return None

                        print(f"     📊 Found {len(coins)} potential matches for {symbol}")

                        best_coin = None
                        search_symbol_lower = symbol.lower()
                        for coin in coins:
                            coin_symbol = coin.get('symbol', '').lower()
                            coin_name = coin.get('name', '').lower()
                            if coin_symbol == search_symbol_lower:
                                best_coin = coin; print(f"       ✅ Exact symbol match: {coin.get('name')}"); break
                            if not best_coin and coin_name == search_symbol_lower:
                                best_coin = coin; print(f"       ✅ Exact name match: {coin.get('name')}")
                        if not best_coin:
                            best_coin = coins[0]; print(f"       ⚠️ Using first result: {best_coin.get('name')}")

                        coin_id = best_coin.get('id')
                        if not coin_id: print(f"       ❌ No ID found"); return None

                        print(f"     🔍 Fetching details for CoinGecko ID: {coin_id}")
                        detail_url = f"https://api.coingecko.com/api/v3/coins/{coin_id}"
                        await asyncio.sleep(1.1)
                        detail_response = await self.http_client.get(detail_url, headers=headers, timeout=15.0)

                        if detail_response.status_code == 429:
                            wait_time = 5 * (attempt + 1)
                            print(f"     ⏰ Rate limit (Details) for {coin_id} - Waiting {wait_time}s")
                            await asyncio.sleep(wait_time)
                            detail_response = await self.http_client.get(detail_url, headers=headers, timeout=15.0)

                        detail_response.raise_for_status()
                        detail_data = detail_response.json()
                        platforms = detail_data.get('platforms', {})
                        if not platforms: print(f"       ❌ No platform data found"); return None

                        network_priority = ['ethereum', 'binance-smart-chain', 'polygon-pos', 'arbitrum-one', 'optimistic-ethereum', 'avalanche', 'fantom', 'solana']
                        network_map = {'ethereum': 'ethereum', 'binance-smart-chain': 'bsc', 'polygon-pos': 'polygon', 'arbitrum-one': 'arbitrum', 'optimistic-ethereum': 'optimism', 'avalanche': 'avalanche', 'fantom': 'fantom', 'solana': 'solana'}

                        for platform_cg in network_priority:
                            address = platforms.get(platform_cg)
                            if address and isinstance(address, str) and address.strip():
                                network = network_map.get(platform_cg)
                                if network:
                                     print(f"       ✅ Found contract on {network}: {address}")
                                     return address, network

                        print(f"       ❌ No contract found on supported platforms for {coin_id}")
                        return None

                    except Exception as inner_e:
                        if "Cannot reopen a client instance" in str(inner_e):
                             print(f"     ❌ فشل فادح (CoinGecko): محاولة استخدام client مغلق. {inner_e}")
                             return None
                        if isinstance(inner_e, httpx.HTTPStatusError):
                            print(f"     ❌ HTTP Error from CoinGecko ({inner_e.request.url}): {inner_e.response.status_code}")
                        elif isinstance(inner_e, httpx.RequestError):
                            print(f"     ❌ Connection Error with CoinGecko ({inner_e.request.url}): {inner_e}")
                        else:
                            print(f"     ❌ Unexpected error during CoinGecko API call (Attempt {attempt + 1}): {inner_e}")
                        
                        if attempt < max_retries - 1: await asyncio.sleep(1); continue
                        else: return None

            print(f"   ❌ Failed to get contract from CoinGecko for {symbol} after {max_retries} attempts.")
            return None

        except Exception as e:
            print(f"❌ General failure in _find_contract_via_coingecko for {symbol}: {e}")
            traceback.print_exc()
            return None


    def _is_exchange_address(self, address):
        """التحقق إذا كان العنوان ينتمي إلى منصة"""
        try:
            if not isinstance(address, str): return False
            return address.lower() in self.address_categories['exchange']
        except Exception: return False

    def _classify_address(self, address):
        """تصنيف العنوان إلى نوع محدد"""
        try:
            if not isinstance(address, str): return 'unknown'
            return self.address_labels.get(address.lower(), 'unknown')
        except Exception: return 'unknown'

    def _create_no_contract_response(self, symbol):
        """إنشاء رد عند عدم العثور على عقد"""
        return {
            'symbol': symbol, 'data_available': False, 'error': 'NO_CONTRACT_FOUND',
            'trading_signal': {'action': 'HOLD', 'confidence': 0.3, 'reason': 'لم يتم العثور على عقد العملة - لا توجد بيانات حيتان'},
            'llm_friendly_summary': {'whale_activity_summary': 'لا توجد بيانات عن تحركات الحيتان - لم يتم العثور على عقد العملة', 'recommended_action': 'HOLD', 'confidence': 0.3, 'key_metrics': {'whale_movement_impact': 'NO_DATA'}}
        }

    def _create_no_transfers_response(self, symbol):
        """إنشاء رد عند عدم العثور على تحويلات"""
        return {
            'symbol': symbol, 'data_available': False, 'error': 'NO_TRANSFERS_FOUND',
            'trading_signal': {'action': 'HOLD', 'confidence': 0.3, 'reason': 'لا توجد تحويلات للعملة - لا توجد بيانات حيتان'},
            'llm_friendly_summary': {'whale_activity_summary': 'لا توجد تحويلات حديثة للعملة - لا توجد بيانات حيتان', 'recommended_action': 'HOLD', 'confidence': 0.3, 'key_metrics': {'whale_movement_impact': 'NO_DATA'}}
        }

    def _create_no_recent_activity_response(self, symbol):
         """إنشاء رد عند عدم وجود نشاط حديث"""
         return {
            'symbol': symbol, 'data_available': False, 'error': 'NO_RECENT_ACTIVITY',
            'trading_signal': {'action': 'HOLD', 'confidence': 0.3, 'reason': 'لا توجد تحويلات حديثة للعملة - لا توجد بيانات حيتان'},
            'llm_friendly_summary': {'whale_activity_summary': 'لا توجد تحويلات حيتان حديثة في آخر 120 دقيقة - لا توجد بيانات حيتان', 'recommended_action': 'HOLD', 'confidence': 0.3, 'key_metrics': {'whale_movement_impact': 'NO_DATA'}}
        }


    def _create_error_response(self, symbol, error_msg):
        """إنشاء رد في حالة حدوث خطأ"""
        return {
            'symbol': symbol, 'data_available': False, 'error': f'ANALYSIS_ERROR: {error_msg}',
            'trading_signal': {'action': 'HOLD', 'confidence': 0.3, 'reason': f'خطأ في تحليل الحيتان: {error_msg} - لا توجد بيانات حيتان'},
            'llm_friendly_summary': {'whale_activity_summary': f'فشل في تحليل تحركات الحيتان: {error_msg} - لا توجد بيانات حيتان', 'recommended_action': 'HOLD', 'confidence': 0.3, 'key_metrics': {'whale_movement_impact': 'ERROR'}}
        }

    async def _save_contracts_to_r2(self):
        """حفظ قاعدة البيانات العقود إلى R2"""
        if not self.r2_service: return
        try:
            key = "contracts.json"
            contracts_to_save = {}
            for symbol, data in self.contracts_db.items():
                if isinstance(data, dict) and 'address' in data and 'network' in data:
                    contracts_to_save[symbol] = data
                elif isinstance(data, str):
                     contracts_to_save[symbol] = {'address': data, 'network': self._detect_network_from_address(data)}

            if not contracts_to_save:
                 print("⚠️ لا توجد بيانات عقود صالحة للحفظ في R2.")
                 return

            data_json = json.dumps(contracts_to_save, indent=2).encode('utf-8')
            self.r2_service.s3_client.put_object(
                Bucket="trading", Key=key, Body=data_json, ContentType="application/json"
            )
            print(f"✅ تم حفظ قاعدة بيانات العقود ({len(contracts_to_save)} إدخال) إلى R2")
        except Exception as e:
            print(f"❌ فشل حفظ قاعدة البيانات العقود: {e}")


    async def generate_whale_trading_signal(self, symbol, whale_data, market_context):
        """توليد إشارة تداول بناءً على بيانات الحيتان"""
        try:
            if not whale_data or not whale_data.get('data_available', False):
                return {
                    'action': 'HOLD', 'confidence': 0.3,
                    'reason': 'لا توجد بيانات كافية عن نشاط الحيتان',
                    'source': 'whale_analysis', 'data_available': False
                }

            whale_signal = whale_data.get('trading_signal', {})
            analysis_details = whale_data

            return {
                'action': whale_signal.get('action', 'HOLD'),
                'confidence': whale_signal.get('confidence', 0.3),
                'reason': whale_signal.get('reason', 'تحليل الحيتان غير متوفر'),
                'source': 'whale_analysis',
                'critical_alert': whale_signal.get('critical_alert', False),
                'data_available': True,
                'whale_analysis_details': analysis_details
            }

        except Exception as e:
            print(f"❌ خطأ في توليد إشارة تداول الحيتان لـ {symbol}: {e}")
            return {
                'action': 'HOLD', 'confidence': 0.3,
                'reason': f'خطأ في تحليل الحيتان: {str(e)} - لا توجد بيانات حيتان',
                'source': 'whale_analysis', 'data_available': False
            }


    async def cleanup(self):
        """تنظيف الموارد عند الإغلاق"""
        if hasattr(self, 'http_client') and self.http_client and not self.http_client.is_closed:
            try:
                await self.http_client.aclose()
                print("✅ تم إغلاق اتصال HTTP client لمراقب الحيتان.")
            except Exception as e:
                 print(f"⚠️ خطأ أثناء إغلاق HTTP client لمراقب الحيتان: {e}")


print("✅ EnhancedWhaleMonitor loaded - RPC Rate Limiting (Semaphore) & Updated Endpoints")