import os
import time
import urllib

import modal
from suno_utils.worker.modal_base import get_modal_base_image

vllm_image = (
    get_modal_base_image()
    .pip_install(
        "vllm==0.7.2",
        "huggingface_hub[hf_transfer]==0.26.2",
        "flashinfer-python==0.2.0.post2",  # pinning, very unstable
        extra_index_url="https://flashinfer.ai/whl/cu124/torch2.5",
    )
    .env({"HF_HUB_ENABLE_HF_TRANSFER": "1"})  # faster model transfers
    .add_local_python_source("suno_utils", copy=False)
)


MODEL_DIR = "llamas"

hf_cache_vol = modal.Volume.from_name("huggingface-cache", create_if_missing=True)
vllm_cache_vol = modal.Volume.from_name("vllm-cache", create_if_missing=True)

llamas_vol = modal.Volume.from_name(MODEL_DIR, create_if_missing=False)
app = modal.App("lyrics-gen")
ADAPTER_PATH = f"/root/{MODEL_DIR}/llama3-1-8b-lora-dpo-beta0p01-gradient-acc-lr3/epoch_2"
N_GPU = 1  # tip: for best results, first upgrade to more powerful GPUs, and only then increase GPU count
MAX_LORA_RANK = 32
MINUTES = 60  # seconds

VLLM_PORT = 8000

BASE_MODEL_NAME = "meta-llama/Llama-3.1-8B-Instruct"
FT_MODEL_NAME = "my-remi-8b-1"
SERVER_API_KEY = "sunosunosuno"


@app.function(
    image=vllm_image,
    gpu=f"H100:{N_GPU}",
    # how long should we stay up with no requests?
    scaledown_window=15 * MINUTES,
    volumes={
        "/root/.cache/huggingface": hf_cache_vol,
        "/root/.cache/vllm": vllm_cache_vol,
        "/root/llamas": llamas_vol,
    },
    secrets=[modal.Secret.from_name("huggingface-secret-suno")],
)
@modal.concurrent(max_inputs=100)
@modal.web_server(port=VLLM_PORT, startup_timeout=5 * MINUTES)
def serve():
    import subprocess

    cmd = [
        "vllm",
        "serve",
        BASE_MODEL_NAME,
        "--enable-lora",
        "--lora-modules",
        f"{FT_MODEL_NAME}={ADAPTER_PATH}",
        "--api-key",
        SERVER_API_KEY,
        "--max-lora-rank",
        str(MAX_LORA_RANK),
        "--host",
        "0.0.0.0",
        "--port",
        str(VLLM_PORT),
    ]
    subprocess.Popen(" ".join(cmd), shell=True)


def perform_health_check(url, test_timeout=1 * MINUTES):
    print(f"Running health check for server at {url}")
    up, start, delay = False, time.time(), 10
    full_url = str(os.path.join(url, "health"))
    print("hitting:", full_url)
    while not up:
        try:
            with urllib.request.urlopen(full_url) as response:
                if response.getcode() == 200:
                    up = True
        except Exception as e:
            print(f"Got exception: {e} while performing server health check")
            if time.time() - start > test_timeout:
                break
            time.sleep(delay)

    assert up, f"Failed health check for server at {url}"

    print(f"Successful health check for server at {url}")


@app.local_entrypoint()
def test(test_timeout=5 * MINUTES):
    import json
    import urllib

    import openai

    perform_health_check(serve.web_url)

    messages = [
        {
            "role": "system",
            "content": "You are an expert songwriter.  Write a song based on the following prompt:",
        },
        {"role": "user", "content": "a heart-rending country song about a literal banana"},
    ]
    print(f"Sending a sample message to {serve.web_url}", *messages, sep="\n")

    headers = {
        "Authorization": f"Bearer {SERVER_API_KEY}",
        "Content-Type": "application/json",
    }
    payload = json.dumps({"messages": messages, "model": FT_MODEL_NAME})
    req = urllib.request.Request(
        serve.web_url + "/v1/chat/completions",
        data=payload.encode("utf-8"),
        headers=headers,
        method="POST",
    )
    with urllib.request.urlopen(req) as response:
        payload = json.loads(response.read().decode())
        lyrics = payload["choices"][0]["message"]["content"]
        print(payload)
        print(lyrics)

    client = openai.OpenAI(base_url=str(serve.web_url) + "/v1", api_key=SERVER_API_KEY)
    resp = client.chat.completions.create(
        model=FT_MODEL_NAME,
        messages=[
            {"role": "system", "content": "You are a cool assistant"},
            {"role": "user", "content": "Hi, is this thing on?"},
        ],
    )
    print(resp)
