#!/usr/bin/env python3
from __future__ import annotations
import datetime,json,os,re,sys,time,urllib.parse,urllib.request
from collections import defaultdict
from pathlib import Path
from zoneinfo import ZoneInfo
B='https://postiz-api.pain.io.vn/public/v1';FB='cmrioo6m00015j7c8ppe2dj97';YT='cmrhv05ze0009j7c8gjy7lmcn';H={'Authorization':os.environ['POSTIZ_API_KEY']};TZ=ZoneInfo('Asia/Ho_Chi_Minh')
def utc_iso(d):return d.astimezone(datetime.timezone.utc).isoformat(timespec='milliseconds').replace('+00:00','Z')
def get_posts(a,b):
 q=urllib.parse.urlencode({'startDate':a,'endDate':b});r=json.load(urllib.request.urlopen(urllib.request.Request(B+'/posts?'+q,headers=H),timeout=180));return r if isinstance(r,list) else r.get('posts',r.get('data',[]))
def iid(x):
 it=x.get('integration');return it.get('id') if isinstance(it,dict) else x.get('integrationId') or it
def pid(x):return x.get('postId') or x.get('id')
def pub(x):return x.get('publishDate') or x.get('date')
def content(x):
 v=x.get('content') or x.get('value') or ''
 if isinstance(v,list):v=' '.join(str(z.get('content','')) if isinstance(z,dict) else str(z) for z in v)
 return re.sub(r'\s+',' ',str(v)).strip().casefold()
def duplicate_groups(rows):
 g=defaultdict(list)
 for x in rows:
  if iid(x) and pub(x):g[(iid(x),pub(x),content(x))].append(pid(x))
 return {str(k):v for k,v in g.items() if len(v)>1}
def main():
 p=Path(sys.argv[1]).resolve();assert p.name=='032-Vi-Sao-Chan-Ban-Te-Ran-Sau-Khi-Ngoi-De-Lau' and (p/'project.json').exists();up=json.load(open(p/'logs/postiz_uploads.json'));now=datetime.datetime.now(TZ);lead=now+datetime.timedelta(minutes=15);end=now+datetime.timedelta(days=21);a=utc_iso(now-datetime.timedelta(days=1));b=utc_iso(end+datetime.timedelta(days=1));rows=get_posts(a,b);pre=duplicate_groups(rows);assert not pre,pre
 occ={FB:set(),YT:set()}
 for x in rows:
  if iid(x) in occ and pub(x):occ[iid(x)].add(pub(x))
 candidates=[]
 for add in range(22):
  day=(now+datetime.timedelta(days=add)).date()
  for hh,mm in ((9,20),(19,0)):
   local=datetime.datetime(day.year,day.month,day.day,hh,mm,tzinfo=TZ)
   if local>lead:candidates.append((local,utc_iso(local)))
 assert candidates and all((x[0].hour,x[0].minute) in ((9,20),(19,0)) for x in candidates)
 local,chosen=next(x for x in candidates if x[1] not in occ[FB] and x[1] not in occ[YT])
 title=(p/'metadata/title.txt').read_text().strip();desc=(p/'metadata/description.txt').read_text().strip();raw=[x.strip() for x in (p/'metadata/tags.txt').read_text().split(',') if x.strip()];tags=[];total=0
 for x in raw:
  cost=len(x)*2
  if total+cost>=490:break
  tags.append({'value':x,'label':x});total+=cost
 payload={'type':'schedule','date':chosen,'shortLink':False,'tags':tags,'posts':[{'integration':{'id':FB},'value':[{'content':desc,'image':[{'id':up['facebook_video']['id'],'path':up['facebook_video']['path']}]}],'settings':{'__type':'facebook'}},{'integration':{'id':YT},'value':[{'content':desc,'image':[{'id':up['youtube_video']['id'],'path':up['youtube_video']['path']}]}],'settings':{'__type':'youtube','title':title,'type':'public','selfDeclaredMadeForKids':'no','thumbnail':{'id':up['thumbnail']['id'],'path':up['thumbnail']['path']}}}]}
 r=json.load(urllib.request.urlopen(urllib.request.Request(B+'/posts',data=json.dumps(payload).encode(),headers={**H,'Content-Type':'application/json'},method='POST'),timeout=300));rr=r if isinstance(r,list) else r.get('posts',r.get('data',[]));ids={pid(x) for x in rr};ids.discard(None);assert len(ids)==2,(r,ids)
 found={}
 for _ in range(10):
  live=get_posts(utc_iso(local-datetime.timedelta(hours=2)),utc_iso(local+datetime.timedelta(hours=2)))
  for x in live:
   if pid(x) in ids:found[pid(x)]={'integration_id':iid(x),'publish_date':pub(x),'state':x.get('state')}
  if len(found)==2 and {v['integration_id'] for v in found.values()}=={FB,YT} and all(v['state']=='QUEUE' and v['publish_date']==chosen for v in found.values()):break
  time.sleep(5)
 assert len(found)==2 and {v['integration_id'] for v in found.values()}=={FB,YT} and all(v['state']=='QUEUE' and v['publish_date']==chosen for v in found.values()),found
 postrows=get_posts(a,b);post=duplicate_groups(postrows);assert not post,post
 out={'status':'PASS','date_local':local.isoformat(),'date_utc':chosen,'posts':found,'duplicate_audit':{'pre_groups':0,'post_groups':0,'key':'integration.id + publishDate + normalized content'},'verified_at':datetime.datetime.now(datetime.timezone.utc).isoformat()};(p/'logs/postiz_calendar.json').write_text(json.dumps(out,ensure_ascii=False,indent=2)+'\n');d=json.load(open(p/'project.json'));d['postiz']=out;d['status']='completed';d['current_step']='completed';open(p/'project.json','w').write(json.dumps(d,ensure_ascii=False,indent=2)+'\n');print(json.dumps(out,ensure_ascii=False))
if __name__=='__main__':main()
