"""Use existing screenshots and update the existing appendix only."""
from pathlib import Path
from datetime import datetime
from shutil import copy2
from hashlib import sha256
from zipfile import ZipFile
import json, re, sys
from PIL import Image, ImageDraw
from docx import Document
from docx.shared import Cm, Pt
from docx.enum.text import WD_ALIGN_PARAGRAPH

ROOT=Path(r'C:\Users\Asus\Desktop\TourAppProject\travelin_app\docs\thesis_appendices')
WORD=ROOT/'TourApp_Appendix_A_B.docx'
INBOX=ROOT/'work/capture_inbox/agency'
OUT=ROOT/'screenshots/appendix_b/agency'
STATE=ROOT/'work/agency_capture_batch.json'
FILES={
    'leads':'Screenshot_3-10-2026_195926_127.0.0.1.jpeg',
    'bids':'Screenshot_3-10-2026_2019_127.0.0.1.jpeg',
    'travelers':'Screenshot_3-10-2026_2021_127.0.0.1.jpeg',
    'journey':'Screenshot_3-10-2026_20229_127.0.0.1.jpeg',
}

def masked(key,boxes,base=None):
    with Image.open(INBOX/FILES[key]) as source:
        im=source.convert('RGB')
    draw=ImageDraw.Draw(im)
    for box in boxes:
        if base:
            box=tuple(round(v*im.size[i%2]/base[i%2]) for i,v in enumerate(box))
        draw.rectangle(box,fill='#626A78')
    return im

def set_text(p,text):
    p.clear();p.add_run(text)

def figure(p,path,caption,width=15):
    p.clear();p.style='Normal';p.alignment=WD_ALIGN_PARAGRAPH.CENTER
    f=p.paragraph_format;f.first_line_indent=Cm(0);f.line_spacing=1
    f.space_before=Pt(6);f.space_after=Pt(6);f.keep_with_next=True
    shape=p.add_run().add_picture(str(path),width=Cm(width))
    shape._inline.docPr.set('descr',caption+' สำเนาปิดบังข้อมูลส่วนตัวจากภาพหน้าจอจริง')
    c=p.insert_paragraph_before('ภาพประกอบที่ ข-0 '+caption,style='Caption')
    p._p.addnext(c._p);c.alignment=WD_ALIGN_PARAGRAPH.CENTER
    c.paragraph_format.first_line_indent=Cm(0);c.paragraph_format.keep_with_next=True
    return c

if sys.argv[1]=='prepare':
    stamp=datetime.now().strftime('%Y%m%d_%H%M%S')
    cp=ROOT/'checkpoints'/('agency_capture_'+stamp);cp.mkdir(parents=True,exist_ok=False)
    for name in (WORD.name,'TourApp_Appendix_A_B_Review.pdf','appendix_progress.md','appendix_pending.md','appendix_image_index.md','appendix_capture_guide.md'):
        copy2(ROOT/name,cp/('before_'+name))
    outputs={k:OUT/name for k,name in {'leads':'agency_leads.png','bids':'agency_bid_history.png','travelers':'agency_travelers.png','journey':'agency_journey_existing.png','journey_form':'agency_journey_existing_02.png'}.items()}
    assert not any(p.exists() for p in outputs.values()), 'Do not overwrite earlier screenshots'
    company=[(69,768,244,805),(36,784,59,814)]
    masked('leads',company).save(outputs['leads'])
    masked('bids',[(70,777,245,817),(36,790,60,820),(399,850,690,1110),(710,850,950,1110)]).save(outputs['bids'])
    masked('travelers',company+[(353,313,683,447),(699,313,993,447)]).save(outputs['travelers'])
    journey=masked('journey',[(59,648,200,679),(30,662,52,683),
        (320,244,470,267),(320,283,920,322),(336,566,704,675),
        (390,404,565,426),(367,833,665,856),(368,918,1000,945),
        (367,1002,1010,1030),(367,1085,1040,1118)],base=(1472,1720))
    journey.crop((0,0,1763,850)).save(outputs['journey'])
    journey.crop((360,845,1700,1990)).save(outputs['journey_form'])
    state={'checkpoint':str(cp),'outputs':{k:str(v) for k,v in outputs.items()},
        'sources':{k:str(INBOX/v) for k,v in FILES.items()},
        'original_hashes':{k:sha256((INBOX/v).read_bytes()).hexdigest() for k,v in FILES.items()},
        'before_word_sha256':sha256(WORD.read_bytes()).hexdigest(),
        'duplicate_skipped':'Screenshot_3-10-2026_195811_127.0.0.1.jpeg',
        'privacy':'Opaque redaction in copies only; source screenshot records are not opened in application/database'}
    STATE.write_text(json.dumps(state,ensure_ascii=False,indent=2),encoding='utf-8')
    print(json.dumps({'checkpoint':str(cp),'outputs':list(state['outputs'].values()),'source_files_used':4,'duplicate_skipped':1},ensure_ascii=False))
elif sys.argv[1]=='insert':
    s=json.loads(STATE.read_text(encoding='utf-8'))
    assert sha256(WORD.read_bytes()).hexdigest()==s['before_word_sha256']
    d=Document(WORD)
    assert len(d.inline_shapes)==16
    old_caps=[(p._p,re.match(r'ภาพประกอบที่ (ข-\d+) ',p.text).group(1)) for p in d.paragraphs if re.match(r'ภาพประกอบที่ (ข-\d+) ',p.text)]
    changes=[
      ('การค้นหากลุ่มใน Lead Marketplace','leads','หน้าค้นหากลุ่มที่พร้อมรับข้อเสนอและสถานะไม่พบผลค้นหา',
       'เลือกเมนู Lead Marketplace เพื่อค้นหากลุ่มที่พร้อมรับข้อเสนอ กรอกปลายทางและช่วงงบประมาณต่อคนตามช่องที่แสดง แล้วกด ค้นหา หากต้องการเริ่มค้นหาโดยไม่ใช้เงื่อนไขเดิมให้กด ล้างตัวกรอง ภาพตัวอย่างแสดงข้อความ ยังไม่พบกลุ่มที่ตรงกัน จึงยังไม่มีรายการให้เปิดรายละเอียด บริษัทควรปรับเงื่อนไขหรือกลับมาตรวจใหม่ ไม่ถือว่าสถานะนี้เป็นการส่งข้อเสนอสำเร็จ'),
      ('การติดตามข้อเสนอและสถานะ','bids','หน้าข้อเสนอของบริษัทและประวัติข้อเสนอ',
       'เลือกเมนู ข้อเสนอของฉัน เพื่อตรวจรายการที่ส่งจากบัญชีบริษัทนี้ ภาพตัวอย่างแสดงว่า ไม่มีข้อเสนอที่กำลังใช้งาน และมี ประวัติข้อเสนอ อยู่ด้านล่าง ตารางแสดงราคาและสถานะ เช่น ทริปเสร็จสิ้น หรือ ยกเลิกแล้ว ซึ่งเป็นคนละสถานะกัน บริษัทสามารถกด ดู ในรายการที่ตนมีสิทธิ์เพื่ออ่านรายละเอียด แต่รายการประวัติไม่สามารถแก้ไขหรือยกเลิกได้ ไม่ควรตีความว่าข้อเสนอทุกแถวถูกยกเลิก'),
      ('การตรวจเอกสารนักท่องเที่ยวของทริปที่ชนะ','travelers','หน้ารายการลูกทัวร์และสถานะเอกสารการเดินทาง',
       'เลือกเมนู ลูกทัวร์และเอกสาร เพื่อดูสมาชิกของกลุ่มที่ข้อเสนอของบริษัทชนะและประกาศผลแล้ว ตารางแสดงสถานะเอกสารและรายการอัปเดต ภาพตัวอย่างแสดง ยังไม่ส่ง และ รอสมาชิกส่ง จึงยังไม่มีเอกสารให้ตรวจ เมื่อสมาชิกส่งเอกสารแล้วจึงเปิดรายละเอียดตามสิทธิ์เพื่ออ่านข้อมูลที่จำเป็นและดำเนินการตรวจตามตัวเลือกของระบบ ภาพนี้ไม่ใช่หลักฐานการตรวจเอกสารผ่าน'),
      ('การจัดการนัดหมายและสถานะทริป','journey','หน้าทริปของบริษัทและความพร้อมของสมาชิก',
       'เลือกเมนู ทริปและความพร้อม เพื่อดูเฉพาะกลุ่มที่บริษัทเป็นผู้ชนะและประกาศผลแล้ว ตรวจสถานะการชำระเงิน เอกสาร และความพร้อมของสมาชิกจากตาราง ภาพตัวอย่างมีสถานะ paid และ not_started โดยเอกสารยัง missing และความพร้อมยังไม่ครบ การแสดง paid ในภาพเป็นสถานะรายการเดิม ไม่ใช่หลักฐานว่าคู่มือนี้ได้ตรวจสลิปหรือทำธุรกรรมใหม่'),
    ]
    journey_first=None
    for title,key,caption,text in changes:
        matches=[p for p in d.paragraphs if p.text=='[รอภาพหน้าจอจริง: '+title+']']
        assert len(matches)==1
        c=figure(matches[0],s['outputs'][key],caption)
        p=next(p for p in d.paragraphs if p._p is c._p.getnext())
        set_text(p,text)
        if key=='journey':journey_first=p
    # The tall original is cropped into two readable views, never stretched or fabricated.
    p=journey_first.insert_paragraph_before('',style='Normal');journey_first._p.addnext(p._p)
    c=figure(p,s['outputs']['journey_form'],'ส่วนข้อมูลนัดหมายและสถานะการเดินทางของทริป',width=14)
    note=c.insert_paragraph_before('ในส่วน จุดนัดหมายและผู้ประสานงาน บริษัทกรอกวันเวลา จุดนัดหมาย ผู้ประสานงาน เบอร์ติดต่อและหมายเหตุ แล้วกด บันทึกข้อมูลนัดหมาย เมื่อต้องบันทึกข้อมูลจริงตามสิทธิ์ ส่วน สถานะการเดินทางและ Visa/ETA ใช้เลือกสถานะทริป ความต้องการ Visa/ETA และสถานะเอกสาร ก่อนกด อัปเดตสถานะ ภาพนี้แสดงส่วนติดต่อสำหรับดำเนินการเท่านั้น ไม่ใช่ผลการบันทึกหรือการเปลี่ยนสถานะ',style='Normal')
    c._p.addnext(note._p);note.paragraph_format.first_line_indent=Cm(1.27)
    # Existing lifecycle explanation remains, but do not present it as an action performed for capture.
    n=0;mapping={};captions=[]
    for p in d.paragraphs:
        if re.match(r'ภาพประกอบที่ ข-\d+ ',p.text):
            n+=1;old=next((old for el,old in old_caps if el is p._p),None)
            if old:mapping[old]=f'ข-{n}'
            set_text(p,re.sub(r'ข-\d+',f'ข-{n}',p.text,count=1));captions.append(p.text)
    for p in d.paragraphs:
        if p.text.startswith('ภาพประกอบที่ '):continue
        for run in p.runs:
            if re.search(r'ข-\d+',run.text):run.text=re.sub(r'ข-\d+',lambda m:mapping.get(m.group(0),m.group(0)),run.text)
    assert len(d.inline_shapes)==21
    assert sum('[รอภาพหน้าจอจริง:' in p.text for p in d.paragraphs)==29
    assert sum(p.style.name=='Heading 2' for p in d.paragraphs)==54
    d.core_properties.author='';d.core_properties.last_modified_by=''
    stage=ROOT/'work/agency_capture_staging.docx';d.save(stage)
    with ZipFile(stage) as z:
        assert z.testzip() is None
        media={sha256(z.read(n)).hexdigest() for n in z.namelist() if n.startswith('word/media/')}
        assert all(sha256(Path(p).read_bytes()).hexdigest() in media for p in s['outputs'].values())
        assert all(sha256(Path(p).read_bytes()).hexdigest() not in media for p in s['sources'].values())
    stage.replace(WORD);copy2(WORD,Path(s['checkpoint'])/WORD.name)
    s.update({'embedded_images':21,'unique_media':20,'pending_topics':29,'caption_mapping':mapping,'captions_b':captions,'word_sha256':sha256(WORD.read_bytes()).hexdigest()})
    STATE.write_text(json.dumps(s,ensure_ascii=False,indent=2),encoding='utf-8')
    (Path(s['checkpoint'])/'verification.json').write_text(json.dumps(s,ensure_ascii=False,indent=2),encoding='utf-8')
    print(json.dumps({'checkpoint':s['checkpoint'],'embedded_images':21,'pending_topics':29,'caption_mapping':mapping,'captions_b':captions},ensure_ascii=False))
else:
    raise SystemExit('Use prepare or insert')
