#!/usr/bin/env python3
"""autoproof.py — Auto-fill `sorry` blocks in Lean using phi4 on neon-64gb.

Usage:
  python3 scripts/autoproof.py
  python3 scripts/autoproof.py --file formal/CoreFormalism/CRTSidon.lean
  python3 scripts/autoproof.py --all  # fill all sorries in the file
"""
import os
import sys
import re
import json
import subprocess
import urllib.request

NEON_API = "http://100.92.88.64:8766/generate"
SILVERSIGHT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

def call_phi4(prompt, timeout=600):
    """Call phi4 on neon via the LeanCopilot-compatible server."""
    body = json.dumps({
        "name": "phi4:14b",
        "input": prompt,
    }).encode()
    req = urllib.request.Request(NEON_API, data=body,
        headers={"Content-Type": "application/json"})
    resp = urllib.request.urlopen(req, timeout=timeout)
    data = json.loads(resp.read())
    return data["outputs"][0]["text"]

def extract_lean(text):
    """Extract Lean code from LLM response (strip markdown fences)."""
    text = text.strip()
    if text.startswith("```"):
        # Remove first ``` line
        text = text.split("\n", 1)[1] if "\n" in text else text
        # Remove trailing ```
        text = text.rsplit("```", 1)[0].strip()
    # Remove leading "lean" or "lean4" language tag
    if text.startswith("lean"):
        text = text[4:].strip()
    # Remove theorem header if it was repeated
    lines = text.split("\n")
    cleaned = []
    in_proof = False
    for line in lines:
        if ":= by" in line:
            # Remove everything up to and including `:= by`
            line = line.split(":= by", 1)[1].strip()
            in_proof = True
        if in_proof:
            cleaned.append(line)
    if cleaned:
        text = "\n".join(cleaned)
    return text.strip()

def find_sorry_context(filepath):
    """Find the first `sorry` and return its context."""
    with open(filepath) as f:
        content = f.read()
    
    # Find the theorem/lemma containing the first sorry
    lines = content.split("\n")
    theorem_start = None
    sorry_line = None
    
    for i, line in enumerate(lines):
        if re.match(r'\s*(theorem|lemma)\s', line):
            theorem_start = i
        if "sorry" in line and theorem_start is not None:
            sorry_line = i
            break
    
    if sorry_line is None:
        return content, None, content  # no sorries
    
    # Extract the theorem block (from theorem to the sorry)
    context_lines = lines[theorem_start:sorry_line + 1]
    context = "\n".join(context_lines)
    return content, context, content

def replace_sorry(filepath, proof_text):
    """Replace the first `sorry` with proof_text."""
    with open(filepath) as f:
        content = f.read()
    
    # Replace the first occurrence of "  sorry" with the proof
    # We need proper indentation
    new_content = content.replace("  sorry", f"  {proof_text}", 1)
    
    with open(filepath, "w") as f:
        f.write(new_content)
    return new_content

def lake_build(target="CoreFormalism.CRTSidon"):
    """Run lake build and return (success, output)."""
    result = subprocess.run(
        ["lake", "build", target],
        cwd=SILVERSIGHT,
        capture_output=True, text=True,
        timeout=300,
    )
    output = result.stdout + result.stderr
    success = result.returncode == 0
    return success, output

def main():
    filepath = sys.argv[sys.argv.index("--file") + 1] if "--file" in sys.argv else \
        "formal/CoreFormalism/CRTSidon.lean"
    fill_all = "--all" in sys.argv
    
    full_path = os.path.join(SILVERSIGHT, filepath)
    print(f"=== AutoProof: {filepath} ===")
    
    content, context, _ = find_sorry_context(full_path)
    if context is None:
        print("No sorries found.")
        return
    
    sorry_count = content.count("sorry")
    print(f"Found {sorry_count} sorry/s.")
    
    while True:
        content, context, original = find_sorry_context(full_path)
        if context is None:
            print("All sorries filled!")
            break
        
        print(f"\n--- Filling sorry ---")
        print(f"Context:\n{context}\n")
        
        # Build prompt
        prompt = (
            "Fill the `sorry` in this Lean 4 theorem. "
            "Output ONLY the proof body that replaces `sorry` after `:= by`. "
            "No markdown, no theorem header, no commentary.\n\n"
            f"{context}\n\nProof body:"
        )
        
        # Call phi4 on neon
        print("Calling phi4 on neon-64gb...", flush=True)
        response = call_phi4(prompt)
        proof = extract_lean(response)
        
        if not proof:
            print("Empty proof from LLM, retrying...")
            continue
        
        print(f"Generated proof ({len(proof)} chars): {proof[:100]}...")
        
        # Save original and apply
        replace_sorry(full_path, proof)
        
        # Build
        print("Running lake build...", flush=True)
        success, output = lake_build()
        
        if success:
            print("  PASSED!")
            subprocess.run(["git", "add", "-f", filepath], cwd=SILVERSIGHT)
            subprocess.run(
                ["git", "commit", "-m", f"autoproof: filled sorry in {filepath}"],
                cwd=SILVERSIGHT,
            )
            print("  Committed.")
            if not fill_all:
                break
        else:
            print("  FAILED. Restoring original...")
            with open(full_path, "w") as f:
                f.write(original)
            print("  Restored.")
            # Could retry with different prompt
            break
    
    print("\n=== Done ===")

if __name__ == "__main__":
    main()
