#!/usr/bin/env python3
from pathlib import Path
import datetime,hashlib,json,os
ROOT=Path('/data/video-pipeline/HaTramAudio/project/019-Buc-Tuong-Giua-Hai-Ban-Cong');PID=ROOT.name;RUN='run-20260721T065616Z-a7dbad2f';OWNER='Levy';TITLE='Bức Tường Giữa Hai Ban Công'
def now():return datetime.datetime.now(datetime.timezone.utc).isoformat().replace('+00:00','Z')
def sha(p):return hashlib.sha256(p.read_bytes()).hexdigest()
def guard():
 d=json.loads((ROOT/'.ownership-lock.json').read_text())
 for k,v in {'project_id':PID,'run_id':RUN,'owner':OWNER}.items():
  if d.get(k)!=v:raise RuntimeError('ownership mismatch '+k)
def atomic_text(p,s):
 guard();p.parent.mkdir(parents=True,exist_ok=True);t=p.with_name('.'+p.name+'.tmp');t.write_text(s);os.replace(t,p)
def atomic_json(p,d):atomic_text(p,json.dumps(d,ensure_ascii=False,indent=2)+'\n')
def main():
 guard();m=json.loads((ROOT/'script/project-manifest.json').read_text());canon=m.get('canon_sha256')
 story=m.get('gate_events',{}).get('story',{})
 if m.get('steps',{}).get('story')!='completed' or story.get('verified') is not True or story.get('canon_sha256')!=canon:raise RuntimeError('Story Gate is not committed/current')
 promo=json.loads((ROOT/'script/promotion-report.json').read_text());brief=ROOT/'script/story-brief.json'
 if not promo.get('verified') or promo.get('canon_sha256')!=canon or sha(ROOT/'story/story-canon.txt')!=canon:raise RuntimeError('canon promotion invalid')
 yt=f'Truyện Audio | {TITLE} (Full) | Hạ Trâm Audio'
 summary='Trở lại căn hộ tuổi thơ ở Tô Châu để quyết định việc bán nhà trong ba mươi ngày, Tống Vãn Chi buộc phải đối diện Lục Cảnh Hòa, người bạn lớn lên bên ban công kế bên và cũng là người hai gia đình luôn gọi là anh trai của cô. Giữa chiếc chìa khóa cũ, những tín hiệu đèn và các ký ức từng bị hiểu sai, cả hai phải học cách gọi đúng mong muốn của mình, tôn trọng ranh giới và công khai bước từ người nhà sang tình yêu.'
 genres=['Ngôn tình đô thị','Thanh mai trúc mã','Hài nhẹ','Gia đình ấm áp']
 info=f'{yt}\n\n{summary}\n\nThể loại: '+', '.join(genres)+'\n\nTruyện được phát độc quyền tại Hạ Trâm Audio, nghiêm cấm sao chép dưới mọi hình thức\n'
 ip=ROOT/'output/info.txt';atomic_text(ip,info)
 metadata={'schema_version':1,'source_project_id':PID,'project_id':PID,'run_id':RUN,'status':'completed','verified':True,'source_canon_sha256':canon,'source_story_brief_sha256':sha(brief),'canonical_title':TITLE,'youtube_title':yt,'title_length':len(yt),'description':summary,'summary':summary,'genres':genres,'tags':[],'thumbnail_text':TITLE,'copyright_line':'Truyện được phát độc quyền tại Hạ Trâm Audio, nghiêm cấm sao chép dưới mọi hình thức','authority_sha256':{'story_canon':canon,'story_brief':sha(brief)},'contains_spoiler_reveal':False}
 mp=ROOT/'script/metadata.json';atomic_json(mp,metadata);receipt={**metadata,'artifact_path':str(ip),'artifact_sha256':sha(ip),'metadata_json_path':str(mp),'metadata_json_sha256':sha(mp),'completed_at':now()};atomic_json(ROOT/'log/metadata.json',receipt);print(json.dumps({'status':'completed','verified':True,'youtube_title':yt,'artifact_sha256':receipt['artifact_sha256']},ensure_ascii=False))
if __name__=='__main__':main()
