"""
Instant Backup

Instantly backup repos to github and autogenerate licence and readme.md using lmstudio.

Save environmental variables as listed in the config below

Created by Christopher (Egrigor86)
"""

__version__ = "0.1.0"

import os
import requests
import datetime
import subprocess
import sys
import re

# ==== CONFIG ====
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN")
GITHUB_USERNAME = os.environ.get("GITHUB_USERNAME")

PRIVATE = True
LMSTUDIO_URL = "http://localhost:1234"
MODEL_NAME = "qwen2.5-coder-3b-instruct"
# =======================

if not GITHUB_TOKEN or not GITHUB_USERNAME:
    print("[!] ERROR: GITHUB_TOKEN or GITHUB_USERNAME environment variables not set.")
    sys.exit(1)

def run(cmd):
    result = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    if result.returncode != 0 and b"nothing to commit" not in result.stderr:
        print(f"[!] Error: {result.stderr.decode()}")
    return result.stdout.decode()

def chat_with_model(model, message):
    payload = {
        "model": model,
        "messages": [{"role": "user", "content": message}]
    }
    try:
        response = requests.post(f"{LMSTUDIO_URL}/v1/chat/completions", json=payload)
        response.raise_for_status()
        return response.json().get("choices", [{}])[0].get("message", {}).get("content", "")
    except Exception as e:
        print(f"[!] LMStudio error: {e}")
        return "README generation failed."

def auto_generate_readme(folder_path, repo_name):
    readme_path = os.path.join(folder_path, "README.md")
    script_name = os.path.basename(__file__)

    # Skip if README exists and has content
    if os.path.exists(readme_path) and os.path.getsize(readme_path) > 10:
        print("[~] README.md already has content. Skipping auto-generation.")
        return

    # Collect Python file contents
    code_files = [
        f for f in os.listdir(folder_path)
        if f.endswith(".py") and f != script_name
    ]
    combined_code = ""
    for file in code_files:
        with open(os.path.join(folder_path, file), "r", encoding="utf-8") as f:
            combined_code += f"\n# {file}\n" + f.read() + "\n"

    # Prompt for the model
    prompt = f"""You are an expert open-source assistant. Please read the following Python code and generate a README.md for it.

Project Name: {repo_name}

The README should include:
- A short description of what the project does
- Key features
- Requirements or dependencies (if applicable)
- How to run or use the script

Code:
{combined_code}
"""

    print("[~] Requesting README generation from LMStudio...")
    response = chat_with_model(MODEL_NAME, prompt)

    # Save result to README
    with open(readme_path, "w", encoding="utf-8") as f:
        f.write(response.strip())

    print("[+] README.md auto-generated by LMStudio.")

def auto_generate_requirements(folder_path):
    requirements_path = os.path.join(folder_path, "requirements.txt")
    script_name = os.path.basename(__file__)
    existing = set()

    if os.path.exists(requirements_path):
        with open(requirements_path, "r", encoding="utf-8") as f:
            existing = set(line.strip() for line in f if line.strip())

    found = set()
    for file in os.listdir(folder_path):
        if file.endswith(".py") and file != script_name:
            with open(os.path.join(folder_path, file), "r", encoding="utf-8") as f:
                for line in f:
                    match = re.match(r"^\s*(?:import|from)\s+([a-zA-Z0-9_]+)", line)
                    if match:
                        mod = match.group(1)
                        if mod not in ("os", "sys", "re", "datetime", "subprocess", "requests"):
                            found.add(mod)

    new_reqs = sorted(list(found - existing))
    if new_reqs:
        with open(requirements_path, "a", encoding="utf-8") as f:
            for req in new_reqs:
                f.write(req + "\n")
        print(f"[+] requirements.txt updated with: {', '.join(new_reqs)}")
    else:
        print("[~] requirements.txt is up to date.")

def create_starter_files(folder_path, repo_name):
    readme_path = os.path.join(folder_path, "README.md")
    gitignore_path = os.path.join(folder_path, ".gitignore")
    license_path = os.path.join(folder_path, "LICENSE")

    if not os.path.exists(readme_path):
        with open(readme_path, "w", encoding="utf-8") as f:
            f.write(f"# {repo_name}\n")
        print("[+] README.md created (empty)")

    if not os.path.exists(gitignore_path):
        with open(gitignore_path, "w", encoding="utf-8") as f:
            f.write("__pycache__/\n*.pyc\n.env\n.DS_Store\n*.log\n")
        print("[+] .gitignore created")

    if not os.path.exists(license_path):
        year = datetime.datetime.now().year
        license_text = f"""MIT License

Copyright (c) {year} {GITHUB_USERNAME}

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
        with open(license_path, "w", encoding="utf-8") as f:
            f.write(license_text)
        print("[+] LICENSE (MIT) created")

def create_github_repo(repo_name):
    url = "https://api.github.com/user/repos"
    headers = {"Authorization": f"token {GITHUB_TOKEN}"}
    data = {"name": repo_name, "private": PRIVATE}
    r = requests.post(url, headers=headers, json=data)
    if r.status_code == 201:
        print(f"[✓] GitHub repo '{repo_name}' created.")
        return True
    elif r.status_code == 422 and "already exists" in r.text:
        print(f"[~] Repo already exists. Skipping creation.")
        return True
    else:
        print(f"[!] GitHub repo creation failed: {r.text}")
        return False

def init_or_use_repo(folder_path, repo_name):
    os.chdir(folder_path)
    if not os.path.isdir(".git"):
        print("[~] No git repo found. Initializing...")
        run("git init")
        run(f"git config user.name \"{GITHUB_USERNAME}\"")
        run(f"git config user.email \"{GITHUB_USERNAME}@users.noreply.github.com\"")
        run(f"git remote add origin https://{GITHUB_TOKEN}@github.com/{GITHUB_USERNAME}/{repo_name}.git")
        run("git branch -M main")
    else:
        print("[✓] Git repo already initialized.")

def commit_and_push(repo_name):
    run("git add .")
    timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    run(f"git commit -m \"Auto-backup at {timestamp}\"")
    run("git push -u origin main")

def main():
    folder_path = os.getcwd()
    repo_name = os.path.basename(folder_path.rstrip('/\\'))

    print(f"[~] Preparing to back up: {folder_path}")

    auto_generate_readme(folder_path, repo_name)
    create_starter_files(folder_path, repo_name)
    auto_generate_requirements(folder_path)

    if create_github_repo(repo_name):
        init_or_use_repo(folder_path, repo_name)
        commit_and_push(repo_name)
        print(f"[✓] Backup pushed to GitHub '{repo_name}' successfully.")

if __name__ == "__main__":
    main()