File size: 31,640 Bytes
a07803d
 
 
 
726d730
a07803d
 
 
 
aef1ab8
a07803d
 
 
 
 
 
 
 
 
 
 
726d730
a07803d
 
 
 
 
726d730
 
 
 
 
 
a07803d
 
 
 
09db822
 
1a7cde6
 
 
09db822
a07803d
1a7cde6
 
09db822
1a7cde6
 
 
 
 
 
 
 
 
a07803d
 
1a7cde6
794b24d
a07803d
09db822
 
1a7cde6
09db822
1a7cde6
09db822
8eaf509
09db822
1a7cde6
 
 
09db822
1a7cde6
09db822
1a7cde6
 
8eaf509
09db822
1a7cde6
 
8eaf509
1a7cde6
8eaf509
1a7cde6
 
8eaf509
1a7cde6
 
 
 
 
09db822
a07803d
 
794b24d
a07803d
 
9d16c90
1a7cde6
 
a07803d
1a7cde6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a07803d
 
 
794b24d
a07803d
1a7cde6
794b24d
 
 
 
 
 
 
 
a07803d
794b24d
 
9d16c90
 
 
 
 
 
 
 
 
 
794b24d
a07803d
 
8eaf509
9d16c90
b0b31a7
 
 
8eaf509
 
726d730
9d16c90
794b24d
1a7cde6
794b24d
 
 
 
1a7cde6
b0b31a7
794b24d
726d730
a07803d
9d16c90
726d730
794b24d
726d730
 
9d16c90
1a7cde6
794b24d
726d730
a07803d
726d730
8eaf509
a07803d
8eaf509
9d16c90
09db822
 
1a7cde6
09db822
1a7cde6
 
 
 
09db822
1a7cde6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
09db822
a07803d
 
1a7cde6
 
 
 
 
a07803d
8eaf509
1a7cde6
 
 
8eaf509
1a7cde6
8eaf509
 
 
 
 
 
1a7cde6
 
63e20c4
1a7cde6
 
 
 
 
 
 
8eaf509
1a7cde6
 
 
 
 
 
 
 
 
 
 
a07803d
 
 
 
1a7cde6
a07803d
 
1a7cde6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a07803d
 
 
1a7cde6
a07803d
 
1a7cde6
a07803d
 
 
 
 
1a7cde6
 
a07803d
 
 
1a7cde6
d593db7
 
1a7cde6
a07803d
1a7cde6
a07803d
1a7cde6
 
 
 
 
 
 
 
 
 
a07803d
 
1a7cde6
 
09db822
1a7cde6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
09db822
1a7cde6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
09db822
1a7cde6
09db822
 
1a7cde6
 
 
 
63e20c4
1a7cde6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a07803d
1a7cde6
 
 
 
 
 
0b33a03
a07803d
1a7cde6
a07803d
1a7cde6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9d16c90
a07803d
1a7cde6
8eaf509
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1a7cde6
 
 
 
 
 
 
 
 
 
 
63e20c4
1a7cde6
 
fb65c42
d593db7
1a7cde6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d593db7
fb65c42
d593db7
1a7cde6
 
 
d593db7
1a7cde6
 
 
fb65c42
1a7cde6
 
 
 
 
 
 
 
 
8eaf509
1a7cde6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8eaf509
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9734477
8eaf509
 
1a7cde6
 
 
 
 
 
 
 
fb65c42
794b24d
 
 
 
 
a07803d
8eaf509
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
import os
import json
import time
import requests
from anthropic import Anthropic
from openai import OpenAI
import gradio as gr
import pandas as pd
from huggingface_hub import CommitScheduler
from datetime import datetime, timedelta
import uuid
from user_agents import parse as parse_ua
import schedule
import threading

# --- Konfiguration ---
CHARGENODE_URL = "https://www.chargenode.eu"

# Kontrollera om vi kör i Hugging Face-miljön
IS_HUGGINGFACE = os.environ.get("SPACE_ID") is not None

# OpenAI-klient behålls för bakåtkompatibilitet
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
if not OPENAI_API_KEY:
    raise ValueError("OPENAI_API_KEY saknas")
client = OpenAI(api_key=OPENAI_API_KEY)

# Lägg till Anthropic API-nyckel och klient
ANTHROPIC_API_KEY = os.environ.get("ANTHROPIC_API_KEY")
if not ANTHROPIC_API_KEY:
    raise ValueError("ANTHROPIC_API_KEY saknas")
anthropic_client = Anthropic(api_key=ANTHROPIC_API_KEY)

log_folder = "logs"
os.makedirs(log_folder, exist_ok=True)
log_file_path = os.path.join(log_folder, "conversation_log_v2.txt")

# Skapa en tom loggfil om den inte finns
if not os.path.exists(log_file_path):
    with open(log_file_path, "w", encoding="utf-8") as f:
        f.write("")  # Skapa en tom fil
    print(f"Skapade tom loggfil: {log_file_path}")

hf_token = os.environ.get("HF_TOKEN")
if not hf_token:
    raise ValueError("HF_TOKEN saknas")

# Minsta möjliga konfiguration som bör fungera
scheduler = CommitScheduler(
    repo_id="ChargeNodeEurope/logfiles",
    repo_type="dataset",
    folder_path=log_folder,
    path_in_repo="logs_v2",
    every=300,  # Vänta 5 minuter
    token=hf_token
)

# --- Globala variabler ---
last_log = None  # Sparar loggdata från senaste svar för feedback
full_context = None  # Används för att spara hela kontexten

# --- Förbättrad loggfunktion ---
def safe_append_to_log(log_entry):
    """Säker metod för att lägga till loggdata utan att förlora historisk information."""
    try:
        # Öppna filen i append-läge
        with open(log_file_path, "a", encoding="utf-8") as log_file:
            log_json = json.dumps(log_entry)
            log_file.write(log_json + "\n")
            log_file.flush()  # Säkerställ att data skrivs till disk omedelbart
            
        print(f"Loggpost tillagd: {log_entry.get('timestamp', 'okänd tid')}")
        return True
        
    except Exception as e:
        print(f"Fel vid loggning: {e}")
        
        # Försök skapa mappen om den inte finns
        try:
            os.makedirs(os.path.dirname(log_file_path), exist_ok=True)
            
            # Försök igen
            with open(log_file_path, "a", encoding="utf-8") as log_file:
                log_json = json.dumps(log_entry)
                log_file.write(log_json + "\n")
                
            print("Loggpost tillagd efter återhämtning")
            return True
            
        except Exception as retry_error:
            print(f"Kritiskt fel vid loggning: {retry_error}")
            return False

# --- Laddar textkällor ---
def load_local_files():
    """Laddar alla lokala filer och returnerar som en sammanhängande text."""
    uploaded_text = ""
    allowed = [".txt", ".docx", ".pdf", ".csv", ".xls", ".xlsx"]
    excluded = ["requirements.txt", "app.py", "conversation_log.txt", "conversation_log_v2.txt", "secrets", "prompt.txt"]
    for file in os.listdir("."):
        if file.lower().endswith(tuple(allowed)) and file not in excluded:
            try:
                if file.endswith(".txt"):
                    with open(file, "r", encoding="utf-8") as f:
                        content = f.read()
                elif file.endswith(".docx"):
                    from docx import Document  # Import sker vid behov
                    content = "\n".join([p.text for p in Document(file).paragraphs])
                elif file.endswith(".pdf"):
                    import PyPDF2  # Import sker vid behov
                    with open(file, "rb") as f:
                        reader = PyPDF2.PdfReader(f)
                        content = "\n".join([p.extract_text() or "" for p in reader.pages])
                elif file.endswith(".csv"):
                    content = pd.read_csv(file).to_string()
                elif file.endswith((".xls", ".xlsx")):
                    if file == "FAQ stadat.xlsx":
                        df = pd.read_excel(file)
                        rows = []
                        for index, row in df.iterrows():
                            rows.append(f"Fråga: {row['Fråga']}\nSvar: {row['Svar']}")
                        content = "\n\n".join(rows)
                    else:
                        content = pd.read_excel(file).to_string()
                uploaded_text += f"\n\nFIL: {file}\n{content}"
            except Exception as e:
                print(f"Fel vid läsning av {file}: {str(e)}")
    return uploaded_text.strip()

def load_prompt():
    """Läser in system-prompts från prompt.txt med bättre felhantering."""
    try:
        with open("prompt.txt", "r", encoding="utf-8") as f:
            prompt_content = f.read().strip()
            if not prompt_content:
                print("Varning: prompt.txt är tom, använder standardprompt")
                return "Du är ChargeNode's AI-assistent. Svara på frågor om ChargeNode's produkter och tjänster baserat på den tillhandahållna informationen."
            return prompt_content
    except FileNotFoundError:
        print("Varning: prompt.txt hittades inte, använder standardprompt")
        return "Du är ChargeNode's AI-assistent. Svara på frågor om ChargeNode's produkter och tjänster baserat på den tillhandahållna informationen."
    except Exception as e:
        print(f"Fel vid inläsning av prompt.txt: {e}, använder standardprompt")
        return "Du är ChargeNode's AI-assistent. Svara på frågor om ChargeNode's produkter och tjänster baserat på den tillhandahållna informationen."

def load_full_context():
    """Laddar hela kontexten en gång och cachar resultatet."""
    global full_context
    if full_context is None:
        print("Laddar alla textfiler till fullständig kontext...")
        full_context = load_local_files()
        print(f"Laddade {len(full_context)} tecken till kontext")
    return full_context

# Ladda prompt template
prompt_template = load_prompt()

def generate_answer(query):
    """Genererar svar baserat på fråga med hela kontexten."""
    # Hämta hela kontexten
    context = load_full_context()
    
    if not context.strip():
        return "Jag hittar ingen relevant information i mina källor.\n\nDetta är ett AI genererat svar."
    
    # System-prompts och användarfråga
    system_prompt = prompt_template
    
    # Skapa ett renare användarmeddelande
    user_message = f"""Jag har en fråga om ChargeNode.

Hela dataunderlaget du kan använda för att svara:
{context}

Min fråga är: {query}"""
    
    try:
        # Använd Claude Haiku med hela kontexten
        response = anthropic_client.messages.create(
            model="claude-3-haiku-20240307",
            max_tokens=500,
            temperature=0.2,
            system=system_prompt,
            messages=[
                {"role": "user", "content": user_message}
            ]
        )
        answer = response.content[0].text
        return answer + "\n\nAI-genererat. Otillräcklig hjälp? Kontakta support@chargenode.eu eller 010-2051055"
    except Exception as e:
        return f"Tekniskt fel: {str(e)}\n\nAI-genererat. Kontakta support@chargenode.eu eller 010-2051055"

# --- Slack Integration ---
def send_to_slack(subject, content, color="#2a9d8f"):
    """Basfunktion för att skicka meddelanden till Slack."""
    webhook_url = os.environ.get("SLACK_WEBHOOK_URL")
    if not webhook_url:
        print("Slack webhook URL saknas")
        return False
    
    try:
        # Formatera meddelandet för Slack
        payload = {
            "blocks": [
                {
                    "type": "header",
                    "text": {
                        "type": "plain_text",
                        "text": subject
                    }
                },
                {
                    "type": "section",
                    "text": {
                        "type": "mrkdwn",
                        "text": content
                    }
                }
            ]
        }
        
        response = requests.post(
            webhook_url,
            json=payload,
            headers={"Content-Type": "application/json"}
        )
        
        if response.status_code == 200:
            print(f"Slack-meddelande skickat: {subject}")
            return True
        else:
            print(f"Slack-anrop misslyckades: {response.status_code}, {response.text}")
            return False
    except Exception as e:
        print(f"Fel vid sändning till Slack: {type(e).__name__}: {e}")
        return False

# --- Feedback & Like-funktion ---
def vote(data: gr.LikeData):
    """
    Hanterar feedback från Gradio's inbyggda like-funktion.
    data.liked är True om uppvote, annars False.
    data.value innehåller information om meddelandet.
    """
    feedback_type = "up" if data.liked else "down"
    global last_log
    log_entry = {
        "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        "feedback": feedback_type,
        "bot_reply": data.value if not isinstance(data.value, dict) else data.value.get("value")
    }
    # Om global logdata finns, lägg till ytterligare metadata.
    if last_log:
        log_entry.update({
            "session_id": last_log.get("session_id"),
            "user_message": last_log.get("user_message"),
        })
    
    # Använd den förbättrade loggfunktionen
    safe_append_to_log(log_entry)
    
    # Skicka feedback till Slack
    try:
        if feedback_type == "down":  # Skicka bara negativ feedback
            feedback_message = f"""
*⚠️ Negativ feedback registrerad*

*Fråga:* {last_log.get('user_message', 'Okänd fråga')}

*Svar:* {log_entry.get('bot_reply', 'Okänt svar')[:300]}{'...' if len(log_entry.get('bot_reply', '')) > 300 else ''}
"""
            # Skicka asynkront
            threading.Thread(
                target=lambda: send_to_slack("Negativ feedback", feedback_message, "#ff0000"),
                daemon=True
            ).start()
    except Exception as e:
        print(f"Kunde inte skicka feedback till Slack: {e}")
    
    return

# --- Rapportering ---
def read_logs():
    """Läs alla loggposter från loggfilen."""
    logs = []
    try:
        if os.path.exists(log_file_path):
            with open(log_file_path, "r", encoding="utf-8") as file:
                line_count = 0
                for line in file:
                    line_count += 1
                    try:
                        log_entry = json.loads(line.strip())
                        logs.append(log_entry)
                    except json.JSONDecodeError as e:
                        print(f"Varning: Kunde inte tolka rad {line_count}: {e}")
                        continue
            print(f"Läste {len(logs)} av {line_count} loggposter")
        else:
            print(f"Loggfil saknas: {log_file_path}")
    except Exception as e:
        print(f"Fel vid läsning av loggfil: {e}")
    return logs

def get_latest_conversations(logs, limit=50):
    """Hämta de senaste frågorna och svaren."""
    conversations = []
    for log in reversed(logs):
        if 'user_message' in log and 'bot_reply' in log:
            conversations.append({
                'user_message': log['user_message'],
                'bot_reply': log['bot_reply'],
                'timestamp': log.get('timestamp', '')
            })
            if len(conversations) >= limit:
                break
    return conversations

def get_feedback_stats(logs):
    """Sammanfatta feedback (tumme upp/ned)."""
    feedback_count = {"up": 0, "down": 0}
    negative_feedback_examples = []
    
    for log in logs:
        if 'feedback' in log:
            feedback = log.get('feedback')
            if feedback in feedback_count:
                feedback_count[feedback] += 1
                
            # Samla exempel på negativ feedback
            if feedback == "down" and 'user_message' in log and len(negative_feedback_examples) < 10:
                negative_feedback_examples.append({
                    'user_message': log.get('user_message', 'Okänd fråga'),
                    'bot_reply': log.get('bot_reply', 'Okänt svar')
                })
    
    return feedback_count, negative_feedback_examples

def generate_monthly_stats(days=30):
    """Genererar omfattande statistik över botanvändning för den senaste månaden."""
    print(f"Genererar statistik för de senaste {days} dagarna...")
    
    # Hämta loggar
    logs = read_logs()
    
    if not logs:
        return {"error": "Inga loggar hittades för den angivna perioden"}
    
    # Filtrera på datumintervall
    now = datetime.now()
    cutoff_date = now - timedelta(days=days)
    filtered_logs = []
    
    for log in logs:
        if 'timestamp' in log:
            try:
                log_date = datetime.strptime(log['timestamp'], "%Y-%m-%d %H:%M:%S")
                if log_date >= cutoff_date:
                    filtered_logs.append(log)
            except:
                pass  # Hoppa över poster med ogiltigt datum
    
    logs = filtered_logs
    
    # Basstatistik
    total_conversations = sum(1 for log in logs if 'user_message' in log)
    unique_sessions = len(set(log.get('session_id', 'unknown') for log in logs if 'session_id' in log))
    unique_users = len(set(log.get('user_id', 'unknown') for log in logs if 'user_id' in log))
    
    # Feedback-statistik
    feedback_logs = [log for log in logs if 'feedback' in log]
    positive_feedback = sum(1 for log in feedback_logs if log.get('feedback') == 'up')
    negative_feedback = sum(1 for log in feedback_logs if log.get('feedback') == 'down')
    feedback_ratio = (positive_feedback / len(feedback_logs) * 100) if feedback_logs else 0
    
    # Svarstidsstatistik
    response_times = [log.get('response_time', 0) for log in logs if 'response_time' in log]
    avg_response_time = sum(response_times) / len(response_times) if response_times else 0
    
    # Plattformsstatistik
    platforms = {}
    browsers = {}
    operating_systems = {}
    for log in logs:
        if 'platform' in log:
            platforms[log['platform']] = platforms.get(log['platform'], 0) + 1
        if 'browser' in log:
            browsers[log['browser']] = browsers.get(log['browser'], 0) + 1
        if 'os' in log:
            operating_systems[log['os']] = operating_systems.get(log['os'], 0) + 1
    
    # Skapa rapport
    report = {
        "period": f"Senaste {days} dagarna",
        "generated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        "basic_stats": {
            "total_conversations": total_conversations,
            "unique_sessions": unique_sessions,
            "unique_users": unique_users,
            "messages_per_user": round(total_conversations / unique_users, 2) if unique_users else 0
        },
        "feedback": {
            "positive": positive_feedback,
            "negative": negative_feedback,
            "ratio_percent": round(feedback_ratio, 1)
        },
        "performance": {
            "avg_response_time": round(avg_response_time, 2)
        },
        "platform_distribution": platforms,
        "browser_distribution": browsers,
        "os_distribution": operating_systems
    }
    
    return report

def simple_status_report():
    """Skickar en förenklad statusrapport till Slack."""
    print("Genererar statusrapport för Slack...")
    
    try:
        # Generera statistik
        stats = generate_monthly_stats(days=7)  # Senaste veckan
        
        # Skapa innehåll för Slack
        now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        subject = f"ChargeNode AI Bot - Status {now}"
        
        if 'error' in stats:
            content = f"*Fel vid generering av statistik:* {stats['error']}"
            return send_to_slack(subject, content, "#ff0000")
        
        # Formatera statistik
        basic = stats["basic_stats"]
        feedback = stats["feedback"]
        perf = stats["performance"]
        
        content = f"""
*ChargeNode AI Bot - Statusrapport {now}*

*Basstatistik* (senaste 7 dagarna)
- Totalt antal konversationer: {basic['total_conversations']}
- Unika sessioner: {basic['unique_sessions']}
- Unika användare: {basic['unique_users']}
- Genomsnittlig svarstid: {perf['avg_response_time']} sekunder

*Feedback*
- 👍 Tumme upp: {feedback['positive']}
- 👎 Tumme ned: {feedback['negative']}
- Nöjdhet: {feedback['ratio_percent']}%
"""
        
        # Lägg till de senaste konversationerna
        logs = read_logs()
        conversations = get_latest_conversations(logs, 3)
        
        if conversations:
            content += "\n*Senaste konversationer*\n"
            for conv in conversations:
                content += f"""
> *Tid:* {conv['timestamp']}
> *Fråga:* {conv['user_message'][:100]}{'...' if len(conv['user_message']) > 100 else ''}
> *Svar:* {conv['bot_reply'][:100]}{'...' if len(conv['bot_reply']) > 100 else ''}
"""
        
        # Skicka till Slack
        return send_to_slack(subject, content, "#2a9d8f")
        
    except Exception as e:
        print(f"Fel vid generering av statusrapport: {e}")
        
        # Skicka felmeddelande till Slack
        error_subject = f"ChargeNode AI Bot - Fel vid statusrapport"
        error_content = f"*Fel vid generering av statusrapport:* {str(e)}"
        return send_to_slack(error_subject, error_content, "#ff0000")

def send_support_to_slack(områdeskod, uttagsnummer, email, chat_history):
    """Skickar en supportförfrågan till Slack."""
    try:
        # Formatera chat-historiken
        chat_content = ""
        for msg in chat_history:
            if msg['role'] == 'user':
                chat_content += f">*Användare:* {msg['content']}\n\n"
            elif msg['role'] == 'assistant':
                chat_content += f">*Bot:* {msg['content'][:300]}{'...' if len(msg['content']) > 300 else ''}\n\n"
        
        # Skapa innehåll
        subject = f"Support förfrågan - {datetime.now().strftime('%Y-%m-%d %H:%M')}"
        
        content = f"""
*Användarinformation*
- *Områdeskod:* {områdeskod or 'Ej angiven'}
- *Uttagsnummer:* {uttagsnummer or 'Ej angiven'}
- *Email:* {email}
- *Tidpunkt:* {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

*Chatthistorik:*
{chat_content}
"""
        
        # Skicka till Slack
        return send_to_slack(subject, content, "#e76f51")
    except Exception as e:
        print(f"Fel vid sändning av support till Slack: {type(e).__name__}: {e}")
        return False

# --- Schemaläggning av rapporter ---
def run_scheduler():
    """Kör schemaläggaren i en separat tråd med förenklad statusrapportering."""
    # Använd den förenklade funktionen för rapportering
    schedule.every().day.at("08:00").do(simple_status_report)
    schedule.every().day.at("12:00").do(simple_status_report)
    schedule.every().day.at("17:00").do(simple_status_report)
    
    # Veckorapport på måndagar
    schedule.every().monday.at("09:00").do(lambda: send_to_slack(
        "Veckostatistik", 
        f"*ChargeNode AI Bot - Veckostatistik*\n\n{json.dumps(generate_monthly_stats(7), indent=2)}", 
        "#3498db"
    ))
    
    while True:
        schedule.run_pending()
        time.sleep(60)  # Kontrollera varje minut

# Starta schemaläggaren i en separat tråd
scheduler_thread = threading.Thread(target=run_scheduler, daemon=True)
scheduler_thread.start()

# Kör en statusrapport vid uppstart för att verifiera att allt fungerar
try:
    print("Skickar en inledande statusrapport för att verifiera Slack-integrationen...")
    # Anropa inte direkt här - sker i schemaläggaren
except Exception as e:
    print(f"Information: Statusrapport kommer att skickas enligt schema: {e}")

# --- Gradio UI ---
initial_chat = [{"role": "assistant", "content": "Detta är ChargeNode's AI bot. Hur kan jag hjälpa dig idag?"}]

custom_css = """
body {background-color: #f7f7f7; font-family: Arial, sans-serif; margin: 0; padding: 0;}
h1 {font-family: Helvetica, sans-serif; color: #2a9d8f; text-align: center; margin-bottom: 0.5em;}
.gradio-container {max-width: 400px; margin: 0; padding: 10px; position: fixed; bottom: 20px; right: 20px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); border-radius: 10px; background-color: #fff;}
#chatbot_conversation { max-height: 300px; overflow-y: auto; }
.gr-button {background-color: #2a9d8f; color: #fff; border: none; border-radius: 4px; padding: 8px 16px; margin: 5px;}
.gr-button:hover {background-color: #264653;}
.support-btn {background-color: #000000; color: #ffffff; margin-top: 5px; margin-bottom: 10px;}
.support-btn:hover {background-color: #333333;}
.flex-row {display: flex; flex-direction: row; gap: 5px;}
.gr-form {padding: 10px; border: 1px solid #eee; border-radius: 4px; margin-bottom: 10px;}
.chat-preview {max-height: 150px; overflow-y: auto; border: 1px solid #eee; padding: 8px; margin-top: 10px; font-size: 12px; background-color: #f9f9f9;}
.success-message {font-size: 16px; font-weight: normal; margin-bottom: 15px;}
/* Dölj Gradio-footer */
footer {display: none !important;}
.footer {display: none !important;}
.gr-footer {display: none !important;}
.gradio-footer {display: none !important;}
.gradio-container .footer {display: none !important;}
.gradio-container .gr-footer {display: none !important;}
"""

with gr.Blocks(css=custom_css, title="ChargeNode Kundtjänst") as app:
    gr.Markdown("Ställ din fråga om ChargeNodes produkter och tjänster nedan. Om du inte gillar botten, så ring oss gärna på 010 – 205 10 55")
    
    # Chat interface
    with gr.Group(visible=True) as chat_interface:
        chatbot = gr.Chatbot(value=initial_chat, type="messages", elem_id="chatbot_conversation")
        chatbot.like(vote, None, None)
        
        with gr.Row():
            msg = gr.Textbox(label="Meddelande", placeholder="Ange din fråga...")
        
        with gr.Row():
            with gr.Column(scale=1):
                clear = gr.Button("Rensa")
            with gr.Column(scale=1):
                support_btn = gr.Button("Behöver du mer hjälp?", elem_classes="support-btn")
    
    # Support form interface (initially hidden)
    with gr.Group(visible=False) as support_interface:
        gr.Markdown("### Vänligen fyll i din områdeskod, uttagsnummer och din email adress")
        
        with gr.Group(elem_classes="gr-form"):
            områdeskod = gr.Textbox(label="Områdeskod", placeholder="Områdeskod (valfritt)", info="Numeriskt värde")
            uttagsnummer = gr.Textbox(label="Uttagsnummer", placeholder="Uttagsnummer (valfritt)", info="Numeriskt värde")
            email = gr.Textbox(label="Din email adress", placeholder="din@email.se", info="Email adress krävs")
            
            gr.Markdown("### Chat som skickas till support:")
            chat_preview = gr.Markdown(elem_classes="chat-preview")
            
            with gr.Row():
                back_btn = gr.Button("Tillbaka")
                send_support_btn = gr.Button("Skicka")
    
    # Success message (initially hidden)
    with gr.Group(visible=False) as success_interface:
        gr.Markdown("Tack för att du kontaktar support@chargenode.eu. Vi återkommer inom kort", elem_classes="success-message")
        back_to_chat_btn = gr.Button("Tillbaka till chatten")
    
    def respond(message, chat_history, request: gr.Request):
        global last_log
        start = time.time()
        response = generate_answer(message)
        elapsed = round(time.time() - start, 2)

        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        session_id = str(uuid.uuid4())
        
        # Använd session_id från tidigare logg om det finns
        if last_log and 'session_id' in last_log:
            session_id = last_log.get('session_id')
            
        user_id = request.client.host if request else "okänd"

        ua_str = request.headers.get("user-agent", "")
        ref = request.headers.get("referer", "")
        ip = request.headers.get("x-forwarded-for", user_id).split(",")[0]
        ua = parse_ua(ua_str)
        browser = f"{ua.browser.family} {ua.browser.version_string}"
        osys = f"{ua.os.family} {ua.os.version_string}"

        platform = "webb"
        if "chargenode.eu" in ref:
            platform = "chargenode.eu"
        elif "localhost" in ref:
            platform = "test"
        elif "app" in ref:
            platform = "app"

        log_data = {
            "timestamp": timestamp,
            "user_id": user_id,
            "session_id": session_id,
            "user_message": message,
            "bot_reply": response,
            "response_time": elapsed,
            "ip": ip,
            "browser": browser,
            "os": osys,
            "platform": platform
        }

        # Använd den förbättrade loggfunktionen
        safe_append_to_log(log_data)
        last_log = log_data

        # Skicka varje konversation direkt till Slack
        try:
            # Konversationsinnehåll
            conversation_content = f"""
*Ny konversation {timestamp}*

*Användare:* {message}

*Bot:* {response[:300]}{'...' if len(response) > 300 else ''}

*Sessionsinfo:* {session_id[:8]}... | {browser} | {platform}
"""
            # Skicka asynkront för att inte blockera svarstiden
            threading.Thread(
                target=lambda: send_to_slack(f"Ny konversation", conversation_content),
                daemon=True
            ).start()
        except Exception as e:
            print(f"Kunde inte skicka konversation till Slack: {e}")

        chat_history.append({"role": "user", "content": message})
        chat_history.append({"role": "assistant", "content": response})
        return "", chat_history
    
    def format_chat_preview(chat_history):
        if not chat_history:
            return "Ingen chatthistorik att visa."
        
        preview = ""
        for msg in chat_history:
            sender = "Användare" if msg["role"] == "user" else "Bot"
            content = msg["content"]
            if len(content) > 100:  # Truncate long messages
                content = content[:100] + "..."
            preview += f"**{sender}:** {content}\n\n"
        
        return preview
    
    def show_support_form(chat_history):
        preview = format_chat_preview(chat_history)
        return {
            chat_interface: gr.Group(visible=False),
            support_interface: gr.Group(visible=True),
            success_interface: gr.Group(visible=False),
            chat_preview: preview
        }
    
    def back_to_chat():
        return {
            chat_interface: gr.Group(visible=True),
            support_interface: gr.Group(visible=False),
            success_interface: gr.Group(visible=False)
        }
    
    def submit_support_form(områdeskod, uttagsnummer, email, chat_history):
        """Hanterar formulärinskickningen med bättre felhantering."""
        print(f"Support-förfrågan: områdeskod={områdeskod}, uttagsnummer={uttagsnummer}, email={email}")
        
        # Validera input med tydligare loggning
        validation_errors = []
        
        if områdeskod and not områdeskod.isdigit():
            print(f"Validerar områdeskod: '{områdeskod}' (felaktig)")
            validation_errors.append("Områdeskod måste vara numerisk.")
        else:
            print(f"Validerar områdeskod: '{områdeskod}' (ok)")
        
        if uttagsnummer and not uttagsnummer.isdigit():
            print(f"Validerar uttagsnummer: '{uttagsnummer}' (felaktig)")
            validation_errors.append("Uttagsnummer måste vara numerisk.")
        else:
            print(f"Validerar uttagsnummer: '{uttagsnummer}' (ok)")
        
        if not email:
            print("Validerar email: (saknas)")
            validation_errors.append("En giltig e-postadress krävs.")
        elif '@' not in email or '.' not in email.split('@')[1]:
            print(f"Validerar email: '{email}' (felaktigt format)")
            validation_errors.append("En giltig e-postadress krävs.")
        else:
            print(f"Validerar email: '{email}' (ok)")
        
        # Om det finns valideringsfel
        if validation_errors:
            print(f"Valideringsfel: {validation_errors}")
            return {
                chat_interface: gr.Group(visible=False),
                support_interface: gr.Group(visible=True),
                success_interface: gr.Group(visible=False),
                chat_preview: "\n".join(["**Fel:**"] + validation_errors)
            }
        
        # Om formuläret klarade valideringen, försök skicka till Slack
        try:
            print("Försöker skicka supportförfrågan till Slack...")
            
            # Skapa en förenklad chathistorik för loggning
            chat_summary = []
            for msg in chat_history:
                if 'role' in msg and 'content' in msg:
                    chat_summary.append(f"{msg['role']}: {msg['content'][:30]}...")
            print(f"Chatthistorik att skicka: {chat_summary}")
            
            # Skicka till Slack
            success = send_support_to_slack(områdeskod, uttagsnummer, email, chat_history)
            
            if success:
                print("Support-förfrågan skickad till Slack framgångsrikt")
                return {
                    chat_interface: gr.Group(visible=False),
                    support_interface: gr.Group(visible=False),
                    success_interface: gr.Group(visible=True)
                }
            else:
                print("Support-förfrågan till Slack misslyckades")
                return {
                    chat_interface: gr.Group(visible=False),
                    support_interface: gr.Group(visible=True),
                    success_interface: gr.Group(visible=False),
                    chat_preview: "**Ett fel uppstod när meddelandet skulle skickas. Vänligen försök igen senare.**"
                }
        except Exception as e:
            print(f"Oväntat fel vid hantering av support-formulär: {e}")
            return {
                chat_interface: gr.Group(visible=False),
                support_interface: gr.Group(visible=True),
                success_interface: gr.Group(visible=False),
                chat_preview: f"**Ett fel uppstod: {str(e)}**"
            }
    
    msg.submit(respond, [msg, chatbot], [msg, chatbot])
    clear.click(lambda: None, None, chatbot, queue=False)
    support_btn.click(show_support_form, chatbot, [chat_interface, support_interface, success_interface, chat_preview])
    back_btn.click(back_to_chat, None, [chat_interface, support_interface, success_interface])
    back_to_chat_btn.click(back_to_chat, None, [chat_interface, support_interface, success_interface])
    send_support_btn.click(
        submit_support_form, 
        [områdeskod, uttagsnummer, email, chatbot], 
        [chat_interface, support_interface, success_interface, chat_preview]
    )

# Ladda kontexten direkt vid uppstart
print("Förbereder hela kontexten vid uppstart...")
load_full_context()
print("Kontext laddad och redo!")

if __name__ == "__main__":
    app.launch(share=True)