#!/usr/bin/env python3
"""
Konvertor + pool stok artikel web (Solusi B)
============================================
Membaca semua stok artikel konten-utuh per client dari berbagai format
(md SEO, txt meta, py dict) lalu menggabungkannya menjadi SATU pool terpusat:

    stok_artikel_web.json  = { "client": [ {artikel_1}, {artikel_2}, ... ], ... }

Setiap artikel dict siap dipakai pipeline (format sama dgn CFG["article"]):
  judul, slug, meta_desc, focus_kw, cta_title, cta_desc, cta_text,
  image_url, categories, tags, content[ {type:h2|p|ul, t/items} ]

Cara jalan: python3 build_stok_web.py
   -> regenerate stok_artikel_web.json dari semua file stok di workspace.
"""
import os, re, glob, json, html as H, hashlib

WS = os.path.dirname(os.path.abspath(__file__))

# ---------------------------------------------------------------- helpers
def _strip(tag_nodes):
    out=[]
    for li in tag_nodes:
        out.append(H.unescape(re.sub(r'<[^>]+>','',li)).strip())
    return [x for x in out if x]

def html_to_blocks(html):
    """Konversi HTML artikel jadi list [type, value] pipeline (h2/p/ul)."""
    blocks=[]
    def add_h2():  # h2
        for m in re.finditer(r'<h2>(.*?)</h2>', html, re.S):
            t=H.unescape(re.sub(r'<[^>]+>','',m.group(1))).strip()
            if t: blocks.append(['h2',t])
    def add_p():
        for m in re.finditer(r'<p>(.*?)</p>', html, re.S):
            t=H.unescape(re.sub(r'<[^>]+>','',m.group(1))).strip()
            if t and not t.lower().startswith(('artikel ini untuk','kunjungi ','* artikel','disclaimer')): blocks.append(['p',t])
    def add_list(tag):
        for m in re.finditer(rf'<{tag}>(.*?)</{tag}>', html, re.S):
            items=_strip(re.findall(r'<li>(.*?)</li>',m.group(1),re.S))
            if items: blocks.append(['ul',items])
    add_h2(); add_p(); add_list('ul'); add_list('ol')
    return blocks

def slugify(s):
    s=re.sub(r'[^a-z0-9]+','-',s.lower()).strip('-').strip('.')
    return s[:100]

def norm(s): return re.sub(r'[^a-z0-9]+','',s.lower())

# ---------------------------------------------------------------- parsers
def parse_md_seo(path):
    """Format: # Judul, ## SEO Data (meta, slug, focus, kategori, tags), ## Konten (H1/H2/paragraf)."""
    try: t=open(path,encoding='utf-8',errors='ignore').read()
    except: return []
    arts=[]
    # TSC/skin-code format: "# Artikel 1: X" ... "## SEO Data" ... "## Konten"
    seck=re.split(r'(?m)^#\s+Artikel\s+\d+[:\-]\s*', t)
    for b in seck[1:]:
        title=(b.split('\n')[0]).strip()
        meta={}
        mm=re.search(r'\*\*Meta Description:\*\*\s*(.+)', b); meta['md']=mm.group(1).strip() if mm else ''
        ms=re.search(r'\*\*Slug:\*\*\s*(\S+)', b); meta['slug']=ms.group(1).strip() if ms else slugify(title)
        mf=re.search(r'\*\*Focus Keyphrase:\*\*\s*(.+)', b); meta['kw']=mf.group(1).strip() if mf else title
        mk=re.search(r'\*\*Kategori:\*\*\s*(.+)', b); meta['cat']=mk.group(1).strip() if mk else ''
        mt=re.search(r'\*\*Tags:\*\*\s*(.+)', b); meta['tags']=mt.group(1).strip() if mt else ''
        konten=re.search(r'##\s*Konten\n(.*)', b, re.S)
        if not konten: continue
        body=konten.group(1)
        blocks=[]
        cur=None
        for line in body.split('\n'):
            line=line.strip()
            if not line: continue
            if line.startswith('### '):
                blocks.append(['h2', line[4:].strip()])
            elif line.startswith('###'):
                blocks.append(['h2', line.lstrip('#').strip()])
            elif line.startswith('**') and line.endswith('**') and len(line)>8:
                blocks.append(['h2', line.strip('*').strip()])
            elif re.match(r'^[-*]\s+', line):
                if blocks and blocks[-1][0]=='ul': blocks[-1][1].append(line[2:].strip())
                else: blocks.append(['ul',[line[2:].strip()]])
            elif re.match(r'^\d+[\.\)]\s+', line):
                if blocks and blocks[-1][0]=='ul': blocks[-1][1].append(re.sub(r'^\d+[\.\)]\s+','',line))
                else: blocks.append(['ul',[re.sub(r'^\d+[\.\)]\s+','',line)]])
            else:
                # bold first line jadi h2 kalo belum ada paragraf
                blocks.append(['p', line])
        if not blocks: continue
        arts.append({
            'judul': title,'slug': meta['slug'] or slugify(title),
            'meta_desc': meta['md'] or title[:155],'focus_kw': meta['kw'] or title,
            'categories':[meta['cat']] if meta['cat'] else [],
            'tags':[x.strip() for x in meta['tags'].split(',')] if meta['tags'] else [],
            'content':blocks,'_src':os.path.basename(path)
        })
    return arts

def parse_txt_seo(path):
    """Format: # Judul, # keyphrase, # slug, # WA, lalu [H1] + paragraf/##."""
    try: t=open(path,encoding='utf-8',errors='ignore').read()
    except: return []
    lines=[l.rstrip() for l in t.split('\n')]
    title=''; kw=''; sl=''; start=0
    for i,l in enumerate(lines):
        if l.startswith('# ') and not l.lstrip('# ').startswith(('#','[','Key','Slug','WA')):
            if not title: title=l.lstrip('# ').strip()
        if l.lower().startswith('# keyphrase'): kw=l.split(':',1)[1].strip()
        if l.lower().startswith('# slug'): sl=l.split(':',1)[1].strip()
        if l.lower().startswith('# wa button'): start=i+1
    # konten mulai dari [H1]
    body='\n'.join(lines[start:])
    blocks=[]
    for line in body.split('\n'):
        line=line.strip()
        if not line: continue
        if line.startswith('[') and ']' in line:
            blocks.append(['h2', re.sub(r'^\[H?\d?\](.*)$',r'\1',line).strip() or re.sub(r'[\[\]]','',line)])
        elif line.startswith('## '):
            blocks.append(['h2', line[3:].strip()])
        elif re.match(r'^[-*]\s+', line):
            if blocks and blocks[-1][0]=='ul': blocks[-1][1].append(line[2:].strip())
            else: blocks.append(['ul',[line[2:].strip()]])
        else:
            blocks.append(['p', line])
    if not title or not blocks: return []
    return [{
        'judul':title,'slug':sl or slugify(title),'meta_desc':title[:155],
        'focus_kw':kw or title,'categories':[],'tags':[],
        'content':blocks,'_src':os.path.basename(path)
    }]

def parse_txt_abimata(path):
    """Format 10_artikel_abimata.txt: 'ARTIKEL N\nKategori: X\nJudul: Y\n\n<deskripsi paragraf>'."""
    try: t=open(path,encoding='utf-8',errors='ignore').read()
    except: return []
    arts=[]
    secs=re.split(r'(?m)^\s*ARTIKEL\s+\d+\s*\n',t)
    for b in secs[1:]:
        kat=re.search(r'(?m)^Kategori:\s*(.+)',b); kt=kat.group(1).strip() if kat else 'Tips'
        jud=re.search(r'(?m)^Judul:\s*(.+)',b); title=jud.group(1).strip() if jud else ''
        if not title: continue
        # konten = paragraf2 selain metadata
        lines=[l.strip() for l in b.split('\n') if l.strip()]
        body=' '.join([l for l in lines if not l.lower().startswith(('kategori:','judul:'))])
        arts.append({
            'judul':title,'slug':slugify(title),'meta_desc':body[:155],
            'focus_kw':title.split(':')[0],'categories':[kt],'tags':[],
            'content':[{'type':'p','t':body}],'_src':os.path.basename(path)
        })
    return arts

def parse_frontmatter_md(path):
    """Format YAML frontmatter (--- title/slug/category/tags/meta_description ---) + # H1 + ## H2 konten.
    (SSC, Yasma, LEIT bervariasi; ini handler generik.)"""
    try: t=open(path,encoding='utf-8',errors='ignore').read()
    except: return []
    # frontmatter
    fm={}
    mm=re.match(r'^---\s*\n(.*?)\n---\s*\n', t, re.S)
    body=t
    if mm:
        for line in mm.group(1).split('\n'):
            if ':' in line:
                k,v=line.split(':',1); fm[k.strip().lower()]=v.strip().strip('"\'')
        body=t[mm.end():]
    title=fm.get('title','') or ''
    slug=fm.get('slug','') or slugify(re.sub(r'^#\s*','',body.split('\n')[0].strip()))
    meta=fm.get('meta_description','') or fm.get('metadesc','') or f'{title[:155]}'
    kw=fm.get('target_keyword','') or fm.get('focus','') or title
    cats=[fm['category']] if fm.get('category') else []
    tags=[x.strip() for x in fm.get('tags','').split(',')] if fm.get('tags') else []
    # konten: # H1 jadi judul utama (skip), ## H2 paragraf/list
    if not title:
        title=re.sub(r'^#\s*','',body.split('\n')[0].strip())
    blocks=[]
    for line in body.split('\n'):
        line=line.strip()
        if not line: continue
        if line.startswith('## '):
            blocks.append(['h2', line[3:].strip()])
        elif line.startswith('# '):
            continue  # H1 = judul utama, bukan block
        elif re.match(r'^[-*]\s+', line):
            if blocks and blocks[-1][0]=='ul': blocks[-1][1].append(line[2:].strip())
            else: blocks.append(['ul',[line[2:].strip()]])
        elif re.match(r'^\d+[\.\)]\s+', line):
            if blocks and blocks[-1][0]=='ul': blocks[-1][1].append(re.sub(r'^\d+[\.\)]\s+','',line))
            else: blocks.append(['ul',[re.sub(r'^\d+[\.\)]\s+','',line)]])
        else:
            blocks.append(['p', line])
    blocks=[b for b in blocks if (b[1] if isinstance(b[1],str) else b[1])]
    if not title or not blocks: return []
    return [{
        'judul':title,'slug':slug or slugify(title),'meta_desc':meta,'focus_kw':kw or title,
        'categories':cats,'tags':tags,'content':blocks,'_src':os.path.basename(path)
    }]

def parse_leit_md(path):
    """LEIT format: judul (text biasa line1) + `1.` / `-` / paragraf singkat."""
    try: t=open(path,encoding='utf-8',errors='ignore').read()
    except: return []
    lines=[l.rstrip() for l in t.split('\n') if l.strip()]
    if not lines: return []
    title=lines[0].strip()
    blocks=[]
    for line in lines[1:]:
        line=line.strip()
        if re.match(r'^\d+[\.\)]\s+', line):
            blocks.append(['h2', re.sub(r'^\d+[\.\)]\s+','',line)])
        elif re.match(r'^[-*]\s+', line):
            if blocks and blocks[-1][0]=='ul': blocks[-1][1].append(line[2:].strip())
            else: blocks.append(['ul',[line[2:].strip()]])
        else:
            blocks.append(['p', line])
    if not blocks: return []
    return [{'judul':title,'slug':slugify(title),'meta_desc':title[:155],
             'focus_kw':title.split(':')[0],'categories':[],'tags':[],
             'content':blocks,'_src':os.path.basename(path)}]

def parse_py_articles(path):
    """Format everybe_10_artikel.py: articles=[{title,slug,excerpt,categories,content(html)}]."""
    try: src=open(path,encoding='utf-8',errors='ignore').read()
    except: return []
    if 'articles = [' not in src: return []
    idx=src.index('articles = ['); idx_end=src.index('def ',idx) if 'def ' in src[idx:] else len(src)
    block=src[idx:idx_end]
    ns={}
    try:
        exec('articles='+block.replace('articles = ','',1).rsplit(']',1)[0]+']', ns)
    except Exception as e:
        return []
    arts=[]
    meta_descs=[]
    mm=re.search(r'meta_descs\s*=\s*\[(.*?)\]', src, re.S)
    if mm: meta_descs=[x.strip().strip('"\'') for x in mm.group(1).split(',') if x.strip()]
    for i,a in enumerate(ns['articles']):
        blocks=html_to_blocks(a['content'])
        if not blocks: continue
        arts.append({
            'judul':a['title'],'slug':a.get('slug') or slugify(a['title']),
            'meta_desc':(meta_descs[i] if i<len(meta_descs) else a.get('excerpt','')[:155]),
            'focus_kw':a['title'].split(':')[0],'categories':[],
            'tags':[],'content':blocks,'_src':os.path.basename(path)
        })
    return arts

# ---------------------------------------------------------------- sumber
SOURCES = {
  'EveryBe':    [('parse_py_articles','everybe/everybe_10_artikel.py')],
  'Apotek':     [('parse_txt_seo','artikel_seo/apotek_bahaya-antibiotik-tanpa-resep-dokter.txt'),
                 ('parse_txt_seo','artikel_seo/apotek_cara-mengatasi-demam-anak.txt'),
                 ('parse_txt_seo','artikel_seo/apotek_vitamin-untuk-daya-tahan-tubuh.txt')],
  'Abimata':    [('parse_txt_abimata','10_artikel_abimata.txt')],
  'TSC':        [( 'parse_md_seo', f ) for f in glob.glob('the-skin-code-articles/*.md')],
  'Nucleasoft': [( 'parse_md_seo', f ) for f in glob.glob('nucleasoft-articles/*.md')],
  'DAS':        [( 'parse_md_seo', f ) for f in glob.glob('artikel-das-20/*.md')] +
                 [( 'parse_md_seo', f ) for f in glob.glob('artikel-das/*.md')],
  'TSLaw':      [( 'parse_txt_seo', 'artikel_seo/ttalaw_cara-daftar-merek-dagang.txt'),
                 ('parse_txt_seo','artikel_seo/ttalaw_kontrak-bisnis-yang-aman.txt'),
                 ('parse_txt_seo','artikel_seo/ttalaw_panduan-mendirikan-pt-untuk-umkm.txt'),
                 ('parse_md_seo','artikel_seo_tslaw_pendampingan.md')],
  'SSC':        [( 'parse_frontmatter_md', f ) for f in glob.glob('artikel_ssc*.md')],
  'Yasma':      [('parse_frontmatter_md','artikel_yasmanatura_13072026.md'),
                 ('parse_frontmatter_md','yasma_listicle_draf.md')],
  'LEIT':       [( 'parse_leit_md', f ) for f in glob.glob('artikel_leit_*.md')],
}

def main():
    pool={}
    for client,srcs in SOURCES.items():
        arts=[]
        for fn,path in srcs:
            path=os.path.join(WS,path)
            if not os.path.exists(path): continue
            try:
                res=globals()[fn](path)
                arts+=res
            except Exception as e:
                print(f'  ! {client} {path}: {e}')
        # dedup by slug dalam satu client
        seen={}; ded=[]
        for a in arts:
            k=norm(a['slug'] or a['judul'])
            if k in seen: continue
            seen[k]=1
            a['_hash']=hashlib.md5(json.dumps(a['content'],ensure_ascii=False).encode()).hexdigest()[:8]
            ded.append(a)
        pool[client]=ded
        print(f'{client}: {len(ded)} artikel unik')
    out=os.path.join(WS,'stok_artikel_web.json')
    json.dump(pool,open(out,'w'),ensure_ascii=False,indent=1)
    print(f'\n✅ Selesai → {out}')

if __name__=='__main__':
    main()
