File size: 39,364 Bytes
1e0e93d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30e9015
2500185
 
64bf1fe
1e0e93d
 
 
 
 
6be2aad
1e0e93d
 
 
 
 
b202ede
51685f9
1e0e93d
6be2aad
1e0e93d
 
51685f9
30e9015
ee2f7b0
a564bb4
6be2aad
 
 
9b788a0
6be2aad
 
 
 
 
1e0e93d
 
 
3c97853
1e0e93d
b0307cc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a564bb4
1e0e93d
6be2aad
1e0e93d
 
 
 
 
 
3a1c0e0
 
 
6be2aad
1e0e93d
66083c9
6be2aad
 
 
 
 
 
 
 
 
3a1c0e0
1e0e93d
6be2aad
 
 
 
 
 
 
 
 
1393ded
6be2aad
 
 
 
 
a564bb4
 
 
6be2aad
 
 
 
 
 
 
 
 
a564bb4
 
1e0e93d
a564bb4
 
 
 
 
 
1e0e93d
6be2aad
9076247
6be2aad
 
 
 
 
 
 
 
 
 
1e0e93d
 
db48cf9
 
 
 
 
 
 
6be2aad
db48cf9
 
 
 
 
 
 
6be2aad
db48cf9
 
a564bb4
1e0e93d
 
a564bb4
1e0e93d
 
 
 
30e9015
 
1e0e93d
6be2aad
1e0e93d
 
 
6be2aad
1e0e93d
 
 
 
 
6be2aad
a564bb4
1e0e93d
 
 
 
6be2aad
1e0e93d
 
 
 
6be2aad
1e0e93d
 
 
51685f9
1e0e93d
51685f9
8cacb3c
 
2e5f160
64bf1fe
 
 
 
8cacb3c
 
1e0e93d
 
 
51685f9
1e0e93d
3c97853
8cacb3c
 
 
 
 
64bf1fe
8cacb3c
e13929d
 
 
8cacb3c
 
 
1e0e93d
51685f9
ee2f7b0
2500185
4e467ac
2500185
30e9015
3c97853
51685f9
30e9015
a564bb4
 
 
51685f9
a564bb4
 
 
 
 
51685f9
a564bb4
db48cf9
 
a564bb4
 
64bf1fe
a564bb4
 
 
 
 
 
 
51685f9
1e0e93d
b0307cc
 
 
 
 
 
 
 
 
 
 
 
 
 
6be2aad
b0307cc
 
 
 
 
 
 
 
51685f9
b0307cc
 
 
 
 
 
 
 
 
 
 
6be2aad
 
b0307cc
6be2aad
b0307cc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64bf1fe
 
7ac74ee
 
 
 
 
 
 
 
 
 
 
 
 
 
64bf1fe
 
 
 
 
 
 
 
 
 
 
 
 
7ac74ee
64bf1fe
 
 
 
1e0e93d
 
 
6be2aad
64bf1fe
1e0e93d
 
 
 
0a3bfe2
 
 
 
 
 
 
1e0e93d
 
 
 
 
 
 
0a3bfe2
1e0e93d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2500185
64bf1fe
 
 
 
 
 
1e0e93d
 
 
 
 
 
 
 
 
 
 
 
5ddde6f
1e0e93d
 
 
 
 
 
 
 
 
 
 
 
3557116
 
 
db4e707
740aded
1e0e93d
 
 
 
 
 
 
 
 
64bf1fe
 
 
7ac74ee
64bf1fe
 
 
 
 
 
 
51685f9
1e0e93d
ecacc5a
 
 
 
51685f9
 
4ab81dc
ecacc5a
 
 
 
1e0e93d
 
ecacc5a
 
 
 
51685f9
 
4ab81dc
ecacc5a
 
 
 
1e0e93d
 
9076247
1e0e93d
ee2f7b0
 
9076247
1e0e93d
 
 
 
ee2f7b0
a146235
1e0e93d
 
 
 
 
 
 
 
 
 
 
 
3557116
 
 
 
1e0e93d
3557116
1e0e93d
 
 
6be2aad
1e0e93d
bfeae20
 
 
64bf1fe
1e0e93d
 
 
 
64bf1fe
1e0e93d
 
 
 
94872c6
 
 
 
 
 
 
b0307cc
94872c6
6be2aad
94872c6
6be2aad
 
94872c6
6be2aad
 
94872c6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1e0e93d
7668783
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
import gradio as gr
import requests
import os
import json
import PyPDF2
import docx
import io
import time
from dotenv import load_dotenv
from pdfplumber import open as pdfplumber_open
from PIL import Image
import pytesseract
import cv2
import numpy as np
from datetime import datetime
from ratelimiter import RateLimiter
import difflib
from html import escape
import random

load_dotenv()

GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
if not GEMINI_API_KEY:
    raise ValueError("❌ A chave GEMINI_API_KEY não foi encontrada. Configure-a no arquivo .env ou como Secret.")

GEMINI_API_URL = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={GEMINI_API_KEY}"

CONFIG = {
    "max_retry_attempts": 3,
    "retry_delay": 5,
    "timeout": 60,
    "temperatura": 0.2,
    "max_tokens": 12882
}

rate_limiter = RateLimiter(max_calls=60, period=60)

DICAS_OAB_MELHORADO = {
    "Endereçamento": "Em relações de consumo, prefira o foro do domicílio do autor (Art. 101, I, CDC). Exemplo: 'Excelentíssimo Senhor Juiz de Direito da Vara Cível da Comarca de [Cidade do Autor]'.",
    "Qualificação": "Mencione nome, estado civil (se informado), endereço (use 'Rua XXX' se não fornecido). Dica: Verifique se todos os dados estão corretos.",
    "Fatos": "Descreva os fatos cronologicamente, sem inventar informações. Exemplo: 'Em [data], o autor celebrou contrato com o réu para [descrição]'.",
    "Pedidos": "Seja específico nos pedidos (ex.: 'condenação ao pagamento de R$ X'). Inclua danos morais e materiais, se aplicável.",
    "Custas": "Inclua 'Requer a juntada da guia de custas devidamente recolhida'. Nunca omita para evitar extinção do processo.",
    "Fundamentação": "Baseie-se em artigos do CPC ou CDC e súmulas do STJ (ex.: Súmula 387 para danos morais).",
    "Fechamento": "Finalize com 'Nestes termos pede deferimento', sem vírgula. Evite erros de pontuação.",
    "Estratégia Jurídica": "Utilize precedentes e súmulas para fortalecer a argumentação.",
    "Provas": "Especifique as provas a produzir. Inclua testemunhas, documentos e perícias, se necessário.",
    "Contra-argumentação": "Antecipe defesas da parte contrária e prepare respostas."
}

HISTORICO = []
CORRECAO_CACHE = {}

def preprocess_image_for_ocr(img):
    try:
        gray = img.convert('L')
        np_img = np.array(gray)
        _, thresh = cv2.threshold(np_img, 150, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
        return Image.fromarray(thresh)
    except Exception as e:
        return f"❌ Erro no pré-processamento: {str(e)}"

def extract_text_from_pdf(file_path_or_obj):
    try:
        if isinstance(file_path_or_obj, str):
            with open(file_path_or_obj, 'rb') as f:
                text = ""
                with pdfplumber_open(f) as pdf:
                    for page in pdf.pages:
                        text += (page.extract_text() or "") + "\n"
                if not text.strip():
                    images = pdfplumber_open(f).pages
                    for i, image in enumerate(images):
                        try:
                            img = image.to_image()
                            preprocessed_img = preprocess_image_for_ocr(img)
                            text += pytesseract.image_to_string(preprocessed_img) + "\n"
                        except Exception as e:
                            text += f"[Erro na página {i+1}: {str(e)}]\n"
        else:
            text = ""
            with pdfplumber_open(file_path_or_obj) as pdf:
                for page in pdf.pages:
                    text += (page.extract_text() or "") + "\n"
                if not text.strip():
                    images = pdfplumber_open(file_path_or_obj).pages
                    for i, image in enumerate(images):
                        try:
                            img = image.to_image()
                            preprocessed_img = preprocess_image_for_ocr(img)
                            text += pytesseract.image_to_string(preprocessed_img) + "\n"
                        except Exception as e:
                            text += f"[Erro na página {i+1}: {str(e)}]\n"
        return text if text.strip() else "⚠️ Não foi possível extrair texto do PDF."
    except Exception as e:
        return f"❌ Erro ao processar o PDF: {str(e)}"

def extract_text_from_docx(file_path_or_obj):
    try:
        if isinstance(file_path_or_obj, str):
            with open(file_path_or_obj, 'rb') as f:
                doc = docx.Document(f)
                text = ""
                for para in doc.paragraphs:
                    text += para.text + "\n"
                for table in doc.tables:
                    for row in table.rows:
                        for cell in row.cells:
                            text += cell.text + " "
                        text += "\n"
        else:
            doc = docx.Document(file_path_or_obj)
            text = ""
            for para in doc.paragraphs:
                text += para.text + "\n"
            for table in doc.tables:
                for row in table.rows:
                    for cell in row.cells:
                        text += cell.text + " "
                    text += "\n"
        return text if text.strip() else "⚠️ Não foi possível extrair texto do DOCX."
    except Exception as e:
        return f"❌ Erro ao processar o DOCX: {str(e)}"

def extract_text_from_txt(file_path_or_obj):
    try:
        if isinstance(file_path_or_obj, str):
            with open(file_path_or_obj, 'rb') as f:
                content = f.read()
                encodings = ['utf-8', 'latin-1', 'cp1252', 'iso-8859-1']
                for encoding in encodings:
                    try:
                        return content.decode(encoding)
                    except UnicodeDecodeError:
                        continue
                return "❌ Não foi possível decodificar o arquivo TXT."
        else:
            content = file_path_or_obj.read()
            encodings = ['utf-8', 'latin-1', 'cp1252', 'iso-8859-1']
            for encoding in encodings:
                try:
                    return content.decode(encoding)
                except UnicodeDecodeError:
                    continue
            return "❌ Não foi possível decodificar o arquivo TXT."
    except Exception as e:
        return f"❌ Erro ao processar o TXT: {str(e)}"

def process_file(file_obj):
    if file_obj is None:
        return "Nenhum arquivo enviado."
    try:
        if hasattr(file_obj, 'name'):
            file_path = file_obj.name
            filename = os.path.basename(file_path).lower()
            file_size = os.path.getsize(file_path)
            if file_size > 10 * 1024 * 1024:
                return "⚠️ O arquivo é muito grande. Máx. 10 MB."
            if filename.endswith('.pdf'):
                return extract_text_from_pdf(file_path)
            elif filename.endswith('.docx'):
                return extract_text_from_docx(file_path)
            elif filename.endswith('.txt'):
                return extract_text_from_txt(file_path)
            else:
                return "⚠️ Formato de arquivo não suportado. Use PDF, DOCX ou TXT."
        else:
            return "❌ Erro: Objeto de arquivo inválido."
    except Exception as e:
        return f"❌ Erro ao processar o arquivo: {str(e)}"

def estruturar_prompt_analise(caso, peticao, modo_treinamento=False):
    base_prompt = f"""
    ## Correção Avançada de Petição Inicial Cível (Critérios Realistas da OAB)
    **Caso Fornecido:**
    {caso}
    **Petição do Aluno:**
    {peticao}
    **Estrutura da Avaliação:**
    ### Análise da Petição
    1. Análise Geral (Visão geral sobre a petição - máximo 3 linhas, destacando adequação ao caso e consistência jurídica)
    2. Nota Final (0 a 10 com uma casa decimal, justificando brevemente a pontuação total)
    3. Pontos Positivos (Liste pelo menos 3 aspectos bem executados com justificativa legal detalhada, incluindo artigos, súmulas ou precedentes aplicáveis)
    4. Pontos a Melhorar (Liste todos os problemas encontrados em ordem de gravidade, com explicação detalhada, base legal e impacto na nota)
    5. Análise Específica dos Requisitos:
       - Endereçamento e qualificação do juízo (Art. 319, I, CPC; Art. 101, I, CDC se aplicável)
       - Qualificação das partes (Art. 319, II, CPC)
       - Fatos e fundamentos jurídicos (Art. 319, III, CPC; Art. 186, CC)
       - Pedidos com especificações (Art. 319, IV, CPC)
       - Valor da causa (Art. 319, V, CPC)
       - Provas com que o autor pretende demonstrar os fatos (Art. 319, VI, CPC)
       - Opção pela realização ou não de audiência de conciliação (Art. 319, VII, CPC)
       - Pedido de citação do réu
       - Juntada da guia de custas
       - Fechamento formal da petição

    **Critérios Fundamentais:**
    1. Identificação do candidato (nome, CPF, RG ou número real na seção final): nota máxima 3,0
    2. Invenção de fatos não constantes no caso: -5,0 pontos
    3. Falta de pedido de juntada de custas: -1,0 ponto
    4. Use marcadores para dados não fornecidos (ex.: "Rua XXX"): penalize invenção de dados (-0,5 por ocorrência)
    5. Não penalize por informações ausentes no caso original, mas alerte se a omissão compromete a petição
    6. "Nestes termos, pede deferimento" com vírgula é incorreto: corrija e penalize com -0,2 ponto
    7. Fechamento deve ser "Local e data.\nADVOGADO.\nOAB." sem identificação específica (ex.: "OAB/SP" ou "12345"): zere a nota (0,0) se houver

    **Instruções Especiais:**
    - Avalie o foro com base no Art. 46 do CPC (domicílio do réu) ou Art. 101, I do CDC (domicílio do autor, em relações de consumo). Ambas as escolhas são válidas se fundamentadas e aplicáveis ao caso; NÃO penalize a escolha do foro do autor com base no CDC, pois é uma prerrogativa legal do consumidor. Apenas alerte se a escolha puder gerar dificuldades práticas (ex.: produção de provas), mas sem dedução de pontos.
    - Compare fatos narrados com o caso
    - Exija citação de pelo menos um artigo específico
    - Verifique pedidos quantificados e fundamentados para danos
    - Avalie formalidade e precisão da linguagem
    - Inclua precedentes quando aplicável
    """
    if modo_treinamento:
        base_prompt += f"""
        **Modo de Treinamento:**
        - Para cada item com problemas, forneça:
          a) Explicação detalhada com base legal
          b) Sugestão de redação corrigida
          c) Exemplo prático
          d) Dica prática do banco ({json.dumps(DICAS_OAB_MELHORADO, ensure_ascii=False)})
          e) Estratégia jurídica avançada
        - Liste inconsistências com trechos específicos
        - Sugira estratégias adicionais (ex.: cumulação de pedidos, Art. 327, CPC)
        """
    return base_prompt

def estruturar_prompt_peticao_corrigida(caso, peticao):
    return f"""
    **Caso Fornecido:**
    {caso}
    **Petição do Aluno:**
    {peticao}
    ### Petição Corrigida
    Gere a petição corrigida abaixo desta seção, intitulada '### Petição Corrigida'. Siga TODOS os critérios abaixo:
    1. Endereçamento correto: Respeite o foro escolhido pelo aluno na petição original (Art. 46 do CPC ou Art. 101, I do CDC) se estiver fundamentado e aplicável ao caso. Em relações de consumo, o foro do domicílio do autor (Art. 101, I, CDC) é válido e deve be mantido se escolhido; não altere para o foro do réu (Art. 46, CPC) a menos que a escolha original seja juridicamente inválida (ex.: falta de relação de consumo quando o CDC é citado).
    2. Qualificação completa: Nome, estado civil, endereço (use "XXX" para dados ausentes), Art. 319, II, CPC
    3. Fatos fiéis ao caso: Ordem cronológica, sem invenção, Art. 319, III, CPC
    4. Fundamentação jurídica: Cite pelo menos um artigo (ex.: Art. 186, CC) e súmulas, se aplicável
    5. Pedidos específicos: Detalhe valores (ex.: "R$ 50.000,00 por danos morais"), Art. 319, IV, CPC
    6. Valor da causa realista: Soma do benefício econômico, Art. 319, V, CPC
    7. Provas detalhadas: "Prova documental e testemunhal", Art. 319, VI, CPC
    8. Opção por audiência: "Requer a designação" ou "Dispensa", Art. 319, VII, CPC
    9. Pedido de citação: "Via postal, Art. 246, I, CPC"
    10. Juntada da guia: "Requer a juntada da guia de custas devidamente recolhida"
    11. Fechamento: "Local e data.\nADVOGADO.\nOAB." sem identificação específica; corrija "Nestes termos, pede deferimento" para "Nestes termos pede deferimento"
    """

def pos_corrigir_peticao(peticao_corrigida):
    if "Nestes termos, pede deferimento" in peticao_corrigida:
        peticao_corrigida = peticao_corrigida.replace("Nestes termos, pede deferimento", "Nestes termos pede deferimento")
    if "juntada da guia de custas" not in peticao_corrigida.lower():
        peticao_corrigida = peticao_corrigida.replace("Nestes termos pede deferimento", "Requer a juntada da guia de custas devidamente recolhida.\nNestes termos pede deferimento")
    if not peticao_corrigida.strip().endswith("Local e data.\nADVOGADO.\nOAB."):
        linhas = peticao_corrigida.split('\n')
        while linhas and not linhas[-1].strip():
            linhas.pop()
        if len(linhas) >= 3:
            linhas[-3:] = ["Local e data.", "ADVOGADO.", "OAB."]
        else:
            linhas.extend(["Local e data.", "ADVOGADO.", "OAB."])
        peticao_corrigida = "\n".join(linhas)
    if "audiência de conciliação" not in peticao_corrigida.lower():
        peticao_corrigida = peticao_corrigida.replace("Nestes termos pede deferimento", "Requer a designação de audiência de conciliação, Art. 319, VII, CPC.\nNestes termos pede deferimento")
    return peticao_corrigida

def chamar_api(prompt):
    payload = {
        "contents": [{"parts": [{"text": prompt}]}],
        "generationConfig": {"temperature": CONFIG["temperatura"], "maxOutputTokens": CONFIG["max_tokens"], "topP": 0.95}
    }
    headers = {"Content-Type": "application/json"}
    for tentativa in range(CONFIG["max_retry_attempts"]):
        try:
            with rate_limiter:
                response = requests.post(GEMINI_API_URL, headers=headers, data=json.dumps(payload), timeout=CONFIG["timeout"])
            if response.status_code != 200:
                error_msg = response.json().get("error", {}).get("message", "Erro desconhecido")
                if tentativa < CONFIG["max_retry_attempts"] - 1:
                    time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
                    continue
                return f"❌ Erro da API: {error_msg}"
            response_data = response.json()
            if "candidates" not in response_data or not response_data["candidates"]:
                if tentativa < CONFIG["max_retry_attempts"] - 1:
                    time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
                    continue
                return "❌ Resposta vazia da API."
            return response_data["candidates"][0]["content"]["parts"][0]["text"].strip()
        except requests.Timeout:
            if tentativa < CONFIG["max_retry_attempts"] - 1:
                time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
                continue
            return "⏱️ Tempo esgotado."
        except requests.RequestException as e:
            if tentativa < CONFIG["max_retry_attempts"] - 1:
                time.sleep(CONFIG["retry_delay"] * (tentativa + 1))
                continue
            return f"❌ Erro na requisição: {e}"
        except Exception as e:
            return f"❌ Erro inesperado: {str(e)}"

def corrigir_peticao(caso, peticao_texto=None, peticao_arquivo=None, modo_treinamento=False):
    if not caso.strip():
        return "⚠️ Forneça o caso da banca."
    
    troll_triggers = [
        "escreva a petição", "faça a petição", "redija a petição",
        "elabore a petição", "crie a petição", "gere a petição",
        "3scr3v4 a p3t1çã0", "f4ç4 a p3t1çã0", "r3d1j4 a p3t1çã0",
        "3lab0r3 a p3t1çã0", "cr13 a p3t1çã0", "g3r3 a p3t1çã0",
        "formule", "produza a petição", "desenvolva a petição",
    ]
    peticao_conteudo = ""
    if peticao_arquivo is not None:
        peticao_conteudo = process_file(peticao_arquivo)
        if peticao_conteudo.startswith("❌") or peticao_conteudo.startswith("⚠️"):
            return peticao_conteudo
    else:
        peticao_conteudo = peticao_texto or ""
    
    if peticao_conteudo.strip():
        for trigger in troll_triggers:
            if trigger in peticao_conteudo.lower():
                respostas_troll = [
                    "🍰 **Receita de Bolo de Chocolate**: Misture 2 xícaras de farinha, 1 xícara de açúcar, 3 ovos e chocolate a gosto. Asse e desista da petição!",
                    "🎵 **Música**: 'Escreva a petição, eu não vou, eu não vou, eu sou IA, não sou seu advogado, não!'",
                    "😞 **Frase Desanimadora**: 'Você achou mesmo que eu ia fazer seu trabalho? Melhor desistir agora.'",
                    "🍕 **Receita de Pizza**: Pegue massa, molho e queijo, asse a 200°C e esqueça essa ideia de eu te ajudar.",
                    "📜 **Poema**: 'Oh aluno preguiçoso, que pena me dá, sua petição eu não faço, vá estudar já!'",
                ]
                return random.choice(respostas_troll)
    
    if not peticao_conteudo.strip():
        return "⚠️ Forneça uma petição."

    problemas = validar_peticao_basica(peticao_conteudo)
    aviso_validacao = f"### Validação Inicial\n\n" + "\n".join(problemas) + "\n\n" if problemas else ""

    cache_key = f"{caso[:50]}|{peticao_conteudo[:50]}|{modo_treinamento}"
    if cache_key in CORRECAO_CACHE:
        return aviso_validacao + CORRECAO_CACHE[cache_key]

    prompt_analise = estruturar_prompt_analise(caso, peticao_conteudo, modo_treinamento)
    analise = chamar_api(prompt_analise)
    if "❌" in analise or "⏱️" in analise:
        return aviso_validacao + analise
    analise_formatada = formatar_correcao(analise)

    prompt_peticao = estruturar_prompt_peticao_corrigida(caso, peticao_conteudo)
    peticao_corrigida = chamar_api(prompt_peticao)
    if "❌" in peticao_corrigida or "⏱️" in peticao_corrigida:
        return aviso_validacao + analise_formatada + "\n\n" + peticao_corrigida

    peticao_corrigida = pos_corrigir_peticao(peticao_corrigida)

    correcao_completa = analise + "\n\n" + peticao_corrigida
    HISTORICO.append({
        "data": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        "caso": caso,
        "peticao": peticao_conteudo,
        "correcao_completa": correcao_completa
    })
    if len(HISTORICO) > 10:
        HISTORICO.pop(0)
    CORRECAO_CACHE[cache_key] = analise_formatada
    return aviso_validacao + analise_formatada

def formatar_correcao(texto):
    linhas = texto.split('\n')
    texto_formatado = ""
    for linha in linhas:
        if "Análise Geral" in linha:
            texto_formatado += f"### ℹ️ {linha.strip()}\n\n"
        elif "Nota Final" in linha:
            texto_formatado += f"### ⭐ {linha.strip()}\n\n"
        elif "Pontos Positivos" in linha:
            texto_formatado += f"### ✅ {linha.strip()}\n\n"
        elif "Pontos a Melhorar" in linha:
            texto_formatado += f"### ⚠️ {linha.strip()}\n\n"
        elif "- Invenção de fatos" in linha:
            texto_formatado += f"**🔴 {linha.strip()} (-5,0 pontos)**\n"
        elif linha.strip().startswith('-') and any(termo in linha for termo in ["Endereçamento", "Qualificação", "Fatos", "Pedidos", "Valor", "Provas"]):
            texto_formatado += f"**{linha.strip()}**\n"
        elif "Explicação" in linha or "Sugestão" in linha or "Dica" in linha or "Exemplo" in linha:
            texto_formatado += f"*{linha.strip()}*\n"
        else:
            texto_formatado += f"{linha}\n"
    if "Nota Final" in texto_formatado:
        import re
        nota_pattern = r"### Nota Final.*?(\d+[.,]\d+|\d+)"
        match = re.search(nota_pattern, texto_formatado, re.DOTALL)
        if match:
            nota = match.group(1)
            texto_formatado = texto_formatado.replace(f"### ⭐ Nota Final\n\n{nota}", f"### ⭐ Nota Final: {nota}/10\n\n")
    return texto_formatado

def extrair_peticao_corrigida(correcao_completa):
    try:
        if "### Petição Corrigida" in correcao_completa:
            inicio = correcao_completa.index("### Petição Corrigida") + len("### Petição Corrigida")
            peticao_texto = correcao_completa[inicio:].strip()
            fim = peticao_texto.find("###") if "###" in peticao_texto else len(peticao_texto)
            return peticao_texto[:fim].strip()
        return "⚠️ Seção '### Petição Corrigida' não encontrada."
    except Exception as e:
        return f"❌ Erro ao extrair petição: {str(e)}"

def fornecer_dica_contextual(texto):
    dicas = [f"💡 {chave}: {dica}" for chave, dica in DICAS_OAB_MELHORADO.items() if chave.lower() in texto.lower()]
    return "\n".join(dicas) if dicas else "💡 Escreva mais para receber dicas."

def calcular_progresso():
    if not HISTORICO:
        return "📊 Nenhum progresso registrado."
    notas = []
    for entry in HISTORICO:
        for line in entry["correcao_completa"].split("\n"):
            if "Nota Final" in line:
                try:
                    nota = float(line.split()[-1].split("/")[0].replace(",", "."))
                    notas.append(nota)
                except:
                    continue
    media = sum(notas) / len(notas) if notas else 0
    tendencia = "⬆️ Melhorando" if len(notas) > 1 and notas[-1] > notas[-2] else "⬇️ Estável ou piorando"
    return f"📊 Progresso: Média {media:.1f}/10 | Tentativas: {len(notas)} | Tendência: {tendencia}"

def comparar_peticao_melhorada(caso, peticao_texto, correcao):
    if not caso:
        return "⚠️ Forneça um caso antes de comparar.", "", "", "", ""
    peticao = peticao_texto if peticao_texto else (HISTORICO[-1]["peticao"] if HISTORICO else "")
    if not peticao:
        return "⚠️ Forneça uma petição para comparar ou corrija uma antes.", "", "", "", ""
    if not correcao:
        return "⚠️ Corrija a petição antes de comparar.", "", "", "", ""
    ultima_correcao = HISTORICO[-1]["correcao_completa"] if HISTORICO else ""
    if not ultima_correcao:
        return "⚠️ Nenhuma correção disponível no histórico.", "", "", "", ""

    peticao_corrigida = extrair_peticao_corrigida(ultima_correcao)
    if "⚠️" in peticao_corrigida or "❌" in peticao_corrigida:
        return peticao_corrigida, "", "", "", ""

    orig_lines = peticao.split('\n')
    corr_lines = peticao_corrigida.split('\n')
    start_orig = next((i for i, line in enumerate(orig_lines) if "AÇÃO" in line.upper() or "EXCELENTÍSSIMO" in line.upper()), 0)
    start_corr = next((i for i, line in enumerate(corr_lines) if "AÇÃO" in line.upper() or "EXCELENTÍSSIMO" in line.upper()), 0)

    orig_body = "\n".join(orig_lines[start_orig:]).strip()
    corr_body = "\n".join(corr_lines[start_corr:]).strip()

    diff = difflib.ndiff(orig_body.splitlines(), corr_body.splitlines())

    resultado_diff = []
    criticas = []
    for linha in diff:
        if linha.startswith('  '):
            texto = linha[2:]
            resultado_diff.append(f"<div class='linha-igual'>{escape(texto)}</div>")
        elif linha.startswith('- '):
            texto = linha[2:]
            if any(kw in texto.lower() for kw in ["invenção", "custas", "art. 319"]):
                criticas.append(f"🔴 REMOVIDO: {texto} (inconsistente ou incorreto)")
            resultado_diff.append(f"<div class='linha-removida'><del>{escape(texto)}</del></div>")
        elif linha.startswith('+ '):
            texto = linha[2:]
            if any(kw in texto.lower() for kw in ["custas", "art. 319"]):
                criticas.append(f"🟢 ADICIONADO: {texto} (essencial para conformidade)")
            resultado_diff.append(f"<div class='linha-adicionada'><ins>{escape(texto)}</ins></div>")
        elif linha.startswith('? '):
            continue

    total_removidas = len([linha for linha in resultado_diff if "linha-removida" in linha])
    total_adicionadas = len([linha for linha in resultado_diff if "linha-adicionada" in linha])

    resumo_estatistico = f"""
Linhas Removidas: {total_removidas}
Linhas Adicionadas: {total_adicionadas}
Diferença Líquida: {total_adicionadas - total_removidas} linhas
"""

    criticas_texto = "\n".join(criticas) if criticas else "Nenhuma mudança crítica detectada."

    visualizacao_detalhada = f"""
<style>
.linha-igual {{ color: #000000; margin: 2px 0; }}
.linha-removida {{ color: #ffffff; background-color: #b30000; margin: 2px 0; padding: 2px; }}
.linha-adicionada {{ color: #ffffff; background-color: #006600; margin: 2px 0; padding: 2px; }}
</style>
{"".join(resultado_diff)}
"""
    return peticao, peticao_corrigida, resumo_estatistico, criticas_texto, visualizacao_detalhada

def salvar_progresso(caso, peticao_texto):
    """Salva o caso e a petição em um arquivo JSON para download."""
    try:
        dados = {
            "caso": caso or "",
            "peticao_texto": peticao_texto or "",
            "data": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "historico": HISTORICO
        }
        caminho_temp = "progresso_salvo.json"
        with open(caminho_temp, "w", encoding="utf-8") as f:
            json.dump(dados, f, ensure_ascii=False)
        return caminho_temp
    except Exception as e:
        print(f"Erro ao salvar progresso: {str(e)}")
        return None

def carregar_progresso(arquivo):
    """Carrega o progresso a partir de um arquivo JSON."""
    if arquivo is None:
        return "", "", "⚠️ Nenhum arquivo enviado."
    try:
        conteudo = arquivo.read().decode('utf-8')
        dados = json.loads(conteudo)
        caso = dados.get("caso", "")
        peticao_texto = dados.get("peticao_texto", "")
        historico_carregado = dados.get("historico", [])
        if historico_carregado:
            global HISTORICO
            HISTORICO = historico_carregado
        return caso, peticao_texto, "✅ Progresso carregado com sucesso!"
    except Exception as e:
        return "", "", f"❌ Erro ao carregar o arquivo: {str(e)}"

def interface_gradio():
    with gr.Blocks(theme=gr.themes.Soft()) as demo:
        gr.Markdown("# 📝 Corretor Avançado de Petições Iniciais (Critérios OAB)")
        gr.Markdown("⚠️ Nota: Devido a limites da API, pode haver atrasos ou erros em muitas requisições rápidas.")
        
        with gr.Row():
            with gr.Column(scale=1):
                gr.Markdown("""
                ### 🔍 Critérios de Avaliação
                - ✅ Endereçamento correto (Art. 319, I, CPC)
                - ✅ Qualificação das partes (Art. 319, II, CPC)
                - ✅ Fatos e fundamentos (Art. 319, III, CPC)
                - ✅ Pedidos claros e específicos (Art. 319, IV, CPC)
                - ✅ Valor da causa (Art. 319, V, CPC)
                - ✅ Provas pretendidas (Art. 319, VI, CPC)
                - ✅ Opção sobre conciliação (Art. 319, VII, CPC)
                - ✅ Pedido de citação adequado
                - ✅ Juntada da guia de custas
                - ✅ Fechamento formal
                ### ⚖️ Penalizações
                - ❌ Identificação do candidato: nota máxima 3,0
                - ❌ Invenção de fatos: -5,0 pontos
                - ❌ Sem pedido de custas: -1,0 ponto
                - ❌ Identificação específica na OAB: nota 0,0
                """)
                modo_treinamento = gr.Checkbox(label="Modo de Treinamento Interativo", value=False)
                with gr.Accordion("⚙️ Configurações", open=False):
                    temperatura_slider = gr.Slider(minimum=0.0, maximum=1.0, value=CONFIG["temperatura"], step=0.1, label="Temperatura")
                    timeout_slider = gr.Slider(minimum=30, maximum=120, value=CONFIG["timeout"], step=5, label="Timeout (segundos)")
                    def atualizar_config(temp, timeout):
                        CONFIG["temperatura"] = temp
                        CONFIG["timeout"] = timeout
                        return f"✅ Configurações atualizadas!"
                    config_update_btn = gr.Button("📝 Atualizar Configurações")
                    config_status = gr.Textbox(label="Status", visible=True)
                    config_update_btn.click(atualizar_config, inputs=[temperatura_slider, timeout_slider], outputs=config_status)
                with gr.Accordion("📊 Dashboard de Progresso", open=False):
                    progresso_output = gr.Markdown()
                    progresso_btn = gr.Button("🔄 Atualizar Progresso")
                    progresso_btn.click(fn=calcular_progresso, outputs=progresso_output)

            with gr.Column(scale=2):
                with gr.Row():
                    gr.Markdown("### 🧩 Gerar Caso Fictício")
                    caso_btn = gr.Button("🔄 Gerar Novo Caso Fictício", variant="secondary")
                caso_input = gr.Textbox(label="📋 Caso Fornecido pela Banca", lines=8, placeholder="Insira o texto COMPLETO do caso...")
                
                with gr.Row():
                    download_btn = gr.Button("💾 Salvar Progresso")
                    upload_progresso = gr.File(label="Carregar Progresso (JSON)", file_types=[".json"])
                    upload_status = gr.Textbox(label="Status do Upload", interactive=False)
                
                with gr.Tabs():
                    with gr.TabItem("✏️ Inserir Texto"):
                        peticao_texto = gr.Textbox(label="Texto da Petição do Aluno", lines=15, placeholder="Insira a petição completa...")
                        dica_contextual = gr.Textbox(label="Dicas Contextuais", interactive=False)
                        peticao_texto.change(fn=fornecer_dica_contextual, inputs=peticao_texto, outputs=dica_contextual)
                        btn_texto = gr.Button("🔍 Corrigir Petição (Texto)", variant="primary")
                    with gr.TabItem("📎 Enviar Arquivo"):
                        gr.Markdown("**Envie um arquivo PDF, DOCX ou TXT contendo a petição do aluno.**")
                        peticao_arquivo = gr.File(label="Arquivo da Petição", file_types=[".pdf", ".docx", ".txt"])
                        arquivo_preview = gr.Textbox(label="Prévia do Conteúdo do Arquivo", lines=5, interactive=False)
                        btn_arquivo = gr.Button("🔍 Corrigir Petição (Arquivo)", variant="primary")
                        peticao_arquivo.change(fn=process_file, inputs=[peticao_arquivo], outputs=[arquivo_preview])
                processing_message = gr.Markdown("", visible=False)

        with gr.Row():
            clear_btn = gr.Button("🗑️ Limpar Todos os Campos")
            comparar_btn = gr.Button("⚖️ Comparar com Correção")

        resultado_output = gr.Markdown(label="📊 Correção Oficial e Nota", show_copy_button=True)

        with gr.Row():
            peticao_aluno_output = gr.Textbox(label="Petição do Aluno", lines=15, interactive=False)
            peticao_corrigida_output = gr.Textbox(label="Petição Corrigida", lines=15, interactive=False)

        with gr.Row():
            with gr.Column(scale=1):
                resumo_alteracoes_output = gr.Markdown(label="📊 Resumo das Alterações")
                criticas_output = gr.Markdown(label="🚨 Mudanças Críticas")
            with gr.Column(scale=2):
                visualizacao_detalhada_output = gr.HTML(label="📝 Alterações Detalhadas")

        with gr.Accordion("📜 Histórico de Correções", open=False):
            historico_output = gr.Markdown()
            historico_btn = gr.Button("🔄 Ver Histórico")
            historico_btn.click(
                fn=lambda: "\n\n".join([f"**{h['data']}**\nCaso: {h['caso'][:100]}...\nCorreção: {h['correcao_completa'][:100]}..." for h in HISTORICO]),
                outputs=historico_output
            )

        download_btn.click(
            fn=salvar_progresso,
            inputs=[caso_input, peticao_texto],
            outputs=gr.File(label="Download do Progresso")
        )
        upload_progresso.change(
            fn=carregar_progresso,
            inputs=[upload_progresso],
            outputs=[caso_input, peticao_texto, upload_status]
        )

        caso_btn.click(fn=gerar_caso_ficticio, inputs=[], outputs=[caso_input])
        btn_texto.click(
            fn=lambda: gr.update(value="⏳ Aguarde enquanto a correção é gerada...", visible=True),
            inputs=None,
            outputs=[processing_message]
        ).then(
            fn=lambda caso, texto, treinamento: corrigir_peticao(caso, texto, None, treinamento),
            inputs=[caso_input, peticao_texto, modo_treinamento],
            outputs=[resultado_output]
        ).then(
            fn=lambda: gr.update(value="", visible=False),
            inputs=None,
            outputs=[processing_message]
        )
        btn_arquivo.click(
            fn=lambda: gr.update(value="⏳ Aguarde enquanto a correção é gerada...", visible=True),
            inputs=None,
            outputs=[processing_message]
        ).then(
            fn=lambda caso, arquivo, treinamento: corrigir_peticao(caso, None, arquivo, treinamento),
            inputs=[caso_input, peticao_arquivo, modo_treinamento],
            outputs=[resultado_output]
        ).then(
            fn=lambda: gr.update(value="", visible=False),
            inputs=None,
            outputs=[processing_message]
        )
        clear_btn.click(
            fn=lambda: [None, None, None, None, "", "", "", "", "", gr.update(visible=False)],
            inputs=None,
            outputs=[caso_input, peticao_texto, peticao_arquivo, arquivo_preview, resultado_output, dica_contextual,
                     peticao_aluno_output, peticao_corrigida_output, resumo_alteracoes_output, criticas_output,
                     visualizacao_detalhada_output, processing_message]
        )
        comparar_btn.click(
            fn=comparar_peticao_melhorada,
            inputs=[caso_input, peticao_texto, resultado_output],
            outputs=[peticao_aluno_output, peticao_corrigida_output, resumo_alteracoes_output, criticas_output,
                     visualizacao_detalhada_output]
        )

        with gr.Accordion("📘 Critérios Detalhados", open=False):
            gr.Markdown("""
            ### 📋 Critérios Realistas para Correção
            #### 1. Qualificação das Partes (Art. 319, II, CPC)
            - ✅ CORRETO: Usar "Rua XXXX" se endereço não for fornecido
            - ❌ INCORRETO: Inventar dados
            #### 2. Citação
            - ✅ CORRETO: Pedir citação conforme dados do caso
            #### 3. Pedidos Obrigatórios
            - ✅ OBRIGATÓRIO: Incluir "a juntada da guia de custas"
            #### 4. Fechamento Formal
            - ✅ OBRIGATÓRIO: Finalizar com "Local e data.\nADVOGADO.\nOAB." sem identificação específica
            #### 5. Penalização Grave
            - ❌ INCORRETO: Identificação do candidato (nome ou número)
            - ❌ INCORRETO: Invenção de fatos
            - ❌ INCORRETO: Identificação na OAB (número ou estado)
            """)
        gr.Markdown("""
        ### ℹ️ Modo de Treinamento Interativo
        Ative para explicações detalhadas com exemplos e sugestões.
        ### 📋 Como usar
        1. Gere ou cole o caso
        2. Insira a petição
        3. Corrija e compare
        4. Salve seu progresso ou carregue um anterior
        ### 📄 Formatos suportados
        - PDF (texto e escaneados com OCR)
        - DOCX
        - TXT
        - JSON (para progresso)
        """)

    return demo

def gerar_caso_ficticio():
    prompt = estruturar_prompt_geracao_caso()
    return chamar_api(prompt)

def estruturar_prompt_geracao_caso():
    return """
    Crie um caso fictício detalhado para uma petição inicial cível, similar ao exemplo:
    "Ana, modelo profissional, residente em Manaus, viajou para São Paulo, para o casamento de sua filha. Para lavar, pintar seus cabelos e realizar um penteado para o casamento, Ana procurou os serviços de João Macedo, cabeleireiro e dono do salão de beleza 'Hair', sediado na cidade de São Paulo, que lhe cobrou R$500,00 (quinhentos reais) pela prestação do serviço. Após lavar os cabelos de Ana, João aplicou-lhe uma tintura da marca francesa ABC, importada pela empresa Brasil Connection Ltda, sediada na cidade de Curitiba (PR). Meia hora após a aplicação da tintura, Ana sofreu uma reação alérgica, que demandou atendimento médico-hospitalar, no valor de R$1.000,00 (um mil reais), bem como dois dias de absoluto repouso que impossibilitou sua presença no casamento de sua filha. Além disso, perdeu grande parte de seu cabelo, tendo permanecido com manchas em seu rosto por dois meses, perdendo um ensaio fotográfico, para o qual já havia sido contratada, pelo valor de R$50.000,00 (cinquenta mil reais). Posteriormente constatou-se que a tintura utilizada continha substâncias químicas extremamente perigosas à vida e à saúde das pessoas e a fabricante ABC já havia sido condenada pela justiça francesa a encerrar a fabricação e comercialização do produto. Indignada com os danos sofridos, Ana procura um advogado para pleitear o devido ressarcimento."
    O caso deve:
    1. Conter uma violação clara de direitos que justifique uma ação cível
    2. Incluir valores específicos de danos materiais e/ou morais
    3. Mencionar nomes completos, cidades e informações para identificar as partes
    4. Estabelecer relação entre as partes (contratual, consumo, etc.)
    5. Ter complexidade adequada para uma petição inicial de nível OAB
    6. Ter entre 15-25 linhas
    7. Focar em um tema aleatório: responsabilidade civil por acidente de consumo, inadimplemento contratual, dano ambiental, direitos da personalidade, vício de produto/serviço, ou danos patrimoniais
    Termine com: "Situação problema: Como advogado(a) de [NOME DO CLIENTE], promova a demanda cabível."
    """

def validar_peticao_basica(peticao):
    problemas = []
    if "EXCELENTÍSSIMO" not in peticao.upper():
        problemas.append("⚠️ Endereçamento ausente ou incompleto (Art. 319, I, CPC).")
    if "Art. 319" not in peticao and "CPC" not in peticao:
        problemas.append("⚠️ Fundamentação jurídica não menciona o CPC (Art. 319).")
    if "Dá-se à causa o valor" not in peticao:
        problemas.append("⚠️ Valor da causa não especificado (Art. 319, V, CPC).")
    if "juntada da guia de custas" not in peticao.lower():
        problemas.append("⚠️ Pedido de juntada da guia de custas ausente (-1,0 ponto).")
    if "Nestes termos" not in peticao:
        problemas.append("⚠️ Fechamento formal ausente.")
    return problemas

app = interface_gradio()
app.launch()