#!/usr/bin/env python3
"""
Minimal HTTP file upload server (no Flask, no cgi).
Endpoint: POST /uploads  form field name: "file"
Saves files into ./uploads with timestamp + uuid to avoid overwrite.

Requirements: Python 3.7+
"""
import os
import uuid
import time
from http.server import HTTPServer, BaseHTTPRequestHandler
from socketserver import ThreadingMixIn
import re

# CONFIG
HOST = "0.0.0.0"
PORT = 51700
UPLOAD_DIR = "uploads"
MAX_CONTENT_LENGTH = 500 * 1024 * 1024  # 500 MB default max upload (change as needed)

os.makedirs(UPLOAD_DIR, exist_ok=True)

def now_ts():
    return time.strftime("%Y%m%dT%H%M%S", time.gmtime())

def parse_multipart(header, data):
    """Parse multipart/form-data manually"""
    # Extract boundary from Content-Type header
    boundary_match = re.search(r'boundary=(.+)', header)
    if not boundary_match:
        return None, None
    
    boundary = boundary_match.group(1).strip('"')
    boundary_bytes = ('--' + boundary).encode()
    
    # Find file part
    parts = data.split(boundary_bytes)
    for part in parts:
        if b'filename="' in part:
            # Extract filename
            filename_match = re.search(rb'filename="([^"]+)"', part)
            if filename_match:
                filename = filename_match.group(1).decode('utf-8', errors='ignore')
            else:
                filename = f"unnamed_{now_ts()}_{uuid.uuid4().hex}"
            
            # Extract file data (after empty line)
            file_start = part.find(b'\r\n\r\n')
            if file_start != -1:
                file_data = part[file_start+4:]
                # Remove trailing boundary
                if file_data.endswith(b'\r\n'):
                    file_data = file_data[:-2]
                return filename, file_data
    
    return None, None

class UploadHandler(BaseHTTPRequestHandler):
    server_version = "SimpleUpload/0.1"

    def _send(self, code, text):
        self.send_response(code)
        self.send_header("Content-Type", "text/plain; charset=utf-8")
        self.send_header("Content-Length", str(len(text.encode('utf-8'))))
        self.end_headers()
        self.wfile.write(text.encode('utf-8'))

    def do_POST(self):
        if self.path != "/uploads":
            self._send(404, "Not found\n")
            return

        # Basic content-length check
        cl = self.headers.get("Content-Length")
        try:
            content_length = int(cl) if cl is not None else 0
        except ValueError:
            content_length = 0

        if content_length > MAX_CONTENT_LENGTH:
            self._send(413, f"Payload too large (max {MAX_CONTENT_LENGTH} bytes)\n")
            return

        content_type = self.headers.get('Content-Type', '')
        if not content_type.startswith('multipart/form-data'):
            self._send(400, "Content-Type must be multipart/form-data\n")
            return

        # Read the data
        data = self.rfile.read(content_length)
        
        # Parse multipart data
        filename, file_data = parse_multipart(content_type, data)
        
        if filename is None or file_data is None:
            self._send(400, "No file field found (use form field name 'file')\n")
            return
        
        # Sanitize filename
        filename = os.path.basename(filename)
        if not filename:
            filename = f"unnamed_{now_ts()}_{uuid.uuid4().hex}"
        
        unique_name = f"{now_ts()}_{uuid.uuid4().hex}_{filename}"
        out_path = os.path.join(UPLOAD_DIR, unique_name)

        # Save file
        try:
            with open(out_path, "wb") as out:
                out.write(file_data)
        except Exception as e:
            self._send(500, f"Server error saving file: {e}\n")
            return

        # Log basic info
        client = self.client_address[0]
        print(f"[{now_ts()}] Received file from {client}: saved as {out_path}")
        self._send(200, f"OK saved as {unique_name}\n")

    def do_GET(self):
        # tiny healthcheck
        if self.path == "/":
            self._send(200, "SimpleUpload running\n")
        else:
            self._send(404, "Not found\n")

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    daemon_threads = True

if __name__ == "__main__":
    print(f"Starting server on {HOST}:{PORT}, uploads -> {os.path.abspath(UPLOAD_DIR)}")
    httpd = ThreadedHTTPServer((HOST, PORT), UploadHandler)
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print("Shutting down")
        httpd.server_close()
