#!/usr/bin/env python3
import hashlib,json,re,sys
from collections import defaultdict
from datetime import datetime,timezone
from pathlib import Path
PROJECT=Path(__file__).resolve().parents[1];CANDIDATE=PROJECT/'story/spoken-narration.txt';REFERENCES=[Path('/data/video-pipeline/Chanel-HuyenAnAudio/project/002-Co-Ke-Toan-Bi-Ep-Nhan-Toi-Bien-Thu-Quy-Cuu-Tro-Den-Khi-Muoi-Hai-Chu-Ky-Cung-Phan-Chu/story/v3/spoken-narration.txt'),Path('/data/video-pipeline/Chanel-HuyenAnAudio/project/003-Co-Chu-Tro-Bi-Ep-Ban-Nha-Bay-Chia-Khoa-Bong-Tro-Ve/story/spoken-narration.txt')];REPORT=PROJECT/'story/originality-report.json';TOKEN_RE=re.compile(r'[\wÀ-ỹĐđ]+',re.UNICODE)
def tokens(t):return [x.casefold() for x in TOKEN_RE.findall(t)]
def digest(t):return hashlib.sha256(t.encode()).hexdigest()
def longest(a,b):
 pos=defaultdict(list)
 for j,t in enumerate(b):pos[t].append(j)
 best=0;sample=None;prev={}
 for i,t in enumerate(a):
  cur={}
  for j in pos.get(t,[]):
   n=prev.get(j-1,0)+1;cur[j]=n
   if n>best:best=n;sample=(i-n+1,i+1)
  prev=cur
 return best,' '.join(a[sample[0]:sample[1]]) if sample else ''
def main():
 issues=[];candidate=CANDIDATE.read_text() if CANDIDATE.exists() else ''
 if not candidate:issues.append('candidate_missing')
 a=tokens(candidate);rows=[]
 for ref in REFERENCES:
  if not ref.exists():issues.append('reference_missing:'+str(ref));continue
  text=ref.read_text();b=tokens(text);n=14;shingles={tuple(b[i:i+n]) for i in range(max(0,len(b)-n+1))};matches=[]
  for i in range(max(0,len(a)-n+1)):
   sh=tuple(a[i:i+n])
   if sh in shingles:matches.append({'index':i,'text':' '.join(sh)}); 
   if len(matches)>=20:break
  run,sample=longest(a,b) if a and b else (0,'');rows.append({'path':str(ref),'sha256':digest(text),'shingle_matches':matches,'longest_exact_token_run':run,'sample':sample[:500]})
  if matches:issues.append(f'reference_shingle_matches:{ref.parent.parent.name}:{len(matches)}')
  if run>=14:issues.append(f'long_exact_token_run:{ref.parent.parent.name}:{run}')
 paragraphs=[re.sub(r'\s+',' ',x.strip().casefold()) for x in re.split(r'\n\s*\n',candidate) if len(tokens(x))>=35];seen={};dups=[]
 for i,x in enumerate(paragraphs):
  h=hashlib.sha256(x.encode()).hexdigest()
  if h in seen:dups.append({'first':seen[h],'duplicate':i})
  else:seen[h]=i
 if dups:issues.append(f'internal_duplicate_paragraphs:{len(dups)}')
 report={'version':1,'verified':not issues,'status':'passed' if not issues else 'failed','source_canon_sha256':digest(candidate) if candidate else None,'references':rows,'shingle_size':14,'internal_duplicate_paragraphs':dups,'issues':issues,'checked_at':datetime.now(timezone.utc).isoformat()};REPORT.write_text(json.dumps(report,ensure_ascii=False,indent=2)+'\n');print(json.dumps({'verified':report['verified'],'references':len(rows),'issues':issues},ensure_ascii=False));return 0 if report['verified'] else 1
if __name__=='__main__':sys.exit(main())
