#!/usr/bin/env python3
import hashlib
import json
import subprocess
import sys
import time
import urllib.request
from datetime import datetime, timezone
from pathlib import Path

PROJECT=Path(__file__).resolve().parents[1]
FINAL=PROJECT/'output/final.mp4'
FINAL_RECEIPT=PROJECT/'log/final-render.json'
PROMOTION=PROJECT/'story/promotion-report.json'
OUTPUT=PROJECT/'output/final-upload.mp4'
SUBMISSION=PROJECT/'log/upload-transcode-submission.json'
RECEIPT=PROJECT/'log/upload-transcode.json'
BASE='http://192.168.1.104:8024'

def request(method,path,payload=None,timeout=30):
    body=json.dumps(payload).encode() if payload is not None else None
    req=urllib.request.Request(BASE+path,data=body,headers={'Content-Type':'application/json'} if body else {},method=method)
    with urllib.request.urlopen(req,timeout=timeout) as response:return json.load(response)
def sha(path):
    h=hashlib.sha256()
    with path.open('rb') as f:
        for block in iter(lambda:f.read(8*1024*1024),b''):h.update(block)
    return h.hexdigest()
def probe(path):
    out=subprocess.run(['ffprobe','-v','error','-show_entries','format=duration,size','-show_entries','stream=index,codec_name,width,height,sample_rate,channels:stream_tags=encoder','-of','json',str(path)],capture_output=True,text=True,check=True)
    return json.loads(out.stdout)
def main():
    for path in (FINAL,FINAL_RECEIPT,PROMOTION):
        if not path.exists():raise RuntimeError(f'Transcode blocked: missing {path}')
    final_receipt=json.loads(FINAL_RECEIPT.read_text());promotion=json.loads(PROMOTION.read_text());canon=promotion.get('spoken_sha256')
    final_sha=sha(FINAL)
    if promotion.get('verified') is not True or final_receipt.get('verified') is not True or final_receipt.get('visual_qa',{}).get('verified') is not True or final_receipt.get('source_canon_sha256')!=canon or final_receipt.get('output_sha256')!=final_sha:raise RuntimeError('Transcode blocked: final/canon/hash/visual receipt mismatch')
    if OUTPUT.exists() and not RECEIPT.exists():raise RuntimeError('Unreceipted upload output exists; do not overwrite')
    if RECEIPT.exists():
        old=json.loads(RECEIPT.read_text())
        if old.get('verified') is True and old.get('source_canon_sha256')==canon and OUTPUT.exists() and OUTPUT.stat().st_size<1_000_000_000 and old.get('output_sha256')==sha(OUTPUT):
            print(json.dumps({'reused_verified':True,'job_id':old['job_id'],'bytes':OUTPUT.stat().st_size}));return 0
    health=request('GET','/health')
    if health.get('status')!='ok' or health.get('h264_nvenc') is not True:raise RuntimeError('Transcode endpoint unhealthy')
    submission=None
    if SUBMISSION.exists():
        candidate=json.loads(SUBMISSION.read_text())
        if candidate.get('source_canon_sha256')==canon and candidate.get('input_sha256')==final_sha:submission=candidate
    if submission is None:
        payload={'input_video_path':str(FINAL),'output_video_path':str(OUTPUT),'target_output_bytes':800_000_000,'max_output_bytes':1_000_000_000,'audio_bitrate_kbps':96,'width':1920,'height':1080,'fps':30,'overwrite':False}
        created=request('POST','/v1/huyenan-transcode-upload/jobs',payload)
        submission={'version':1,'verified':False,'source_canon_sha256':canon,'input_sha256':final_sha,'job_id':created['job_id'],'status':created['status'],'payload':payload,'submitted_at':datetime.now(timezone.utc).isoformat()}
        SUBMISSION.write_text(json.dumps(submission,ensure_ascii=False,indent=2)+'\n')
    job_id=submission['job_id']
    for _ in range(480):
        job=request('GET','/v1/huyenan-transcode-upload/jobs/'+job_id)
        if job.get('status') in {'completed','failed','cancelled'}:break
        time.sleep(10)
    else:raise RuntimeError('Transcode poll timeout; resume same job')
    if job.get('status')!='completed' or job.get('verification',{}).get('verified') is not True:raise RuntimeError(f'Transcode job ended {job.get("status")}: {job.get("error")}')
    local=probe(OUTPUT);streams=local.get('streams',[]);video=[s for s in streams if s.get('width')];audio=[s for s in streams if s.get('sample_rate')]
    verified=(len(video)==1 and len(audio)==1 and video[0].get('codec_name')=='h264' and audio[0].get('codec_name')=='aac' and video[0].get('width')==1920 and video[0].get('height')==1080 and OUTPUT.stat().st_size<1_000_000_000 and job['verification'].get('under_one_gb') is True)
    receipt={'version':1,'verified':verified,'status':'completed' if verified else 'failed','source_canon_sha256':canon,'job_id':job_id,'endpoint':BASE,'input':str(FINAL.relative_to(PROJECT)),'output':str(OUTPUT.relative_to(PROJECT)),'output_bytes':OUTPUT.stat().st_size,'output_sha256':sha(OUTPUT),'server_verification':job['verification'],'independent_probe':local,'checked_at':datetime.now(timezone.utc).isoformat()}
    RECEIPT.write_text(json.dumps(receipt,ensure_ascii=False,indent=2)+'\n')
    print(json.dumps({'verified':verified,'job_id':job_id,'bytes':receipt['output_bytes'],'sha256':receipt['output_sha256']},ensure_ascii=False));return 0 if verified else 1
if __name__=='__main__':
 try:sys.exit(main())
 except Exception as exc:
  print(str(exc) if str(exc).startswith('Transcode blocked:') else 'Transcode blocked: '+str(exc),file=sys.stderr)
  sys.exit(1)
