import os
import http.server
import socketserver
import urllib.parse
import zipfile
import io
import shutil
import json
from datetime import datetime

# --- Função auxiliar para formatar o tamanho do arquivo ---
def format_file_size(size_bytes):
    """Converte bytes em um formato legível (KB, MB, GB)."""
    if size_bytes < 1024:
        return f"{size_bytes} B"
    elif size_bytes < 1024**2:
        return f"{size_bytes/1024:.1f} KB"
    elif size_bytes < 1024**3:
        return f"{size_bytes/1024**2:.1f} MB"
    else:
        return f"{size_bytes/1024**3:.1f} GB"

# --- Sua função original para carregar JSON ---
def load_file_content(filename):
    try:
        folder = "oplaboptionvols"
        file_path = os.path.join(folder, f"{filename}.json")
        if os.path.exists(file_path):
            with open(file_path, 'r', encoding='utf-8') as file: return file.read()
        return None
    except Exception: return None

# --- Handler do Servidor Aprimorado ---
class MyAdvancedHttpRequestHandler(http.server.SimpleHTTPRequestHandler):

    # --- MÉTODO GET: Lida com visualização, download e sua rota JSON ---
    def do_GET(self):
        parsed_url = urllib.parse.urlparse(self.path)
        query_params = urllib.parse.parse_qs(parsed_url.query)

        # 1. ROTA PARA LISTAGEM EM JSON (Para o Delphi)
        if 'list' in query_params and query_params['list'][0] == 'json':
            path_to_list = self.translate_path(parsed_url.path)
            if os.path.isdir(path_to_list):
                self.serve_json_directory_list(path_to_list)
            else:
                self.send_error(404, "Diretorio nao encontrado")
            return
            
        # 2. Sua rota original de JSON por nome de arquivo
        if 'filename' in query_params:
            self.handle_json_request(query_params['filename'][0])
            return
            
        # 3. Sua rota original de Download ZIP
        if 'download' in query_params and query_params['download'][0] == 'zip':
            path_to_zip = self.translate_path(parsed_url.path)
            if os.path.isdir(path_to_zip):
                self.serve_directory_as_zip(path_to_zip)
            else:
                self.send_error(404, "Diretório não encontrado")
            return
        
        super().do_GET()
        
        
        
    # --- NOVO MÉTODO PARA O DELPHI ---
    def serve_json_directory_list(self, path):
        """Retorna uma lista JSON com caminhos limpos."""
        try:
            # CORREÇÃO: Pegamos apenas a parte do diretório, sem os "?list=json"
            parsed_url = urllib.parse.urlparse(self.path)
            clean_base_path = parsed_url.path 
            
            raw_list = os.listdir(path)
            items = []
            
            for name in raw_list:
                fullname = os.path.join(path, name)
                is_dir = os.path.isdir(fullname)
                
                # Montamos o path relativo de forma limpa
                # Ex: /oplaboptions/2026_02_03__11_00
                relative_path = clean_base_path.rstrip('/') + '/' + name
                
                info = {
                    "name": name,
                    "is_dir": is_dir,
                    "path": relative_path, 
                    "size_bytes": os.path.getsize(fullname) if not is_dir else 0,
                    "mtime": datetime.fromtimestamp(os.path.getmtime(fullname)).isoformat()
                }
                items.append(info)

            items.sort(key=lambda x: (not x['is_dir'], x['name'].lower()))

            response_json = json.dumps(items, indent=4).encode('utf-8')
            
            self.send_response(200)
            self.send_header("Content-type", "application/json; charset=utf-8")
            self.send_header("Content-Length", str(len(response_json)))
            self.end_headers()
            self.wfile.write(response_json)

        except Exception as e:
            self.send_error(500, f"Erro ao listar: {e}")
            
    
    # --- MÉTODO POST: Lida com ações, como deletar ---
    def do_POST(self):
        try:
            content_length = int(self.headers['Content-Length'])
            post_data = self.rfile.read(content_length)
            params = urllib.parse.parse_qs(post_data.decode('utf-8'))

            if params.get('action', [None])[0] == 'delete':
                path_to_delete_str = params.get('path', [None])[0]
                if not path_to_delete_str: raise ValueError("Caminho para deletar não especificado.")
                
                path_to_delete = self.translate_path(path_to_delete_str)
                print(f"Tentando deletar: {path_to_delete}")

                if os.path.isdir(path_to_delete): shutil.rmtree(path_to_delete)
                elif os.path.isfile(path_to_delete): os.remove(path_to_delete)
                else:
                    self.send_error(404, "Arquivo ou diretório não encontrado para deletar.")
                    return
                
                parent_dir = os.path.dirname(path_to_delete_str)
                if not parent_dir.endswith('/'): parent_dir += '/'
                self.send_response(303)
                self.send_header('Location', parent_dir)
                self.end_headers()
            else:
                self.send_error(400, "Ação POST inválida.")
        except Exception as e:
            print(f"Erro no POST: {e}")
            self.send_error(500, f"Erro interno do servidor: {e}")

    # --- Métodos auxiliares para GET ---
    def handle_json_request(self, filename):
        json_content = load_file_content(filename)
        if json_content:
            response_bytes = json_content.encode('utf-8')
            self.send_response(200); self.send_header("Content-type", "application/json")
        else:
            error_msg = '{"error": "File not found"}'
            response_bytes = error_msg.encode('utf-8')
            self.send_response(404); self.send_header("Content-type", "application/json")
        self.send_header("Content-Length", str(len(response_bytes)))
        self.end_headers()
        self.wfile.write(response_bytes)

    def serve_directory_as_zip(self, dir_path):
        """
        Compacta um diretório e seu conteúdo em um arquivo .zip e o serve para download.
        Esta versão foi corrigida e aprimorada para garantir a funcionalidade.
        """
        # Pega o nome do diretório que será o nome do arquivo .zip
        dir_name = os.path.basename(os.path.normpath(dir_path))
        zip_filename = f"{dir_name}.zip"
        
        # Usa um arquivo em memória (BytesIO) para não criar arquivos temporários no disco
        memory_file = io.BytesIO()

        print(f"Iniciando compactação do diretório '{dir_name}' para '{zip_filename}'...")

        # Cria o arquivo zip em modo de escrita
        with zipfile.ZipFile(memory_file, 'w', zipfile.ZIP_DEFLATED) as zf:
            # os.walk percorre o diretório e todos os seus subdiretórios
            for root, dirs, files in os.walk(dir_path):
                # Adiciona todos os arquivos encontrados
                for file in files:
                    # Cria o caminho completo para o arquivo
                    file_path = os.path.join(root, file)
                    # Cria o caminho relativo que será usado dentro do .zip
                    # Isso preserva a estrutura de pastas
                    archive_name = os.path.relpath(file_path, start=dir_path)
                    print(f"  Adicionando arquivo: {archive_name}")
                    zf.write(file_path, archive_name)
                
                # Adiciona diretórios vazios, se houver
                if not files and not dirs:
                    archive_name = os.path.relpath(root, start=dir_path)
                    print(f"  Adicionando diretório vazio: {archive_name}/")
                    zf.writestr(archive_name + '/', '')

        # Retorna ao início do arquivo em memória para que possa ser lido e enviado
        memory_file.seek(0)
        
        print(f"Compactação concluída. Tamanho final: {format_file_size(memory_file.getbuffer().nbytes)}")
        
        # Envia os cabeçalhos HTTP para o navegador iniciar o download
        self.send_response(200)
        self.send_header('Content-Type', 'application/zip')
        self.send_header('Content-Disposition', f'attachment; filename="{zip_filename}"')
        self.send_header('Content-Length', str(memory_file.getbuffer().nbytes))
        self.end_headers()
        
        # Envia o conteúdo do arquivo .zip para o cliente
        self.wfile.write(memory_file.getvalue())

    # --- MÉTODO PRINCIPAL DA INTERFACE ---
    def list_directory(self, path):
        try:
            raw_list = os.listdir(path)
        except OSError:
            self.send_error(404, "Permissão negada para listar o diretório")
            return None
        
        display_path = urllib.parse.unquote(self.path, errors='surrogatepass')
        
        directories, files = [], []
        for name in raw_list:
            if os.path.isdir(os.path.join(path, name)): directories.append(name)
            else: files.append(name)
        directories.sort(key=str.lower)
        files.sort(key=str.lower)
        sorted_list = directories + files
        
        download_icon = '<svg viewBox="0 0 24 24" stroke-width="1.5"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>'
        delete_icon = '<svg viewBox="0 0 24 24" stroke-width="1.5"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>'
        folder_icon = '<svg viewBox="0 0 24 24" stroke="#888" stroke-width="1" fill="#FFC107"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>'
        file_icon = '<svg viewBox="0 0 24 24" stroke="#888" stroke-width="1" fill="#FFFFFF"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline></svg>'
        
        html_parts = [
            '<!DOCTYPE html>', '<html><head>', f'<title>Índice de {display_path}</title>',
            '<meta charset="utf-8">',
            '<style>',
            'body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; margin: 2em; background-color: #f8f9fa; }',
            'h1 { color: #343a40; }',
            'table { width: 100%; border-collapse: collapse; box-shadow: 0 2px 5px rgba(0,0,0,0.1); background-color: #fff; }',
            'th, td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #dee2e6; vertical-align: middle; }',
            'th { background-color: #e9ecef; white-space: nowrap; color: #495057; }',
            'tr:nth-child(even) { background-color: #f8f9fa; }',
            'tr:hover { background-color: #e9ecef; }',
            'td.icon-cell { width: 40px; text-align: center; }',
            '.name-cell a { color: #007bff; text-decoration: none; font-weight: 500; }',
            '.name-cell a:hover { text-decoration: underline; }',
            '.icon-cell svg { width: 22px; height: 22px; stroke: #555; stroke-linecap: round; stroke-linejoin: round; }',
            '.icon-cell a, .icon-cell button { color: #555; display: inline-block; }',
            '.icon-cell a svg, .icon-cell button svg { fill: none; }',
            '.icon-cell a:hover, .icon-cell button:hover { color: #007bff; }',
            '.icon-cell a:hover svg, .icon-cell button:hover svg { stroke: #007bff; }',
            '.delete-icon:hover { color: #dc3545; }',
            '.delete-icon:hover svg { stroke: #dc3545; }',
            'form { margin:0; padding:0; display:inline; }',
            'button { background:none; border:none; padding:0; margin:0; cursor:pointer; vertical-align: middle; }',
            '.align-right { text-align: right; }',
            '</style>', '</head><body>',
            f'<h1>Índice de {display_path}</h1>',
            '<table><thead><tr>',
            '<th class="icon-cell">Baixar</th><th class="icon-cell">Deletar</th><th class="icon-cell">Tipo</th>',
            '<th>Nome</th><th class="align-right">Tamanho</th><th class="align-right">Data de Modificação</th>',
            '</tr></thead><tbody>'
        ]

        if display_path != '/':
            html_parts.append('<tr><td></td><td></td><td class="icon-cell">⬆️</td><td class="name-cell"><a href="..">Diretório Pai</a></td><td class="align-right">-</td><td class="align-right">-</td></tr>')

        for name in sorted_list:
            fullname = os.path.join(path, name)
            link_name = urllib.parse.quote(name, errors='surrogatepass')
            current_path = os.path.join(display_path, link_name).replace("\\", "/")

            try:
                mod_time_ts = os.path.getmtime(fullname)
                mod_time_str = datetime.fromtimestamp(mod_time_ts).strftime('%Y-%m-%d %H:%M')
                file_size_str = format_file_size(os.path.getsize(fullname)) if os.path.isfile(fullname) else '-'
            except OSError:
                mod_time_str, file_size_str = '-', '-'

            if os.path.isdir(fullname):
                type_icon, name_cell = folder_icon, f'<td class="name-cell"><a href="{link_name}/">{name}/</a></td>'
                download_link = f'<a href="{link_name}?download=zip" title="Baixar {name} como .zip">{download_icon}</a>'
            else:
                type_icon, name_cell = file_icon, f'<td class="name-cell">{name}</td>'
                download_link = f'<a href="{link_name}" download="{name}" title="Baixar {name}">{download_icon}</a>'
            
            delete_form = f'''<form method="POST" action="{current_path}" onsubmit="return confirm('ATENÇÃO!\\nTem certeza que deseja deletar \\'{name}\\'?');">
                <input type="hidden" name="action" value="delete"><input type="hidden" name="path" value="{current_path}">
                <button type="submit" class="delete-icon" title="Deletar {name}">{delete_icon}</button></form>'''
            
            html_parts.append(f'''<tr>
                <td class="icon-cell">{download_link}</td><td class="icon-cell">{delete_form}</td>
                <td class="icon-cell">{type_icon}</td>{name_cell}
                <td class="align-right">{file_size_str}</td><td class="align-right">{mod_time_str}</td>
            </tr>''')

        html_parts.extend(['</tbody></table></body></html>'])
        encoded_html = '\n'.join(html_parts).encode('utf-8')
        
        self.send_response(200)
        self.send_header("Content-type", "text/html; charset=utf-8")
        self.send_header("Content-Length", str(len(encoded_html)))
        self.end_headers()
        self.wfile.write(encoded_html)
    
    def translate_path(self, path):
        path = super().translate_path(path)
        base_dir = os.path.abspath(os.getcwd())
        requested_path = os.path.abspath(path)
        if not os.path.commonpath([base_dir, requested_path]) == base_dir:
            raise PermissionError("Tentativa de acesso a um caminho fora do diretório raiz.")
        return requested_path

# --- Configuração e Inicialização do Servidor ---
PORT = 8000
handler_object = MyAdvancedHttpRequestHandler
my_server = socketserver.TCPServer(("", PORT), handler_object)
print(f"Servidor iniciado na porta {PORT}. Acesse http://localhost:{PORT}")
print("Para parar o servidor, pressione Ctrl+C")
try:
    my_server.serve_forever()
except KeyboardInterrupt:
    print("\nServidor sendo desligado.")
    my_server.shutdown()