import os, json, uuid, shutil, asyncio, time
from pathlib import Path
from dotenv import load_dotenv

from fastapi import FastAPI, HTTPException, UploadFile
from fastapi.responses import FileResponse, PlainTextResponse
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel

from vidmaker import make_synced_video
from tiktok_utils import download_tiktok, extract_audio, transcribe_with_replicate
from suno_utils import audio_to_suno_song , get_aligned_lyrics# For deprecated /api/full endpoint
from suno_step_by_step_utils import upload_audio_step_by_step, upload_and_remix_step_by_step, StepLogger
from suno_vocal_cover_utils import upload_and_create_vocal_cover_v45

load_dotenv()

BASE   = Path(__file__).parent
FRONT  = BASE.parent / "frontend"
STORE  = BASE / "storage"; STORE.mkdir(exist_ok=True)

app = FastAPI(title="mvmaker one‑click")
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])

# ─── helpers ─────────────────────────────────────────────────────
JOBS: dict[str, dict] = {}

def jlog(jid:str,msg:str):
    p=STORE/jid/"log.txt"; p.parent.mkdir(parents=True,exist_ok=True)
    with p.open("a") as f: f.write(f"[{jid}] {msg}\n")
    print(f"[{jid}] {msg}")
    JOBS.setdefault(jid,{}).update(note=msg)

def jerr(jid:str,e:Exception):
    import traceback, textwrap; traceback.print_exc()
    JOBS[jid].update(status="error",error=f"{type(e).__name__}: {e}")
    jlog(jid,f"ERROR {e}")

# ─── models ──────────────────────────────────────────────────────
class FullReq(BaseModel):          # one‑click body
    url: str                       # TikTok URL
    prompt: str = "lo‑fi beat"     # Suno style/genre

class JobStatus(BaseModel):
    id:str; status:str
    output:str|None=None; sync:str|None=None
    error:str|None=None; note:str|None=None
    clips:list|None=None; transcripts:dict|None=None

class SunoUploadReq(BaseModel):
    file_path: str  # Path to local WAV file for testing
    
class SunoRemixReq(BaseModel):
    file_path: str  # Path to local WAV file for testing
    prompt: str = "dubstep, heavy bass"  # Style/genre tags
    audio_weight: float = 0.8  # Audio influence (0-1)

# ─── routes ──────────────────────────────────────────────────────
@app.api_route("/api/ping",methods=["GET","POST"])
async def ping(): return {"ok":True}

def hoot_to_transcript(hoot_json):
    transcript = ""
    for t in hoot_json:
        transcript += t["text"]
    return transcript
@app.post("/api/full/v2", response_model=JobStatus)
async def full_v2(req: FullReq):
    
    """Enhanced TikTok → Suno → synced video pipeline with cover generation"""
    jid = str(uuid.uuid4())
    jdir = STORE / jid
    jdir.mkdir(parents=True, exist_ok=True)
    JOBS[jid] = {"status": "processing", "note": "queued"}
    
    async def run():
        env_vars = dict(REPLICATE_API_TOKEN="r8_HGsdsEyz339nplinKh9Ex5Se2mxM8Sc1lbdu4",
        WHISPER_MODEL="villesau/whisper-timestamped:c5b122b7e513b1b5a6ef849891c538869b77cc932cbd0f8203e11d3b357553b8",
        SUNO_TOKEN="ae6cb0480e2e493cbab61a7324030ec5",
        SUNO_DEBUG="1")
        for k, v in env_vars.items():
            os.environ[k] = v
        try:
          
            # # 1) TikTok download
            jlog(jid, "Downloading TikTok...")
            mp4 = await asyncio.to_thread(download_tiktok, req.url, jdir)
            (jdir / "video_path.txt").write_text(mp4)

            # # 2) extract WAV
            wav = jdir / "audio.wav"
            jlog(jid, "Extracting audio...")
            await asyncio.to_thread(extract_audio, mp4, str(wav))
            (jdir / "audio_path.txt").write_text(str(wav))

            # #upload clip
            clip_id, _ = upload_audio_step_by_step(wav, jdir / "generation_log.txt")
            # get transcript
           # clip_id = "e89a99eb-0533-4a68-a50f-f0242645a4c2"
            hoot_json = get_aligned_lyrics(clip_id)
            
            transcript = hoot_to_transcript(hoot_json["aligned_lyrics"])
            (jdir / "video.json").write_text(json.dumps(hoot_json, indent=2))
            # 3) Generate ONE song request (Suno returns 2 clips)
            song = jdir / "suno.wav"
            prompt = req.prompt if req.prompt else "dubstep, heavy bass"
          
            # if transcript_text.strip():
            # Use v4.5 vocal cover approach
            jlog(jid, f"Generating vocal cover with v4.5: style='{prompt}', audio_weight=1.0")
            result, _ =upload_and_create_vocal_cover_v45(clip_id,
                transcript,
                prompt,  # Basic genre tags
                jdir / "generation_log.txt",
                0.7  # 100% audio strength
            )
            # else:
            #     # Fall back to instrumental remix
            #     jlog(jid, f"Generating instrumental remix: prompt='{prompt}', audio_weight=1.0")
            #     result, _ = await asyncio.to_thread(
            #         upload_and_remix_step_by_step,
            #         wav,
            #         prompt,
            #         jdir / "generation_log.txt",
            #         1.0  # 100% weight
            #     )
            
            # Suno returns 2 clips, we'll use the first one for sync
            
            upload_id = result["upload_clip_id"]
            clip_id = result["generated_clips"][0]["id"]
            jlog(jid, f"Generation complete! Got {len(result['generated_clips'])} clips")
            jlog(jid, f"Using first clip for sync: {clip_id}")
            
            # Download the first clip
            from suno_download import download_suno_clip
            if not download_suno_clip(clip_id, song):
                jlog(jid, f"WARNING: Failed to download clip, saving ID")
                song.write_text(f"CLIP_ID: {clip_id}\n")
                assert False

            # 4) Transcribe the generated song (first clip)
            jlog(jid, "Transcribing generated song...")
            sjson = get_aligned_lyrics(clip_id, post=False)
            (jdir / "song.json").write_text(json.dumps(sjson, indent=2))
          

            # 5) Make synced video
            out = jdir / "output.mp4"
            sync = jdir / "combined.json"
            jlog(jid, "Building synced video...")
            make_synced_video(
                clip_path=mp4,              # Original TikTok video
                song_path=str(song),        # Generated Suno audio (first clip)
                video_json_path=str(jdir / "video.json"),   # Original transcript
                song_json_path=str(jdir / "song.json"),     # Generated song transcript
                out_path=str(out),          # Output video
                export_sync=str(sync),      # Sync data export
                smooth=3                    # Smoothing window
            )

            # Update job status
            JOBS[jid].update({
                "status": "done",
                "output": f"/api/download/{jid}",
                "sync": f"/api/sync/{jid}",
                "note": "✅ Video sync complete!"
            })
            jlog(jid, "✅ Finished!")
            
        except Exception as e:
            jerr(jid, e)
            
    asyncio.create_task(run())
    return JobStatus(id=jid, status="processing")

@app.get("/api/status/{jid}",response_model=JobStatus)
def status(jid:str):
    if jid not in JOBS: raise HTTPException(404,"job")
    
    # Add clip info if available
    job_data = JOBS[jid].copy()
    jdir = STORE / jid
    
    # Try to get clip IDs from generation logs
    clips = []
    for log_name in ["generation1_log.txt", "generation2_log.txt"]:
        log_file = jdir / log_name
        if log_file.exists():
            content = log_file.read_text()
            import re
            # Find clip IDs in the log
            matches = re.findall(r'Generated clip[^:]*: ([a-f0-9-]{36})', content)
            if not matches:
                # Try another pattern
                matches = re.findall(r'"id":\s*"([a-f0-9-]{36})"', content)[:2]
            for clip_id in matches:
                clips.append({
                    "id": clip_id,
                    "url": f"https://cdn1.suno.ai/{clip_id}.mp3"
                })
    
    if clips:
        job_data["clips"] = clips
    
    # Add paths to transcription files
    transcripts = {}
    for name in ["video.json", "song.json", "cover_song.json"]:
        path = jdir / name
        if path.exists():
            transcripts[name] = f"/api/transcript/{jid}/{name}"
    
    if transcripts:
        job_data["transcripts"] = transcripts
    
    return JobStatus(id=jid,**job_data)

@app.get("/api/log/{jid}")
def log(jid:str):
    p=STORE/jid/"log.txt"
    if not p.exists(): return PlainTextResponse("",media_type="text/plain")
    return FileResponse(p,media_type="text/plain")

@app.get("/api/download/{jid}")
def dl(jid:str, type: str = None):
    if type == "cover":
        p = STORE/jid/"output_cover.mp4"
        filename = "synced_cover.mp4"
    else:
        p = STORE/jid/"output.mp4"
        filename = "synced.mp4"
    
    if not p.exists(): 
        raise HTTPException(404, "mp4")
    return FileResponse(p, filename=filename, media_type="video/mp4")

@app.get("/api/sync/{jid}")
def sync(jid:str, type: str = None):
    if type == "cover":
        p = STORE/jid/"combined_cover.json"
        filename = "combined_cover.json"
    else:
        p = STORE/jid/"combined.json"
        filename = "combined.json"
    
    if not p.exists(): 
        raise HTTPException(404, "json")
    return FileResponse(p, media_type="application/json", filename=filename)

@app.get("/api/debug/{jid}")
def get_debug_info(jid: str):
    """Get debug information including clip IDs and audio paths"""
    if jid not in JOBS:
        raise HTTPException(404, "Job not found")
    
    jdir = STORE / jid
    debug_info = {
        "job_id": jid,
        "status": JOBS[jid].get("status"),
        "clips": [],
        "audio_files": []
    }
    
    # Find clip IDs from logs
    gen1_log = jdir / "generation1_log.txt"
    gen2_log = jdir / "generation2_log.txt"
    
    for log_file in [gen1_log, gen2_log]:
        if log_file.exists():
            content = log_file.read_text()
            # Extract clip IDs from logs
            import re
            clip_ids = re.findall(r'"id":\s*"([a-f0-9-]{36})"', content)
            for clip_id in clip_ids[:2]:  # First 2 are the generated clips
                debug_info["clips"].append({
                    "id": clip_id,
                    "audio_url": f"https://cdn1.suno.ai/{clip_id}.mp3"
                })
    
    # List audio files
    for audio_file in jdir.glob("*.wav"):
        debug_info["audio_files"].append({
            "name": audio_file.name,
            "path": str(audio_file)
        })
    
    return debug_info

@app.post("/api/transcribe_audio")
async def transcribe_audio_file(req: dict):
    """Transcribe an audio file using Whisper"""
    audio_path = req.get("audio_path")
    if not audio_path:
        raise HTTPException(400, "audio_path required")
    
    audio_file = Path(audio_path)
    if not audio_file.exists():
        raise HTTPException(404, "Audio file not found")
    
    try:
        # Use our existing Whisper transcription
        transcript = await asyncio.to_thread(transcribe_with_replicate, str(audio_file))
        
        # Format transcript for display
        formatted_segments = []
        full_text = ""
        
        for seg in transcript.get("segments", []):
            formatted_segments.append({
                "start": seg.get("start", 0),
                "end": seg.get("end", 0),
                "text": seg.get("text", "")
            })
            full_text += seg.get("text", "") + " "
        
        return {
            "success": True,
            "full_text": full_text.strip(),
            "segments": formatted_segments,
            "audio_path": audio_path
        }
    except Exception as e:
        return {
            "success": False,
            "error": str(e),
            "audio_path": audio_path
        }

@app.post("/api/suno_upload_steps")
async def suno_upload_steps(req: SunoUploadReq):
    """Step-by-step Suno upload with detailed logging"""
    jid = str(uuid.uuid4())
    jdir = STORE / jid
    jdir.mkdir(parents=True, exist_ok=True)
    
    log_file = jdir / "suno_upload_log.txt"
    JOBS[jid] = {"status": "processing", "note": "Starting Suno upload..."}
    
    async def run():
        try:
            # Convert string path to Path object
            audio_path = Path(req.file_path)
            
            if not audio_path.exists():
                raise FileNotFoundError(f"Audio file not found: {audio_path}")
            
            # Run the step-by-step upload
            clip_id, logger = await asyncio.to_thread(
                upload_audio_step_by_step,
                audio_path,
                log_file
            )
            
            JOBS[jid].update({
                "status": "done",
                "clip_id": clip_id,
                "note": f"Upload complete! Clip ID: {clip_id}",
                "logs": logger.get_logs()
            })
            
        except Exception as e:
            import traceback
            traceback.print_exc()
            JOBS[jid].update({
                "status": "error",
                "error": f"{type(e).__name__}: {e}",
                "note": "Upload failed"
            })
    
    asyncio.create_task(run())
    return {"job_id": jid, "status": "processing"}

@app.get("/api/suno_upload_logs/{jid}")
def get_suno_logs(jid: str):
    """Get logs for a Suno upload job"""
    if jid not in JOBS:
        raise HTTPException(404, "Job not found")
    
    job = JOBS[jid]
    log_file = STORE / jid / "suno_upload_log.txt"
    
    # Read log file if exists
    logs_text = ""
    if log_file.exists():
        logs_text = log_file.read_text()
    
    return {
        "job_id": jid,
        "status": job.get("status"),
        "clip_id": job.get("clip_id"),
        "error": job.get("error"),
        "note": job.get("note"),
        "logs": logs_text,
        "logs_structured": job.get("logs", [])
    }

@app.post("/api/suno_upload_remix")
async def suno_upload_remix(req: SunoRemixReq):
    """Step-by-step Suno upload + remix with detailed logging"""
    jid = str(uuid.uuid4())
    jdir = STORE / jid
    jdir.mkdir(parents=True, exist_ok=True)
    
    log_file = jdir / "suno_remix_log.txt"
    JOBS[jid] = {"status": "processing", "note": "Starting Suno upload + remix..."}
    
    async def run():
        try:
            # Convert string path to Path object
            audio_path = Path(req.file_path)
            
            if not audio_path.exists():
                raise FileNotFoundError(f"Audio file not found: {audio_path}")
            
            # Run the step-by-step upload + remix
            result, logger = await asyncio.to_thread(
                upload_and_remix_step_by_step,
                audio_path,
                req.prompt,
                log_file,
                req.audio_weight
            )
            
            JOBS[jid].update({
                "status": "done",
                "upload_clip_id": result["upload_clip_id"],
                "generation_id": result["generation_id"],
                "generated_clips": result["generated_clips"],
                "total_time": result["total_time"],
                "note": f"Remix complete! Generated {len(result['generated_clips'])} clips",
                "logs": logger.get_logs()
            })
            
        except Exception as e:
            import traceback
            traceback.print_exc()
            JOBS[jid].update({
                "status": "error",
                "error": f"{type(e).__name__}: {e}",
                "note": "Remix failed"
            })
    
    asyncio.create_task(run())
    return {"job_id": jid, "status": "processing"}

@app.post("/api/test/step1_download")
async def test_step1_download(req: dict):
    """Step 1: Download TikTok and extract audio"""
    jid = str(uuid.uuid4())
    jdir = STORE / jid
    jdir.mkdir(parents=True, exist_ok=True)
    
    test_url = req.get("url", "https://www.tiktok.com/@ethan.stee1e/video/7529957148734786838")
    
    async def run():
        try:
            # Download TikTok
            jlog(jid, "Step 1: Downloading TikTok...")
            mp4 = await asyncio.to_thread(download_tiktok, test_url, jdir)
            jlog(jid, f"Downloaded to: {mp4}")
            
            # Extract audio
            wav = jdir / "audio.wav"
            jlog(jid, "Step 1: Extracting audio...")
            await asyncio.to_thread(extract_audio, mp4, str(wav))
            jlog(jid, f"Audio extracted to: {wav}")
            
            # Save path for step 2
            (jdir / "audio_path.txt").write_text(str(wav))
            
            JOBS[jid] = {
                "status": "done",
                "audio_path": str(wav),
                "note": "Step 1 complete: Audio ready for upload"
            }
            
        except Exception as e:
            import traceback
            traceback.print_exc()
            JOBS[jid] = {
                "status": "error",
                "error": f"{type(e).__name__}: {e}",
                "note": "Step 1 failed"
            }
    
    asyncio.create_task(run())
    return {"job_id": jid, "status": "processing"}

@app.post("/api/test/step2_suno_exact")
async def test_step2_suno_exact(req: dict):
    """Step 2: Upload to Suno using EXACT same method as working remix"""
    jid = str(uuid.uuid4())
    jdir = STORE / jid
    jdir.mkdir(parents=True, exist_ok=True)
    
    audio_path = req.get("audio_path", "/Users/matthewgordon/Desktop/mvmaker/backend/wobblywiggly_vox.wav")
    prompt = req.get("prompt", "house music")
    
    log_file = jdir / "suno_test_log.txt"
    JOBS[jid] = {"status": "processing", "note": "Starting Suno test..."}
    
    async def run():
        try:
            # Use EXACT same function as working remix
            result, logger = await asyncio.to_thread(
                upload_and_remix_step_by_step,
                Path(audio_path),
                prompt,
                log_file,
                1.0  # 100% audio weight
            )
            
            JOBS[jid].update({
                "status": "done",
                "upload_clip_id": result["upload_clip_id"],
                "generation_id": result["generation_id"],
                "generated_clips": result["generated_clips"],
                "total_time": result["total_time"],
                "note": f"Success! Generated {len(result['generated_clips'])} clips",
                "logs": logger.get_logs()
            })
            
        except Exception as e:
            import traceback
            traceback.print_exc()
            JOBS[jid].update({
                "status": "error",
                "error": f"{type(e).__name__}: {e}",
                "note": "Suno test failed"
            })
    
    asyncio.create_task(run())
    return {"job_id": jid, "status": "processing"}

@app.post("/api/test/exact_working_test")
async def test_exact_working():
    """Test with exact same parameters as working example"""
    jid = str(uuid.uuid4())
    jdir = STORE / jid
    jdir.mkdir(parents=True, exist_ok=True)
    
    # Use EXACT same file as working example
    test_audio = Path("/Users/matthewgordon/Desktop/mvmaker/backend/wobblywiggly_vox.wav")
    
    if not test_audio.exists():
        return {"error": "Test audio file not found"}
    
    log_file = jdir / "test_log.txt"
    JOBS[jid] = {"status": "processing", "note": "Testing with exact working parameters..."}
    
    async def run():
        try:
            # Use EXACT parameters from working example
            result, logger = await asyncio.to_thread(
                upload_and_remix_step_by_step,
                test_audio,
                "dubstep",  # Exact prompt from working example
                log_file,
                0.8  # Exact audio weight from working example
            )
            
            JOBS[jid].update({
                "status": "done",
                "result": result,
                "note": f"Success! Check if audio contains original"
            })
            
        except Exception as e:
            import traceback
            traceback.print_exc()
            JOBS[jid].update({
                "status": "error",
                "error": f"{type(e).__name__}: {e}"
            })
    
    asyncio.create_task(run())
    return {"job_id": jid, "status": "processing"}

@app.get("/api/test/status/{jid}")
def test_status(jid: str):
    """Get test job status"""
    if jid not in JOBS:
        raise HTTPException(404, "Job not found")
    return JOBS[jid]

@app.post("/api/test/overpainting")
async def test_overpainting(req: dict):
    """Test overpainting approach for vocal preservation"""
    jid = str(uuid.uuid4())
    jdir = STORE / jid
    jdir.mkdir(parents=True, exist_ok=True)
    
    audio_path = req.get("audio_path", "/Users/matthewgordon/Desktop/mvmaker/backend/wobblywiggly_vox.wav")
    transcript = req.get("transcript", "Test transcript for vocal preservation")
    style_tags = req.get("style_tags", "house music")
    
    JOBS[jid] = {"status": "processing", "note": "Starting overpainting test..."}
    
    async def run():
        try:
            jlog(jid, f"Testing overpainting with audio: {audio_path}")
            jlog(jid, f"Transcript: {transcript[:100]}..." if len(transcript) > 100 else f"Transcript: {transcript}")
            jlog(jid, f"Style tags: {style_tags}")
            
            result, logger = await asyncio.to_thread(
                upload_and_overpaint_with_transcript,
                Path(audio_path),
                transcript,
                style_tags,
                jdir / "overpainting_log.txt",
                1.0  # 100% audio weight
            )
            
            JOBS[jid].update({
                "status": "done",
                "upload_clip_id": result["upload_clip_id"],
                "generation_id": result["generation_id"],
                "generated_clips": result["generated_clips"],
                "total_time": result["total_time"],
                "note": f"Success! Generated {len(result['generated_clips'])} clips"
            })
            
            # Log clip details
            for clip in result["generated_clips"]:
                jlog(jid, f"Generated clip {clip['id']}: task={clip.get('metadata', {}).get('task')}, has_vocal={clip.get('metadata', {}).get('has_vocal')}")
            
        except Exception as e:
            import traceback
            traceback.print_exc()
            JOBS[jid].update({
                "status": "error",
                "error": f"{type(e).__name__}: {e}",
                "note": "Overpainting test failed"
            })
    
    asyncio.create_task(run())
    return {"job_id": jid, "status": "processing"}

@app.get("/api/suno_remix_logs/{jid}")
def get_suno_remix_logs(jid: str):
    """Get logs for a Suno remix job"""
    if jid not in JOBS:
        raise HTTPException(404, "Job not found")
    
    job = JOBS[jid]
    log_file = STORE / jid / "suno_remix_log.txt"
    
    # Read log file if exists
    logs_text = ""
    if log_file.exists():
        logs_text = log_file.read_text()
    
    return {
        "job_id": jid,
        "status": job.get("status"),
        "upload_clip_id": job.get("upload_clip_id"),
        "generation_id": job.get("generation_id"),
        "generated_clips": job.get("generated_clips"),
        "total_time": job.get("total_time"),
        "error": job.get("error"),
        "note": job.get("note"),
        "logs": logs_text,
        "logs_structured": job.get("logs", [])
    }

@app.get("/api/audio/{jid}/{filename}")
def serve_audio(jid: str, filename: str):
    """Serve audio files from job storage"""
    audio_path = STORE / jid / filename
    if not audio_path.exists() or not audio_path.suffix in [".wav", ".mp3"]:
        raise HTTPException(404, "Audio file not found")
    
    media_type = "audio/wav" if audio_path.suffix == ".wav" else "audio/mpeg"
    return FileResponse(audio_path, media_type=media_type)

@app.get("/api/transcript/{jid}/{filename}")
def serve_transcript(jid: str, filename: str):
    """Serve transcript JSON files"""
    if not filename.endswith(".json"):
        raise HTTPException(400, "Only JSON files allowed")
    
    transcript_path = STORE / jid / filename
    if not transcript_path.exists():
        raise HTTPException(404, "Transcript file not found")
    
    return FileResponse(transcript_path, media_type="application/json")

# Debug endpoints
@app.post("/api/debug/test_sync_data")
async def debug_test_sync_data(req: dict):
    """Test sync compatibility with JSON data directly"""
    video_transcript = req.get("video_transcript")
    song_transcript = req.get("song_transcript")
    
    if not video_transcript or not song_transcript:
        raise HTTPException(400, "Both transcript data required")
    
    try:
        # Extract language info
        video_language = video_transcript.get("language", "unknown")
        song_language = song_transcript.get("output", {}).get("language", song_transcript.get("language", "unknown"))
        
        # Get word counts and debug info
        from vidmaker import _flatten_words, _norm
        
        # Handle both standard Whisper format and Replicate format
        if "output" in song_transcript:
            # Replicate format
            v_words = _flatten_words({"output": video_transcript})
            s_words = _flatten_words(song_transcript)
        else:
            # Standard Whisper format
            v_words = _flatten_words({"output": video_transcript})
            s_words = _flatten_words({"output": song_transcript})
        
        # Normalize words
        for lst in (v_words, s_words):
            for d in lst:
                d["norm"] = _norm(d["raw"])
        
        video_words = len(v_words)
        song_words = len(s_words)
        
        # Find matching words
        v_norm_words = set(w["norm"] for w in v_words if w["norm"])
        s_norm_words = set(w["norm"] for w in s_words if w["norm"])
        matching_words = len(v_norm_words & s_norm_words)
        
        # Try to build sync anchors
        anchor_count = 0
        debug_info = ""
        
        try:
            from vidmaker import build_combined_sync
            anchors = build_combined_sync(video_transcript, song_transcript)
            anchor_count = len(anchors)
            debug_info = f"✅ Sync successful! Found {anchor_count} anchors.\n"
            debug_info += f"First few anchors:\n"
            for i, anchor in enumerate(anchors[:3]):
                debug_info += f"  {i+1}. Song {anchor['songStart']:.2f}s-{anchor['songEnd']:.2f}s → Video {anchor['videoStart']:.2f}s-{anchor['videoEnd']:.2f}s\n"
        except Exception as e:
            debug_info = f"❌ Sync failed: {str(e)}\n"
            debug_info += f"\nDetailed analysis:\n"
            debug_info += f"Video language: {video_language}\n"
            debug_info += f"Song language: {song_language}\n"
            debug_info += f"Video words (first 10): {[w['raw'] for w in v_words[:10]]}\n"
            debug_info += f"Song words (first 10): {[w['raw'] for w in s_words[:10]]}\n"
            debug_info += f"Video normalized (first 10): {[w['norm'] for w in v_words[:10]]}\n"
            debug_info += f"Song normalized (first 10): {[w['norm'] for w in s_words[:10]]}\n"
            debug_info += f"Common words: {list(v_norm_words & s_norm_words)[:20]}\n"
        
        return {
            "video_language": video_language,
            "song_language": song_language,
            "video_words": video_words,
            "song_words": song_words,
            "matching_words": matching_words,
            "anchor_count": anchor_count,
            "debug_info": debug_info
        }
        
    except Exception as e:
        import traceback
        traceback.print_exc()
        raise HTTPException(500, f"Debug test failed: {str(e)}")

@app.post("/api/debug/test_sync")
async def debug_test_sync(req: dict):
    """Test sync compatibility between two transcript files"""
    video_transcript_path = req.get("video_transcript_path")
    song_transcript_path = req.get("song_transcript_path")
    
    if not video_transcript_path or not song_transcript_path:
        raise HTTPException(400, "Both transcript paths required")
    
    try:
        # Load transcript files
        import json
        from pathlib import Path
        
        video_path = Path(video_transcript_path)
        song_path = Path(song_transcript_path)
        
        if not video_path.exists():
            raise HTTPException(404, f"Video transcript not found: {video_path}")
        if not song_path.exists():
            raise HTTPException(404, f"Song transcript not found: {song_path}")
        
        with open(video_path) as f:
            video_json = json.load(f)
        with open(song_path) as f:
            song_json = json.load(f)
        
        # Extract language info
        video_language = video_json.get("language", "unknown")
        song_language = song_json.get("output", {}).get("language", song_json.get("language", "unknown"))
        
        # Get word counts and debug info
        from vidmaker import _flatten_words, _norm
        
        # Handle both standard Whisper format and Replicate format
        if "output" in song_json:
            # Replicate format
            v_words = _flatten_words({"output": video_json})
            s_words = _flatten_words(song_json)
        else:
            # Standard Whisper format
            v_words = _flatten_words({"output": video_json})
            s_words = _flatten_words({"output": song_json})
        
        # Normalize words
        for lst in (v_words, s_words):
            for d in lst:
                d["norm"] = _norm(d["raw"])
        
        video_words = len(v_words)
        song_words = len(s_words)
        
        # Find matching words
        v_norm_words = set(w["norm"] for w in v_words if w["norm"])
        s_norm_words = set(w["norm"] for w in s_words if w["norm"])
        matching_words = len(v_norm_words & s_norm_words)
        
        # Try to build sync anchors
        anchor_count = 0
        debug_info = ""
        
        try:
            from vidmaker import build_combined_sync
            anchors = build_combined_sync(video_json, song_json)
            anchor_count = len(anchors)
            debug_info = f"✅ Sync successful! Found {anchor_count} anchors.\n"
            debug_info += f"First few anchors:\n"
            for i, anchor in enumerate(anchors[:3]):
                debug_info += f"  {i+1}. Song {anchor['songStart']:.2f}s-{anchor['songEnd']:.2f}s → Video {anchor['videoStart']:.2f}s-{anchor['videoEnd']:.2f}s\n"
        except Exception as e:
            debug_info = f"❌ Sync failed: {str(e)}\n"
            debug_info += f"\nDetailed analysis:\n"
            debug_info += f"Video language: {video_language}\n"
            debug_info += f"Song language: {song_language}\n"
            debug_info += f"Video words (first 10): {[w['raw'] for w in v_words[:10]]}\n"
            debug_info += f"Song words (first 10): {[w['raw'] for w in s_words[:10]]}\n"
            debug_info += f"Video normalized (first 10): {[w['norm'] for w in v_words[:10]]}\n"
            debug_info += f"Song normalized (first 10): {[w['norm'] for w in s_words[:10]]}\n"
            debug_info += f"Common words: {list(v_norm_words & s_norm_words)[:20]}\n"
        
        return {
            "video_language": video_language,
            "song_language": song_language,
            "video_words": video_words,
            "song_words": song_words,
            "matching_words": matching_words,
            "anchor_count": anchor_count,
            "debug_info": debug_info
        }
        
    except Exception as e:
        import traceback
        traceback.print_exc()
        raise HTTPException(500, f"Debug test failed: {str(e)}")

@app.post("/api/debug/render_video_upload")
async def debug_render_video_upload(
    video: UploadFile,
    song: UploadFile,
    video_transcript: UploadFile,
    song_transcript: UploadFile
):
    """Render video with uploaded files"""
    jid = str(uuid.uuid4())
    jdir = STORE / jid  
    jdir.mkdir(parents=True, exist_ok=True)
    JOBS[jid] = {"status": "processing", "note": "Uploading files..."}
    
    async def run():
        try:
            import json
            
            jlog(jid, "📤 Receiving uploaded files...")
            
            # Save uploaded files
            video_path = jdir / f"video{Path(video.filename).suffix}"
            song_path = jdir / f"song{Path(song.filename).suffix}"
            video_transcript_path = jdir / "video.json"
            song_transcript_path = jdir / "song.json"
            
            # Write video file
            with open(video_path, "wb") as f:
                content = await video.read()
                f.write(content)
            jlog(jid, f"✅ Video saved: {video.filename}")
            
            # Write song file
            with open(song_path, "wb") as f:
                content = await song.read()
                f.write(content)
            jlog(jid, f"✅ Song saved: {song.filename}")
            
            # Write transcript files
            video_transcript_content = await video_transcript.read()
            with open(video_transcript_path, "wb") as f:
                f.write(video_transcript_content)
            jlog(jid, f"✅ Video transcript saved: {video_transcript.filename}")
            
            song_transcript_content = await song_transcript.read()
            with open(song_transcript_path, "wb") as f:
                f.write(song_transcript_content)
            jlog(jid, f"✅ Song transcript saved: {song_transcript.filename}")
            
            # Render the video
            out = jdir / "output.mp4"
            sync = jdir / "combined.json"
            
            jlog(jid, "🎬 Starting video synchronization...")
            await asyncio.to_thread(
                make_synced_video,
                clip_path=str(video_path),
                song_path=str(song_path),
                video_json_path=str(video_transcript_path),
                song_json_path=str(song_transcript_path),
                out_path=str(out),
                export_sync=str(sync),
                smooth=3
            )
            
            JOBS[jid].update(
                status="done",
                output=f"/api/download/{jid}",
                sync=f"/api/sync/{jid}",
                note="✅ Debug render complete!"
            )
            jlog(jid, "🎉 Debug render successful!")
            
        except Exception as e:
            jerr(jid, e)
    
    asyncio.create_task(run())
    return {"job_id": jid, "status": "processing"}

@app.post("/api/debug/render_video")
async def debug_render_video(req: dict):
    """Render video with debug files"""
    video_path = req.get("video_path")
    song_path = req.get("song_path")
    video_transcript_path = req.get("video_transcript_path")
    song_transcript_path = req.get("song_transcript_path")
    
    if not all([video_path, song_path, video_transcript_path, song_transcript_path]):
        raise HTTPException(400, "All file paths required")
    
    jid = str(uuid.uuid4())
    jdir = STORE / jid  
    jdir.mkdir(parents=True, exist_ok=True)
    JOBS[jid] = {"status": "processing", "note": "Starting debug render..."}
    
    async def run():
        try:
            from pathlib import Path
            import shutil
            
            # Verify all files exist
            for path, name in [(video_path, "video"), (song_path, "song"), 
                             (video_transcript_path, "video transcript"), 
                             (song_transcript_path, "song transcript")]:
                if not Path(path).exists():
                    raise FileNotFoundError(f"{name} file not found: {path}")
            
            jlog(jid, "✅ All files found, starting render...")
            
            # Copy files to job directory for consistency
            shutil.copy2(video_path, jdir / "video.mp4")
            shutil.copy2(song_path, jdir / "song.wav")
            shutil.copy2(video_transcript_path, jdir / "video.json")
            shutil.copy2(song_transcript_path, jdir / "song.json")
            
            jlog(jid, "📁 Files copied to job directory")
            
            # Render the video
            out = jdir / "output.mp4"
            sync = jdir / "combined.json"
            
            jlog(jid, "🎬 Starting video synchronization...")
            await asyncio.to_thread(
                make_synced_video,
                clip_path=str(jdir / "video.mp4"),
                song_path=str(jdir / "song.wav"),
                video_json_path=str(jdir / "video.json"),
                song_json_path=str(jdir / "song.json"),
                out_path=str(out),
                export_sync=str(sync),
                smooth=3
            )
            
            JOBS[jid].update(
                status="done",
                output=f"/api/download/{jid}",
                sync=f"/api/sync/{jid}",
                note="✅ Debug render complete!"
            )
            jlog(jid, "🎉 Debug render successful!")
            
        except Exception as e:
            jerr(jid, e)
    
    asyncio.create_task(run())
    return {"job_id": jid, "status": "processing"}

@app.get("/api/debug/status/{jid}")
def debug_status(jid: str):
    """Get debug job status with logs"""
    if jid not in JOBS:
        raise HTTPException(404, "Job not found")
    
    job_data = JOBS[jid].copy()
    
    # Add log content
    log_file = STORE / jid / "log.txt"
    if log_file.exists():
        job_data["log"] = log_file.read_text()
    
    return job_data

if FRONT.exists():
    app.mount("/",StaticFiles(directory=str(FRONT),html=True),name="static")
