import http.server
import socketserver
import json
import os
import threading
import subprocess

PORT = 8081

# Caminho para agy.exe
AGY_PATH = os.path.join(os.path.expandvars('%LOCALAPPDATA%'), 'agy', 'bin', 'agy.exe')
if not os.path.exists(AGY_PATH):
    AGY_PATH = r"C:\Users\danie\AppData\Local\agy\bin\agy.exe"

# Estado global do processo CLI ativo
running_process = None
process_lock = threading.Lock()

DEFAULT_WORKSPACE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

def get_latest_transcript_file():
    cli_dir = os.path.join(os.path.expanduser('~'), '.gemini', 'antigravity-cli')
    brain_dir = os.path.join(cli_dir, 'brain')
    if not os.path.exists(brain_dir):
        return None
    
    try:
        # Lista todas as subpastas em brain
        subfolders = []
        for item in os.listdir(brain_dir):
            item_path = os.path.join(brain_dir, item)
            if os.path.isdir(item_path):
                subfolders.append(item_path)
                
        if not subfolders:
            return None
            
        # Ordena as pastas pelo tempo de modificação (mais recente primeiro)
        subfolders.sort(key=os.path.getmtime, reverse=True)
        
        # Procura por transcript.jsonl dentro de .system_generated/logs/
        for folder in subfolders:
            transcript_path = os.path.join(folder, '.system_generated', 'logs', 'transcript.jsonl')
            if os.path.exists(transcript_path):
                return transcript_path
    except Exception as e:
        print(f"[-] Erro ao buscar transcript mais recente: {e}")
        
    return None

def run_cli_prompt(prompt, workspace=None):
    global running_process
    with process_lock:
        # Finaliza o processo ativo se houver
        if running_process and running_process.poll() is None:
            try:
                run_kwargs = {
                    'stdout': subprocess.DEVNULL,
                    'stderr': subprocess.DEVNULL
                }
                if os.name == 'nt':
                    run_kwargs['creationflags'] = subprocess.CREATE_NO_WINDOW
                subprocess.run(["taskkill", "/F", "/T", "/PID", str(running_process.pid)], **run_kwargs)
            except:
                pass
        
        cmd = [
            AGY_PATH,
            "--prompt", prompt,
            "--dangerously-skip-permissions"
        ]
        
        target_cwd = workspace if (workspace and os.path.exists(workspace)) else DEFAULT_WORKSPACE
        if target_cwd:
            cmd.extend(["--add-dir", target_cwd])
            
        print(f"[+] Executando CLI em '{target_cwd}': {' '.join(cmd)}")
        
        clean_env = os.environ.copy()
        for key in list(clean_env.keys()):
            if key.startswith('ANTIGRAVITY_'):
                del clean_env[key]
                
        try:
            popen_kwargs = {
                'stdin': subprocess.DEVNULL,
                'stdout': subprocess.DEVNULL,
                'stderr': subprocess.DEVNULL,
                'cwd': target_cwd,
                'env': clean_env,
                'start_new_session': True
            }
            if os.name == 'nt':
                popen_kwargs['creationflags'] = subprocess.CREATE_NO_WINDOW
            running_process = subprocess.Popen(
                cmd,
                **popen_kwargs
            )
        except Exception as e:
            print(f"[-] Erro ao executar agy.exe: {e}")

def stop_cli_process():
    global running_process
    with process_lock:
        if running_process and running_process.poll() is None:
            try:
                run_kwargs = {
                    'stdout': subprocess.DEVNULL,
                    'stderr': subprocess.DEVNULL
                }
                if os.name == 'nt':
                    run_kwargs['creationflags'] = subprocess.CREATE_NO_WINDOW
                subprocess.run(["taskkill", "/F", "/T", "/PID", str(running_process.pid)], **run_kwargs)
                print(f"[+] Processo {running_process.pid} finalizado.")
            except Exception as e:
                print(f"[-] Erro ao parar processo: {e}")
            running_process = None

AUTH_PASSWORD = "123" # Mude a senha aqui!

def check_auth(handler):
    if not AUTH_PASSWORD: return True
    auth_header = handler.headers.get('Authorization')
    if auth_header == f"Bearer {AUTH_PASSWORD}": return True
    
    import urllib.parse
    parsed = urllib.parse.urlparse(handler.path)
    qs = urllib.parse.parse_qs(parsed.query)
    if qs.get('pwd', [''])[0] == AUTH_PASSWORD: return True
    
    return False

class MyHandler(http.server.SimpleHTTPRequestHandler):
    def do_POST(self):
        if self.path.startswith('/api/'):
            if not check_auth(self):
                self.send_response(401)
                self.end_headers()
                self.wfile.write(b"Unauthorized")
                return

        if self.path == '/api/send':
            content_length = int(self.headers['Content-Length'])
            post_data = self.rfile.read(content_length)
            
            try:
                data = json.loads(post_data.decode('utf-8'))
                message = data.get('message', '')
                workspace = data.get('workspace', '')
                
                if message:
                    threading.Thread(target=run_cli_prompt, args=(message, workspace)).start()
                
                self.send_response(200)
                self.send_header('Content-type', 'application/json')
                self.send_header('Access-Control-Allow-Origin', '*')
                self.end_headers()
                self.wfile.write(json.dumps({"status": "ok"}).encode('utf-8'))
            except Exception as e:
                self.send_response(500)
                self.send_header('Access-Control-Allow-Origin', '*')
                self.end_headers()
                self.wfile.write(json.dumps({"error": str(e)}).encode('utf-8'))
                
        elif self.path.startswith('/api/upload_audio'):
            try:
                import urllib.parse
                parsed_url = urllib.parse.urlparse(self.path)
                query_params = urllib.parse.parse_qs(parsed_url.query)
                
                ext = query_params.get('ext', ['webm'])[0]
                workspace = query_params.get('workspace', [''])[0]
                
                content_length = int(self.headers['Content-Length'])
                binary_data = self.rfile.read(content_length)
                
                target_cwd = workspace if (workspace and os.path.exists(workspace)) else DEFAULT_WORKSPACE
                
                if not os.path.exists(target_cwd):
                    os.makedirs(target_cwd, exist_ok=True)
                    
                import datetime
                timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
                filename = f"audio_{timestamp}.{ext}"
                file_path = os.path.join(target_cwd, filename)
                
                with open(file_path, 'wb') as f:
                    f.write(binary_data)
                
                # Executa o CLI prompt enviando o áudio como arquivo
                prompt_message = f"[Áudio enviado: {file_path}]"
                threading.Thread(target=run_cli_prompt, args=(prompt_message, workspace)).start()
                
                self.send_response(200)
                self.send_header('Content-type', 'application/json')
                self.send_header('Access-Control-Allow-Origin', '*')
                self.end_headers()
                self.wfile.write(json.dumps({"status": "ok", "file": file_path}).encode('utf-8'))
            except Exception as e:
                self.send_response(500)
                self.send_header('Access-Control-Allow-Origin', '*')
                self.end_headers()
                self.wfile.write(json.dumps({"error": str(e)}).encode('utf-8'))
                
        elif self.path == '/api/stop':
            try:
                threading.Thread(target=stop_cli_process).start()
                self.send_response(200)
                self.send_header('Content-type', 'application/json')
                self.send_header('Access-Control-Allow-Origin', '*')
                self.end_headers()
                self.wfile.write(json.dumps({"status": "ok"}).encode('utf-8'))
            except Exception as e:
                self.send_response(500)
                self.send_header('Access-Control-Allow-Origin', '*')
                self.end_headers()
                self.wfile.write(json.dumps({"error": str(e)}).encode('utf-8'))
                
        elif self.path == '/api/open_file':
            content_length = int(self.headers['Content-Length'])
            post_data = self.rfile.read(content_length)
            
            try:
                data = json.loads(post_data.decode('utf-8'))
                url = data.get('url', '')
                
                if url:
                    import urllib.parse
                    import urllib.request
                    
                    # Normaliza file:// URLs
                    path_url = url
                    if path_url.lower().startswith('file:'):
                        if path_url.lower().startswith('file://') and not path_url.lower().startswith('file:///'):
                            path_url = 'file:///' + path_url[7:]
                        elif path_url.lower().startswith('file:/') and not path_url.lower().startswith('file:///'):
                            path_url = 'file:///' + path_url[6:]
                            
                        parsed_url = urllib.parse.urlparse(path_url)
                        path = urllib.request.url2pathname(parsed_url.path)
                    else:
                        path = path_url
                    
                    # Decodifica qualquer codificacao URI restante e normaliza
                    path = urllib.parse.unquote(path)
                    path = os.path.normpath(path)
                    
                    print(f"[+] Abrindo arquivo local: {path}")
                    
                    if os.path.exists(path):
                        os.startfile(path)
                        status = "ok"
                        error_msg = ""
                    else:
                        print(f"[-] Arquivo local nao existe: {path}")
                        status = "error"
                        error_msg = f"O arquivo ou pasta nao existe no caminho especificado: {path}"
                else:
                    status = "error"
                    error_msg = "Nenhum link fornecido"
                
                if status == "ok":
                    self.send_response(200)
                    self.send_header('Content-type', 'application/json')
                    self.send_header('Access-Control-Allow-Origin', '*')
                    self.end_headers()
                    self.wfile.write(json.dumps({"status": "ok"}).encode('utf-8'))
                else:
                    self.send_response(400)
                    self.send_header('Content-type', 'application/json')
                    self.send_header('Access-Control-Allow-Origin', '*')
                    self.end_headers()
                    self.wfile.write(json.dumps({"error": error_msg}).encode('utf-8'))
                    
            except Exception as e:
                self.send_response(500)
                self.send_header('Access-Control-Allow-Origin', '*')
                self.end_headers()
                self.wfile.write(json.dumps({"error": str(e)}).encode('utf-8'))
                
        elif self.path == '/api/resumir':
            content_length = int(self.headers['Content-Length'])
            post_data = self.rfile.read(content_length)
            
            try:
                data = json.loads(post_data.decode('utf-8'))
                text = data.get('text', '')
                
                if not text:
                    self.send_response(400)
                    self.send_header('Access-Control-Allow-Origin', '*')
                    self.end_headers()
                    self.wfile.write(json.dumps({"error": "Texto não fornecido"}).encode('utf-8'))
                    return
                
                # Chave API Padrão
                gemini_key = os.environ.get('GEMINI_API_KEY', 'AIzaSyCqlSxtbrbklMkhnxpu0Zk9aP3uzxXE-4k')
                
                # Limpeza de markdown rápida
                import re
                clean_text = re.sub(r'\*\*([^*]+)\*\*', r'\1', text)
                clean_text = re.sub(r'\*([^*]+)\*', r'\1', clean_text)
                clean_text = re.sub(r'`([^`]+)`', r'\1', clean_text)
                clean_text = re.sub(r'```[\s\S]*?```', '[Trecho de código]', clean_text)
                clean_text = re.sub(r'\[([^\]]+)\]\([^)]+\)', r'\1', clean_text)
                clean_text = clean_text.strip()
                
                if not clean_text:
                    self.send_response(400)
                    self.send_header('Access-Control-Allow-Origin', '*')
                    self.end_headers()
                    self.wfile.write(json.dumps({"error": "Texto limpo vazio"}).encode('utf-8'))
                    return
                
                summary_prompt = (
                    "Você é um assistente pessoal. Sua tarefa é ler a mensagem a seguir e gerar um resumo extremamente curto "
                    "(máximo de 2 ou 3 frases simples) que capture apenas os pontos principais, em português do Brasil. "
                    "Escreva de forma conversacional, direta e natural, como se estivesse falando diretamente com o usuário. "
                    "Não use saudações, introduções ou tópicos.\n"
                    "Importante: Indique claramente se o projeto, recurso ou alterações foram publicados (ou não), caso faça sentido com a mensagem que está resumindo.\n\n"
                    f"Mensagem:\n{clean_text}"
                )
                
                summary_payload = {
                    "contents": [{
                        "parts": [{
                            "text": summary_prompt
                        }]
                    }]
                }
                
                import requests
                summary_url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={gemini_key}"
                sum_res = requests.post(summary_url, json=summary_payload, headers={"Content-Type": "application/json"}, timeout=15)
                
                if sum_res.status_code != 200:
                    raise Exception(f"Erro na API de Resumo do Gemini: {sum_res.text}")
                
                sum_data = sum_res.json()
                try:
                    final_summary = sum_data['candidates'][0]['content']['parts'][0]['text'].strip()
                except Exception:
                    final_summary = clean_text
                
                self.send_response(200)
                self.send_header('Content-type', 'application/json')
                self.send_header('Access-Control-Allow-Origin', '*')
                self.end_headers()
                self.wfile.write(json.dumps({"summary": final_summary}).encode('utf-8'))
            except Exception as e:
                self.send_response(500)
                self.send_header('Access-Control-Allow-Origin', '*')
                self.end_headers()
                self.wfile.write(json.dumps({"error": str(e)}).encode('utf-8'))
                
        elif self.path.startswith('/api/tts'):
            content_length = int(self.headers['Content-Length'])
            post_data = self.rfile.read(content_length)
            
            try:
                data = json.loads(post_data.decode('utf-8'))
                text = data.get('text', '')
                is_live = data.get('isLive', False)
                conversation_name = data.get('conversationName', '')
                workspace_name = data.get('workspaceName', '')
                narrate_full = data.get('narrateFull', False)
                
                if not text:
                    self.send_response(400)
                    self.send_header('Access-Control-Allow-Origin', '*')
                    self.end_headers()
                    self.wfile.write(json.dumps({"error": "Texto não fornecido"}).encode('utf-8'))
                    return
                
                # Chave API Padrão
                gemini_key = os.environ.get('GEMINI_API_KEY', 'AIzaSyCqlSxtbrbklMkhnxpu0Zk9aP3uzxXE-4k')
                
                # Limpeza de markdown rápida
                import re
                clean_text = re.sub(r'\*\*([^*]+)\*\*', r'\1', text)
                clean_text = re.sub(r'\*([^*]+)\*', r'\1', clean_text)
                clean_text = re.sub(r'`([^`]+)`', r'\1', clean_text)
                clean_text = re.sub(r'```[\s\S]*?```', '[Trecho de código]', clean_text)
                clean_text = re.sub(r'\[([^\]]+)\]\([^)]+\)', r'\1', clean_text)
                clean_text = clean_text.strip()
                
                if not clean_text:
                    self.send_response(400)
                    self.send_header('Access-Control-Allow-Origin', '*')
                    self.end_headers()
                    self.wfile.write(json.dumps({"error": "Texto limpo vazio"}).encode('utf-8'))
                    return
                
                final_summary = ""
                if narrate_full:
                    final_summary = clean_text[:2500] + '... (texto truncado)' if len(clean_text) > 2500 else clean_text
                else:
                    # Etapa 1: Gerar o resumo em texto usando o gemini-2.5-flash
                    live_instruction = ""
                    if is_live and conversation_name and workspace_name:
                        live_instruction = f'Você DEVE iniciar o resumo dizendo obrigatoriamente algo como: "Na conversa {conversation_name}, do workspace {workspace_name}, ..." ou uma variação natural similar.\n'
                    
                    summary_prompt = (
                        "Você é um assistente pessoal. Sua tarefa é ler a mensagem a seguir e gerar um resumo extremamente curto "
                        "(máximo de 2 ou 3 frases simples) que capture apenas os pontos principais, em português do Brasil. "
                        "Escreva de forma conversacional, direta e natural, como se estivesse falando diretamente com o usuário. "
                        "Não use saudações, introduções ou tópicos.\n"
                        f"{live_instruction}"
                        "Importante: Indique claramente se o projeto, recurso ou alterações foram publicados (ou não), caso faça sentido com a mensagem que está resumindo.\n\n"
                        f"Mensagem:\n{clean_text}"
                    )
                    
                    summary_payload = {
                        "contents": [{
                            "parts": [{
                                "text": summary_prompt
                            }]
                        }]
                    }
                    
                    import requests
                    summary_url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={gemini_key}"
                    sum_res = requests.post(summary_url, json=summary_payload, headers={"Content-Type": "application/json"}, timeout=15)
                    
                    if sum_res.status_code != 200:
                        raise Exception(f"Erro na API de Resumo do Gemini: {sum_res.text}")
                    
                    sum_data = sum_res.json()
                    try:
                        final_summary = sum_data['candidates'][0]['content']['parts'][0]['text'].strip()
                    except Exception:
                        final_summary = clean_text

                # Etapa 2: Gerar o áudio do resumo usando o gemini-3.1-flash-tts-preview
                tts_payload = {
                    "contents": [{
                        "parts": [{
                            "text": f"Leia o seguinte texto com voz natural em português do Brasil:\n{final_summary}"
                        }]
                    }],
                    "generationConfig": {
                        "responseModalities": ["AUDIO"],
                        "speechConfig": {
                            "voiceConfig": {
                                "prebuiltVoiceConfig": {
                                    "voiceName": "Aoede"
                                }
                            }
                        }
                    }
                }
                
                tts_url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-flash-tts-preview:generateContent?key={gemini_key}"
                res = requests.post(tts_url, json=tts_payload, headers={"Content-Type": "application/json"}, timeout=30)
                
                if res.status_code != 200:
                    raise Exception(f"Erro na API do Gemini TTS: {res.text}")
                
                res_data = res.json()
                candidate = res_data.get('candidates', [{}])[0]
                parts = candidate.get('content', {}).get('parts', [])
                
                pcm_base64 = ""
                for part in parts:
                    if 'inlineData' in part and 'data' in part['inlineData']:
                        pcm_base64 = part['inlineData']['data']
                        break
                
                if pcm_base64:
                    # Conversão de PCM Raw 24kHz 16-bit Mono para WAV
                    import base64
                    import struct
                    
                    pcm_data = base64.b64decode(pcm_base64)
                    num_channels = 1
                    sample_rate = 24000
                    bits_per_sample = 16
                    block_align = (num_channels * bits_per_sample) // 8
                    byte_rate = sample_rate * block_align
                    data_size = len(pcm_data)
                    chunk_size = 36 + data_size
                    
                    header = struct.pack(
                        '<4sI4s4sIHHIIHH4sI',
                        b'RIFF',
                        chunk_size,
                        b'WAVE',
                        b'fmt ',
                        16,          # Subchunk1Size
                        1,           # AudioFormat (1 = PCM)
                        num_channels,
                        sample_rate,
                        byte_rate,
                        block_align,
                        bits_per_sample,
                        b'data',
                        data_size
                    )
                    
                    wav_data = header + pcm_data
                    wav_base64 = base64.b64encode(wav_data).decode('utf-8')
                    
                    self.send_response(200)
                    self.send_header('Content-type', 'application/json')
                    self.send_header('Access-Control-Allow-Origin', '*')
                    self.end_headers()
                    self.wfile.write(json.dumps({"summary": final_summary, "audioBase64": wav_base64}).encode('utf-8'))
                else:
                    raise Exception("Formato de resposta do Gemini inválido ou sem dados de áudio")
                    
            except Exception as e:
                self.send_response(500)
                self.send_header('Access-Control-Allow-Origin', '*')
                self.end_headers()
                self.wfile.write(json.dumps({"error": str(e)}).encode('utf-8'))
                
    def do_GET(self):
        if self.path.startswith('/api/'):
            if not check_auth(self):
                self.send_response(401)
                self.end_headers()
                self.wfile.write(b"Unauthorized")
                return
                
        if self.path == '/api/chat':
            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.send_header('Access-Control-Allow-Origin', '*')
            self.end_headers()
            
            messages = []
            transcript_file = get_latest_transcript_file()
            if transcript_file and os.path.exists(transcript_file):
                try:
                    with open(transcript_file, 'r', encoding='utf-8', errors='replace') as f:
                        for line in f:
                            if not line.strip(): continue
                            try:
                                data = json.loads(line.strip())
                                # Filtra as mensagens de origem USER_EXPLICIT e MODEL
                                if "content" in data and data.get("source") in ("USER_EXPLICIT", "MODEL"):
                                    messages.append({
                                        "source": data["source"],
                                        "content": data["content"],
                                        "created_at": data.get("created_at", "")
                                    })
                            except Exception:
                                pass
                except Exception as e:
                    print(f"[-] Erro ao ler transcript: {e}")
            
            self.wfile.write(json.dumps(messages).encode('utf-8'))
            
        elif self.path == '/api/status':
            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.send_header('Access-Control-Allow-Origin', '*')
            self.end_headers()
            
            is_running = False
            with process_lock:
                if running_process and running_process.poll() is None:
                    is_running = True
                    
            self.wfile.write(json.dumps({
                "running": is_running,
                "default_workspace": DEFAULT_WORKSPACE
            }).encode('utf-8'))
            
        elif self.path == '/':
            self.send_response(200)
            self.send_header('Content-type', 'text/html; charset=utf-8')
            self.end_headers()
            with open('index.html', 'rb') as f:
                self.wfile.write(f.read())
        else:
            super().do_GET()

    def do_OPTIONS(self):
        self.send_response(200, "ok")
        self.send_header('Access-Control-Allow-Origin', '*')
        self.send_header('Access-Control-Allow-Methods', 'GET, OPTIONS, POST')
        self.send_header("Access-Control-Allow-Headers", "X-Requested-With, Content-type")
        self.end_headers()

socketserver.TCPServer.allow_reuse_address = True
with socketserver.TCPServer(("0.0.0.0", PORT), MyHandler) as httpd:
    print(f"Servidor CLI rodando em 0.0.0.0:{PORT}")
    httpd.serve_forever()
