File size: 26,313 Bytes
90a8f92
 
 
7d7d966
7922375
9a3420f
7922375
 
90a8f92
9d22343
90a8f92
7922375
90a8f92
13a627a
 
90a8f92
 
13a627a
90a8f92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8ccc4d5
7922375
90a8f92
 
 
7922375
90a8f92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7922375
90a8f92
 
9364a1e
 
90a8f92
7d7d966
9d22343
7bc54dd
7d7d966
5aac306
2679872
 
 
 
 
5aac306
 
 
 
 
 
 
7d7d966
5aac306
 
 
 
 
 
 
7bc54dd
 
7d7d966
36b1a05
7d7d966
 
 
 
 
 
 
 
5aac306
b553fef
5aac306
7d7d966
5aac306
7d7d966
 
 
7bc54dd
7d7d966
b553fef
36b1a05
 
 
 
b553fef
 
 
36b1a05
 
7bc54dd
36b1a05
7bc54dd
 
 
 
 
 
 
36b1a05
7bc54dd
b553fef
84f3aeb
36b1a05
84f3aeb
b553fef
 
 
 
 
 
84f3aeb
b553fef
0305b83
 
 
 
b553fef
 
 
 
36b1a05
7bc54dd
b553fef
7bc54dd
36b1a05
 
 
 
 
 
 
 
 
 
 
 
5aac306
7bc54dd
7d7d966
 
5aac306
 
 
 
 
 
 
2d4575b
5aac306
 
 
 
 
 
 
 
 
 
 
 
 
2d4575b
5aac306
 
 
 
 
 
 
2d4575b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4dda001
 
 
 
 
 
 
 
 
 
 
 
 
2d4575b
55416c9
4dda001
55416c9
 
5aac306
 
2d4575b
5aac306
90a8f92
b48ebe4
 
 
 
8188afb
 
9a3420f
8188afb
 
 
 
 
9a3420f
8188afb
 
5572594
8188afb
 
 
 
 
 
 
 
 
 
 
248f8c8
1cab411
248f8c8
9a3420f
248f8c8
9a3420f
248f8c8
 
 
 
 
 
 
 
 
 
 
 
9a3420f
248f8c8
 
 
 
 
 
 
 
 
ca8a5ca
 
248f8c8
 
9a3420f
 
1cab411
f767469
 
 
 
 
 
 
 
 
 
9a3420f
 
8188afb
 
5572594
8188afb
5572594
8188afb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5572594
8188afb
5572594
8188afb
9a3420f
90a8f92
 
 
 
 
81a19bb
 
 
 
 
 
9364a1e
 
 
81a19bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7922375
e9b4f67
 
 
 
 
 
 
 
 
de8ed9c
753c339
 
e9b4f67
07fe228
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0bdec9b
 
 
 
 
de8ed9c
 
753c339
 
 
 
 
 
 
 
 
 
 
7447c56
de8ed9c
753c339
7447c56
 
 
 
 
 
 
 
 
753c339
de8ed9c
 
753c339
 
 
 
 
 
 
7922375
 
 
 
 
4ebb593
 
 
7922375
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8188afb
4ebb593
 
 
 
c22959c
4ebb593
 
 
c22959c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4ebb593
 
 
 
 
 
c22959c
7e5be8f
4ebb593
 
c22959c
7e5be8f
4ebb593
 
 
c22959c
4ebb593
 
 
 
 
 
c22959c
7e5be8f
4ebb593
 
c22959c
4ebb593
 
c22959c
 
4ebb593
d53aa23
5cc7f9d
e782925
7e5be8f
c22959c
7e5be8f
 
c22959c
7e5be8f
c1b25ee
c22959c
c1b25ee
 
 
 
 
 
d53aa23
c1b25ee
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7e5be8f
c22959c
7e5be8f
 
5cc7f9d
4ebb593
64e6c4c
4ebb593
7922375
 
245f9b8
7922375
 
 
 
 
 
 
 
 
 
 
 
4ebb593
 
 
c22959c
4ebb593
 
c22959c
4ebb593
 
 
 
 
 
7922375
4ebb593
 
 
 
 
 
 
 
c22959c
4ebb593
 
c22959c
4ebb593
 
 
 
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
import os
import logging
import aiohttp
import pytz
import base64
import asyncio

from fastapi import APIRouter, HTTPException, Header, Body
from pydantic import BaseModel
from dateutil import parser
from datetime import datetime
from typing import Optional

logger = logging.getLogger("uvicorn.error")

router = APIRouter()


# 🔧 Supabase Config
SUPABASE_URL = "https://ussxqnifefkgkaumjann.supabase.co"
SUPABASE_KEY = os.getenv("SUPA_KEY")
SUPABASE_ROLE_KEY = os.getenv("SUPA_SERVICE_KEY")

if not SUPABASE_KEY or not SUPABASE_ROLE_KEY:
    raise ValueError("❌ SUPA_KEY or SUPA_SERVICE_KEY not set in environment!")

SUPABASE_HEADERS = {
    "apikey": SUPABASE_KEY,
    "Authorization": f"Bearer {SUPABASE_KEY}",
    "Content-Type": "application/json"
}

SUPABASE_ROLE_HEADERS = {
    "apikey": SUPABASE_ROLE_KEY,
    "Authorization": f"Bearer {SUPABASE_ROLE_KEY}",
    "Content-Type": "application/json",
    "Prefer": "return=representation"
}


# 🛡️ Verificação de token de usuário (sem admin check)
async def verify_user_token(user_token: str) -> str:
    headers = {
        "Authorization": f"Bearer {user_token}",
        "apikey": SUPABASE_KEY,
        "Content-Type": "application/json"
    }

    async with aiohttp.ClientSession() as session:
        async with session.get(f"{SUPABASE_URL}/auth/v1/user", headers=headers) as response:
            if response.status != 200:
                raise HTTPException(status_code=401, detail="Token inválido ou expirado")

            user_data = await response.json()
            user_id = user_data.get("id")
            if not user_id:
                raise HTTPException(status_code=400, detail="ID do usuário não encontrado")

    return user_id


# 📨 Modelo da requisição de ticket
class CreateTicketRequest(BaseModel):
    message: str
    subject: str
    priority: str

def format_datetime(dt_str):
    dt = parser.isoparse(dt_str)
    return dt.strftime("%m/%d/%Y, %I:%M %p")  # Hora com AM/PM

@router.get("/ticket/user")
async def get_user_tickets(
    user_token: str = Header(None, alias="User-key")
):
    user_id = await verify_user_token(user_token)

    async with aiohttp.ClientSession() as session:
        async with session.get(
            f"{SUPABASE_URL}/rest/v1/Tickets?user_id=eq.{user_id}&order=created_at.desc&limit=50",
            headers=SUPABASE_ROLE_HEADERS
        ) as ticket_resp:
            if ticket_resp.status != 200:
                error_detail = await ticket_resp.text()
                raise HTTPException(status_code=500, detail=f"Error fetching tickets: {error_detail}")

            tickets = await ticket_resp.json()

        ticket_results = []

        for ticket in tickets:
            ticket_id = ticket["id"]
            ticket_created = parser.isoparse(ticket["created_at"])
            formatted_ticket_date = ticket_created.strftime("%m/%d/%Y")

            # Buscar a primeira mensagem
            async with session.get(
                f"{SUPABASE_URL}/rest/v1/messages_tickets?ticket_id=eq.{ticket_id}&order=created_at.asc&limit=1",
                headers=SUPABASE_ROLE_HEADERS
            ) as first_msg_resp:
                first_message = None
                if first_msg_resp.status == 200:
                    messages = await first_msg_resp.json()
                    first_message = messages[0] if messages else None

            # Buscar todas mensagens para histórico e status
            async with session.get(
                f"{SUPABASE_URL}/rest/v1/messages_tickets?ticket_id=eq.{ticket_id}&order=created_at.asc",
                headers=SUPABASE_ROLE_HEADERS
            ) as all_msg_resp:
                all_messages = await all_msg_resp.json() if all_msg_resp.status == 200 else []

            history = [f"Ticket created on {ticket_created.strftime('%m/%d/%Y, %I:%M %p')}"]

            # Detectar mudanças de suporte
            assigned_support_ids = []
            last_support_id = None
            for msg in all_messages:
                msg_user = msg["user"]
                if msg_user != user_id and msg_user != last_support_id:
                    last_support_id = msg_user
                    assigned_support_ids.append((msg_user, msg["created_at"]))

            for support_id, assigned_time in assigned_support_ids:
                async with session.get(
                    f"{SUPABASE_URL}/rest/v1/User?id=eq.{support_id}",
                    headers=SUPABASE_ROLE_HEADERS
                ) as user_resp:
                    support_name = "Support"
                    if user_resp.status == 200:
                        user_data = await user_resp.json()
                        if user_data:
                            support_name = user_data[0].get("name", "Support")
                    history.append(f"Assigned to {support_name} on {format_datetime(assigned_time)}")

            # Status e última atualização (se necessário)
            status = "Open"
            if ticket.get("finished"):
                status = "Closed"
                if ticket.get("finished_date"):
                    closed_dt = parser.isoparse(ticket["finished_date"])
                    history.append(f"Ticket closed on {closed_dt.strftime('%m/%d/%Y, %I:%M %p')}")
            elif all_messages:
                last_msg = all_messages[-1]
                if last_msg["user"] != user_id:
                    status = "Answered"
                else:
                    status = "Open"  # já está por padrão, mas pode ser útil explicitar
            
                if len(all_messages) > 1:
                    # Sempre adicionar o last updated se houver mais de uma mensagem
                    last_msg_time = format_datetime(last_msg["created_at"])
                    history.append(f"Last updated on {last_msg_time}")

            # Montar retorno do ticket
            ticket_data = dict(ticket)
            ticket_data["formatted_date"] = formatted_ticket_date
            ticket_data["status"] = status

            if ticket.get("support_id"):
                async with session.get(
                    f"{SUPABASE_URL}/rest/v1/User?id=eq.{ticket['support_id']}",
                    headers=SUPABASE_ROLE_HEADERS
                ) as support_resp:
                    if support_resp.status == 200:
                        support_data = await support_resp.json()
                        if support_data:
                            support_info = support_data[0]
                            ticket_data["support_name"] = support_info.get("name")
                            ticket_data["support_email"] = support_info.get("email")

            ticket_results.append({
                "ticket": ticket_data,
                "first_message": first_message,
                "history": history
            })

    return ticket_results

@router.get("/ticket/detail")
async def get_ticket_details(ticket_id: int):
    async with aiohttp.ClientSession() as session:
        # 1. Buscar dados do ticket
        async with session.get(
            f"{SUPABASE_URL}/rest/v1/Tickets?id=eq.{ticket_id}",
            headers=SUPABASE_ROLE_HEADERS
        ) as ticket_resp:
            if ticket_resp.status != 200:
                raise HTTPException(status_code=404, detail="Ticket não encontrado")

            ticket_data = await ticket_resp.json()
            if not ticket_data:
                raise HTTPException(status_code=404, detail="Ticket inexistente")

            ticket = ticket_data[0]

        # 2. Buscar as 50 últimas mensagens
        async with session.get(
            f"{SUPABASE_URL}/rest/v1/messages_tickets?ticket_id=eq.{ticket_id}&order=created_at.desc&limit=50",
            headers=SUPABASE_ROLE_HEADERS
        ) as msg_resp:
            if msg_resp.status != 200:
                raise HTTPException(status_code=500, detail="Erro ao buscar mensagens")

            messages_raw = await msg_resp.json()

        # 3. Buscar info dos usuários das mensagens
        user_cache = {}

        for msg in messages_raw:
            user_id = msg["user"]
            if user_id not in user_cache:
                async with session.get(
                    f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}",
                    headers=SUPABASE_ROLE_HEADERS
                ) as user_resp:
                    if user_resp.status == 200:
                        user_data = await user_resp.json()
                        if user_data:
                            u = user_data[0]
                            user_cache[user_id] = {
                                "id": u["id"],
                                "name": u.get("name", "Desconhecido"),
                                "avatar": u.get("avatar", None),
                                "type": "support" if u.get("role") == "admin" else "customer"
                            }
                    else:
                        user_cache[user_id] = {
                            "id": user_id,
                            "name": "Desconhecido",
                            "avatar": None,
                            "type": "unknown"
                        }

        # 4. Substituir o campo user nas mensagens
        messages = []
        for msg in messages_raw:
            uid = msg["user"]
            user_info = user_cache.get(uid, {
                "id": uid,
                "name": "Desconhecido",
                "avatar": None,
            })
        
            # Definir o tipo com base no ticket
            if uid == ticket["user_id"]:
                user_info["type"] = "customer"
            else:
                user_info["type"] = "support"
        
            messages.append({
                **msg,
                "user": user_info
            })

    return {
        "ticket": ticket,
        "messages": messages
    }

class RespondTicketRequest(BaseModel):
    ticket_id: int
    content: str
    
@router.post("/ticket/respond")
async def respond_ticket(
    body: RespondTicketRequest,
    user_token: str = Header(None, alias="User-key")
):
    user_id = await verify_user_token(user_token)
    created_at = datetime.utcnow().isoformat()

    # 1. Buscar dados do ticket
    async with aiohttp.ClientSession() as session:
        async with session.get(
            f"{SUPABASE_URL}/rest/v1/Tickets?id=eq.{body.ticket_id}",
            headers=SUPABASE_ROLE_HEADERS
        ) as ticket_resp:
            if ticket_resp.status != 200:
                raise HTTPException(status_code=404, detail="Ticket não encontrado")

            ticket_data = await ticket_resp.json()
            if not ticket_data:
                raise HTTPException(status_code=404, detail="Ticket inexistente")

            ticket = ticket_data[0]

    support_id = ticket.get("support_id")
    ticket_creator_id = ticket.get("user_id")  # <- CORRIGIDO AQUI

    # 2. Verificar/Atribuir suporte
    if support_id is None:
        # Verifica se usuário é admin
        async with aiohttp.ClientSession() as session:
            async with session.get(
                f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}",
                headers=SUPABASE_ROLE_HEADERS
            ) as user_resp:
                if user_resp.status != 200:
                    raise HTTPException(status_code=403, detail="Usuário inválido")

                user_data = await user_resp.json()
                if not user_data or not user_data[0].get("is_admin", False):
                    raise HTTPException(status_code=403, detail="Apenas administradores podem assumir tickets")

        # Atualiza support_id
        async with aiohttp.ClientSession() as session:
            async with session.patch(
                f"{SUPABASE_URL}/rest/v1/Tickets?id=eq.{body.ticket_id}",
                headers={**SUPABASE_ROLE_HEADERS, "Prefer": "return=representation"},
                json={"support_id": user_id}
            ) as patch_resp:
                if patch_resp.status != 200:
                    raise HTTPException(status_code=500, detail="Erro ao atribuir o suporte ao ticket")
    else:
        # Permitir exceção se o usuário for o criador do ticket
        if support_id != user_id and user_id != ticket_creator_id:
            raise HTTPException(status_code=403, detail="Ticket já está atribuído a outro suporte")

    # 3. Espera 1 segundo antes de enviar a mensagem
    await asyncio.sleep(1)

    # 3.1. Reabrir ticket se estiver fechado
    if ticket.get("finished", False) is True:
        async with aiohttp.ClientSession() as session:
            async with session.patch(
                f"{SUPABASE_URL}/rest/v1/Tickets?id=eq.{body.ticket_id}",
                headers={**SUPABASE_ROLE_HEADERS, "Prefer": "return=representation"},
                json={"finished": False, "finished_date": None}
            ) as reopen_resp:
                if reopen_resp.status != 200:
                    raise HTTPException(status_code=500, detail="Erro ao reabrir o ticket")

    # 4. Criar mensagem
    message_payload = {
        "user": user_id,
        "content": body.content,
        "created_at": created_at,
        "ticket_id": body.ticket_id
    }

    async with aiohttp.ClientSession() as session:
        async with session.post(
            f"{SUPABASE_URL}/rest/v1/messages_tickets",
            headers=SUPABASE_ROLE_HEADERS,
            json=message_payload
        ) as message_resp:
            if message_resp.status != 201:
                error_detail = await message_resp.text()
                raise HTTPException(status_code=500, detail=f"Erro ao salvar a mensagem: {error_detail}")

            message_data = await message_resp.json()

    return {
        "status": "response sent successfully",
        "ticket_id": body.ticket_id,
        "message_id": message_data[0]["id"],
        "message_content": body.content
    }
    
@router.post("/ticket/create")
async def create_ticket(
    body: CreateTicketRequest,
    user_token: str = Header(None, alias="User-key")
):
    user_id = await verify_user_token(user_token)
    created_at = datetime.utcnow().isoformat()

    ticket_payload = {
        "user_id": user_id,
        "support_id": None,
        "created_at": created_at,
        "subject": body.subject,
        "priority": body.priority
    }

    async with aiohttp.ClientSession() as session:
        async with session.post(
            f"{SUPABASE_URL}/rest/v1/Tickets",
            headers=SUPABASE_ROLE_HEADERS,
            json=ticket_payload
        ) as ticket_resp:

            if ticket_resp.status != 201:
                error_detail = await ticket_resp.text()
                raise HTTPException(status_code=500, detail=f"Erro ao criar ticket: {error_detail}")

            ticket_data = await ticket_resp.json()
            ticket_id = ticket_data[0]["id"]

    message_payload = {
        "user": user_id,
        "content": body.message,
        "created_at": created_at,
        "ticket_id": ticket_id
    }

    async with aiohttp.ClientSession() as session:
        async with session.post(
            f"{SUPABASE_URL}/rest/v1/messages_tickets",
            headers=SUPABASE_ROLE_HEADERS,
            json=message_payload
        ) as message_resp:

            if message_resp.status != 201:
                error_detail = await message_resp.text()
                raise HTTPException(status_code=500, detail=f"Erro ao criar mensagem: {error_detail}")

    return {"ticket_id": ticket_id}

class CloseTicketRequest(BaseModel):
    ticket_id: int

@router.post("/ticket/close")
async def close_ticket(
    body: CloseTicketRequest,
    user_token: str = Header(None, alias="User-key")
):
    user_id = await verify_user_token(user_token)
    logger.info(f"User ID verified: {user_id}")
    
    finished_date = datetime.utcnow().isoformat()
    
    # Verificar se o ticket já está finalizado
    async with aiohttp.ClientSession() as session:
        async with session.get(
            f"{SUPABASE_URL}/rest/v1/Tickets?id=eq.{body.ticket_id}",
            headers=SUPABASE_ROLE_HEADERS
        ) as get_resp:
            if get_resp.status != 200:
                error_detail = await get_resp.text()
                logger.error(f"Error fetching ticket: {error_detail}")
                raise HTTPException(status_code=500, detail=f"Error fetching ticket: {error_detail}")

            ticket_data = await get_resp.json()
            if not ticket_data:
                raise HTTPException(status_code=404, detail="Ticket not found")

            ticket = ticket_data[0]
            if ticket.get("finished", False):
                logger.info(f"Ticket {body.ticket_id} is already closed.")
                return {"message": "Ticket is already closed", "ticket_id": body.ticket_id}

    # Atualizar o finished e finished_date
    update_payload = {
        "finished": True,
        "finished_date": finished_date
    }
    logger.info(f"Updating ticket with payload: {update_payload}")
    
    try:
        async with aiohttp.ClientSession() as session:
            async with session.patch(
                f"{SUPABASE_URL}/rest/v1/Tickets?id=eq.{body.ticket_id}",
                headers={
                    **SUPABASE_ROLE_HEADERS,
                    "Content-Type": "application/json"
                },
                json=update_payload
            ) as update_resp:
                logger.info(f"Response status: {update_resp.status}")
                response_data = await update_resp.json()  # Alterado de text() para json(), pois é uma resposta JSON.
                logger.info(f"Response data: {response_data}")
                
                if update_resp.status == 200:
                    # Verificar se o ticket foi realmente atualizado corretamente.
                    updated_ticket = response_data[0]
                    if updated_ticket["finished"] and updated_ticket["finished_date"]:
                        logger.info(f"Ticket {body.ticket_id} closed successfully.")
                        return {"message": "Ticket closed successfully", "ticket_id": body.ticket_id}
                    else:
                        logger.error(f"Ticket update failed: {updated_ticket}")
                        raise HTTPException(status_code=500, detail="Ticket update failed.")
                else:
                    logger.error(f"Error updating ticket: {response_data}")
                    raise HTTPException(status_code=500, detail=f"Error updating ticket: {response_data}")
    except aiohttp.ClientError as e:
        logger.error(f"AIOHTTP client error: {str(e)}")
        raise HTTPException(status_code=500, detail=f"Client error: {str(e)}")
    except Exception as e:
        logger.error(f"Unexpected error: {str(e)}")
        raise HTTPException(status_code=500, detail=f"Unexpected error: {str(e)}")
        
# 📧 Envio de e-mails com Gmail API
GMAIL_CLIENT_ID = "784687789817-3genmmvps11ip3a6fkbkkd8dm3bstgdc.apps.googleusercontent.com"
GMAIL_CLIENT_SECRET = "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"
GMAIL_REFRESH_TOKEN = "1//04ZOO_chVwlYiCgYIARAAGAQSNwF-L9IrhQO1ij79thk-DTjiMudl_XQshuU5CDTDYtt8rrOTMbz_rL8ECGjNfEN9da6W-mnjhZA"

class TicketResponseRequest(BaseModel):
    ticket_id: int
    content: str


async def get_gmail_access_token() -> str:
    url = "https://oauth2.googleapis.com/token"
    data = {
        "client_id": GMAIL_CLIENT_ID,
        "client_secret": GMAIL_CLIENT_SECRET,
        "refresh_token": GMAIL_REFRESH_TOKEN,
        "grant_type": "refresh_token"
    }

    async with aiohttp.ClientSession() as session:
        async with session.post(url, data=data) as response:
            if response.status != 200:
                raise HTTPException(status_code=500, detail="Erro ao obter access_token do Gmail")
            token_data = await response.json()
            return token_data["access_token"]


def encode_message(raw_message: str) -> str:
    return base64.urlsafe_b64encode(raw_message.encode("utf-8")).decode("utf-8").replace("=", "")


@router.post("/ticket/support/respond")
async def respond_ticket(
    payload: TicketResponseRequest,
    user_token: str = Header(None, alias="User-key")
):
    # 1. Verify support agent token
    support_id = await verify_user_token(user_token)
    created_at = datetime.utcnow().isoformat()

    # Get support agent name
    async with aiohttp.ClientSession() as session:
        async with session.get(
            f"{SUPABASE_URL}/rest/v1/User?id=eq.{support_id}",
            headers=SUPABASE_ROLE_HEADERS
        ) as support_user_resp:
            if support_user_resp.status != 200:
                raise HTTPException(status_code=404, detail="Support agent not found")
            
            support_user_data = await support_user_resp.json()
            if not support_user_data:
                raise HTTPException(status_code=404, detail="Support agent does not exist")
            
            support_name = support_user_data[0].get("name", "Support Team").strip()

    # 2. Retrieve ticket
    async with aiohttp.ClientSession() as session:
        async with session.get(
            f"{SUPABASE_URL}/rest/v1/Tickets?id=eq.{payload.ticket_id}",
            headers=SUPABASE_ROLE_HEADERS
        ) as ticket_resp:
            if ticket_resp.status != 200:
                raise HTTPException(status_code=404, detail="Ticket not found")

            ticket_data = await ticket_resp.json()
            if not ticket_data:
                raise HTTPException(status_code=404, detail="Ticket does not exist")

            ticket = ticket_data[0]
            user_id = ticket["user_id"]

    # 3. Get user email and name
    async with aiohttp.ClientSession() as session:
        async with session.get(
            f"{SUPABASE_URL}/rest/v1/User?id=eq.{user_id}",
            headers=SUPABASE_ROLE_HEADERS
        ) as user_resp:
            if user_resp.status != 200:
                raise HTTPException(status_code=404, detail="User not found")

            user_data = await user_resp.json()
            if not user_data:
                raise HTTPException(status_code=404, detail="User does not exist")

            user_email = user_data[0]["email"]
            user_name = user_data[0].get("name", "user").strip()
            first_name = user_name.split(" ")[0] if user_name else "user"

    # 4. Send email with clean, elegant HTML template with brand purple gradient
    access_token = await get_gmail_access_token()
    subject = f"Customer Support - Case {payload.ticket_id}"

    email_html = f"""
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    </head>
    <body style="margin:0;padding:0;background-color:#f9f9f9;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;">
        <table width="100%" cellpadding="0" cellspacing="0" border="0" bgcolor="#f9f9f9">
            <tr>
                <td align="center" style="padding:40px 10px;">
                    <table width="600" cellpadding="0" cellspacing="0" border="0" style="background:#ffffff;box-shadow:0 2px 4px rgba(0,0,0,0.1);">
                        <tr>
                            <td style="height:5px;background:linear-gradient(to right, #6E2FC6, #9662D9);"></td>
                        </tr>
                        <tr>
                            <td style="padding:30px 40px;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:14px;line-height:1.6;color:#333333;">
                                <p style="margin:0 0 20px;font-size:15px;color:#333333;">Hello {first_name},</p>
                                <div style="margin:0 0 20px;color:#333333;">
                                    {payload.content}
                                </div>
                                <p style="margin:20px 0 0;font-size:15px;color:#333333;">Best regards,</p>
                                <p style="margin:2px 0 0;font-size:15px;color:#333333;">{support_name}</p>
                                <p style="margin:2px 0 0;font-size:14px;color:#666666;">Customer Support</p>
                            </td>
                        </tr>
                        <tr>
                            <td style="height:1px;background:#f0f0f0;"></td>
                        </tr>
                        <tr>
                            <td style="padding:20px;text-align:center;font-size:12px;color:#999999;">
                                ClosetCoach © 2025
                            </td>
                        </tr>
                    </table>
                </td>
            </tr>
        </table>
    </body>
    </html>
    """

    raw_message = f"""To: {user_email}
From: "ClosetCoach" <[email protected]>
Subject: {subject}
Content-Type: text/html; charset="UTF-8"

{email_html}
"""

    encoded_message = encode_message(raw_message)

    async with aiohttp.ClientSession() as session:
        async with session.post(
            "https://gmail.googleapis.com/gmail/v1/users/me/messages/send",
            headers={
                "Authorization": f"Bearer {access_token}",
                "Content-Type": "application/json"
            },
            json={"raw": encoded_message}
        ) as gmail_resp:
            if gmail_resp.status != 200:
                error_detail = await gmail_resp.text()
                raise HTTPException(status_code=500, detail=f"Error sending email: {error_detail}")
            gmail_data = await gmail_resp.json()

    # 5. Save response in messages_tickets
    message_payload = {
        "user": support_id,
        "content": payload.content,
        "created_at": created_at,
        "ticket_id": payload.ticket_id
    }

    async with aiohttp.ClientSession() as session:
        async with session.post(
            f"{SUPABASE_URL}/rest/v1/messages_tickets",
            headers=SUPABASE_ROLE_HEADERS,
            json=message_payload
        ) as msg_resp:
            if msg_resp.status != 201:
                error_detail = await msg_resp.text()
                raise HTTPException(status_code=500, detail=f"Error recording response: {error_detail}")

    return {
        "status": "response sent successfully",
        "ticket_id": payload.ticket_id,
        "email_to": user_email,
        "message_id": gmail_data.get("id")
    }