#!/usr/bin/env python3
"""
lt.py - Convert LightText to .html, .md, .rtf

LightText syntax (only 5 rules):
  Headings:    ALL-CAPS line/block with blank lines before and after
  Bold/Italic: *bold* and _italic_
  Lists:       - bullet lines
  Links:       [label > url]
  Code:        4-space indented lines

Usage:
  python3 lt.py input.txt output.html
  python3 lt.py input.txt output.md
  python3 lt.py input.txt output.rtf
"""

import sys
import os
import re
import html

def is_all_caps(s):
    t = s.strip()
    return len(t) > 0 and any(c.isalpha() for c in t) and t == t.upper()

# ---------- inline parsers (fixed regexes for Python 3.14) ----------

LINK_RE = re.compile(r'\[([^\]\n]+?)\s*>\s*([^\]\n]+?)\]')
BOLD_RE = re.compile(r'\*([^*]+?)\*')
ITALIC_RE = re.compile(r'_([^_]+?)_')

def parse_inline_html(s):
    # 1. extract links to placeholders
    links = []
    def link_placeholder(m):
        label = m.group(1).strip()
        url = m.group(2).strip()
        links.append((label, url))
        return f"\x00LINK{len(links)-1}\x00"
    s = LINK_RE.sub(link_placeholder, s)

    # 2. escape the rest
    s = html.escape(s)

    # 3. bold / italic (operate on escaped text, * and _ are not escaped)
    s = BOLD_RE.sub(r'<strong>\1</strong>', s)
    s = ITALIC_RE.sub(r'<em>\1</em>', s)

    # 4. restore links
    for i, (label, url) in enumerate(links):
        safe_label = html.escape(label)
        safe_url = html.escape(url, quote=True)
        s = s.replace(f"\x00LINK{i}\x00", f'<a href="{safe_url}">{safe_label}</a>')
    return s

def parse_inline_md(s):
    # [label > url] -> [label](url), *bold* -> **bold**, _italic_ stays *italic* for markdown
    s = LINK_RE.sub(lambda m: f'[{m.group(1).strip()}]({m.group(2).strip()})', s)
    s = BOLD_RE.sub(r'**\1**', s)
    s = ITALIC_RE.sub(r'*\1*', s)
    return s

def esc_rtf(s):
    return s.replace('\\', r'\\').replace('{', r'\{').replace('}', r'\}')

def inline_rtf(s):
    # Links -> label (url)
    s = LINK_RE.sub(lambda m: f"{esc_rtf(m.group(1).strip())} ({esc_rtf(m.group(2).strip())})", s)
    s = BOLD_RE.sub(lambda m: r'{\b ' + esc_rtf(m.group(1)) + '}', s)
    s = ITALIC_RE.sub(lambda m: r'{\i ' + esc_rtf(m.group(1)) + '}', s)
    # escape any remaining plain text chars that are special in RTF
    # we already escaped inside lambdas, now escape rest but avoid double-escaping {\b ...}
    # simple approach: split by RTF groups and escape text parts
    # for this minimal impl, escape the whole string then unescape our inserted groups markers
    # easier: escape before regex would be safer, but we do it after with care:
    # we already handled \ { } inside esc_rtf calls, remaining text may still contain \ { }
    # so escape leftover backslash/braces that are not part of our groups
    # To keep it simple, assume input has no stray \ { } outside markup.
    return s

def title_case_allcaps(s):
    return ' '.join(w.capitalize() for w in s.strip().lower().split())

def lighttext_to_html_body(text):
    lines = text.splitlines()
    out = []
    i = 0
    n = len(lines)
    while i < n:
        line = lines[i]
        if line.startswith('    ') and line.strip() != '':
            code_lines = []
            while i < n and (lines[i].startswith('    ') or lines[i].strip() == ''):
                if lines[i].startswith('    '):
                    code_lines.append(lines[i][4:])
                elif lines[i].strip() == '' and code_lines:
                    if i+1 < n and lines[i+1].startswith('    '):
                        code_lines.append('')
                    else:
                        break
                else:
                    break
                i += 1
            out.append(f'<pre><code>{html.escape(chr(10).join(code_lines))}</code></pre>')
            continue

        if line.strip() == '':
            i += 1
            continue

        if line.lstrip().startswith('- '):
            items = []
            while i < n and lines[i].lstrip().startswith('- '):
                content = lines[i].lstrip()[2:]
                items.append(f'<li>{parse_inline_html(content)}</li>')
                i += 1
            out.append('<ul>\n' + '\n'.join(items) + '\n</ul>')
            continue

        prev_blank = (i == 0 or lines[i-1].strip() == '')
        next_blank = (i+1 == n or lines[i+1].strip() == '')
        if prev_blank and next_blank and is_all_caps(line):
            out.append(f'<h2>{parse_inline_html(line.strip())}</h2>')
            i += 1
            continue

        para_lines = []
        while i < n and lines[i].strip() != '' and not lines[i].startswith('    ') and not lines[i].lstrip().startswith('- ') and not ((i==0 or lines[i-1].strip()=='') and (i+1==n or lines[i+1].strip()=='') and is_all_caps(lines[i])):
            para_lines.append(lines[i].strip())
            i += 1
        para_text = ' '.join(para_lines)
        out.append(f'<p>{parse_inline_html(para_text)}</p>')

    return '\n'.join(out)

def lighttext_to_html_full(text, title="LightText Document"):
    body = lighttext_to_html_body(text)
    return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{html.escape(title)}</title>
<style>
  body{{max-width:720px;margin:3rem auto;padding:0 1.5rem;font-family:Georgia,serif;line-height:1.7;color:#000;background:#fff}}
  h2{{font-family:system-ui,sans-serif;letter-spacing:.04em;margin-top:3rem;font-size:1.25rem;color:#000}}
  a{{color:#0000ee;text-decoration:underline;text-underline-offset:3px}}
  ul{{padding-left:1.2rem}} li{{margin:.3rem 0}}
  pre{{background:#f5f5f5;padding:1rem;overflow:auto;border-radius:8px;font-size:.9rem}} code{{font-family:ui-monospace,Menlo,monospace}}
  p{{margin:1rem 0}}
</style>
</head>
<body>
{body}
</body>
</html>
"""

def lighttext_to_markdown(text):
    lines = text.splitlines()
    out = []
    i = 0
    n = len(lines)
    while i < n:
        line = lines[i]
        if line.startswith('    ') and line.strip() != '':
            out.append('```')
            while i < n and (lines[i].startswith('    ') or (lines[i].strip()=='' and i+1<n and lines[i+1].startswith('    '))):
                if lines[i].startswith('    '):
                    out.append(lines[i][4:])
                else:
                    out.append('')
                i+=1
            out.append('```')
            out.append('')
            continue
        if line.strip() == '':
            out.append('')
            i+=1
            continue
        if line.lstrip().startswith('- '):
            while i < n and lines[i].lstrip().startswith('- '):
                out.append('- ' + parse_inline_md(lines[i].lstrip()[2:]))
                i+=1
            out.append('')
            continue
        prev_blank = (i==0 or lines[i-1].strip()=='')
        next_blank = (i+1==n or lines[i+1].strip()=='')
        if prev_blank and next_blank and is_all_caps(line):
            out.append('# ' + title_case_allcaps(line))
            out.append('')
            i+=1
            continue
        para=[]
        while i<n and lines[i].strip()!='' and not lines[i].startswith('    ') and not lines[i].lstrip().startswith('- ') and not ((i==0 or lines[i-1].strip()=='') and (i+1==n or lines[i+1].strip()=='') and is_all_caps(lines[i])):
            para.append(lines[i].strip())
            i+=1
        out.append(parse_inline_md(' '.join(para)))
        out.append('')
    return '\n'.join(out).strip()+'\n'

def lighttext_to_rtf(text):
    lines = text.splitlines()
    rtf_body = []
    i=0; n=len(lines)
    while i<n:
        line=lines[i]
        if line.startswith('    ') and line.strip()!='':
            rtf_body.append(r'\pard\cbpat1 \f1\fs20')
            while i<n and (lines[i].startswith('    ') or (lines[i].strip()=='' and i+1<n and lines[i+1].startswith('    '))):
                content = lines[i][4:] if lines[i].startswith('    ') else ''
                rtf_body.append(esc_rtf(content) + r'\line')
                i+=1
            rtf_body.append(r'\f0\fs24\par')
            continue
        if line.strip()=='':
            rtf_body.append(r'\par')
            i+=1
            continue
        if line.lstrip().startswith('- '):
            while i<n and lines[i].lstrip().startswith('- '):
                item = inline_rtf(lines[i].lstrip()[2:])
                rtf_body.append(r'\pard{\pntext\f0 \bullet\tab}{\*\pn\pnlvlblt\pnf0\pnindent0{\pntxtb\bullet}} ' + item + r'\par')
                i+=1
            continue
        prev_blank=(i==0 or lines[i-1].strip()=='')
        next_blank=(i+1==n or lines[i+1].strip()=='')
        if prev_blank and next_blank and is_all_caps(line):
            rtf_body.append(r'\pard\sb480\sa240\fs32\b ' + esc_rtf(line.strip()) + r'\b0\fs24\par')
            i+=1
            continue
        para=[]
        while i<n and lines[i].strip()!='' and not lines[i].startswith('    ') and not lines[i].lstrip().startswith('- ') and not ((i==0 or lines[i-1].strip()=='') and (i+1==n or lines[i+1].strip()=='') and is_all_caps(lines[i])):
            para.append(lines[i].strip())
            i+=1
        rtf_body.append(r'\pard ' + inline_rtf(' '.join(para)) + r'\par')
    header = r'{\rtf1\ansi\ansicpg1252\deff0{\fonttbl{\f0\froman\fcharset0 Times New Roman;}{\f1\fmodern\fcharset0 Courier;}}\f0\fs24'
    footer = '}'
    return header + '\n' + '\n'.join(rtf_body) + '\n' + footer

def main():
    if len(sys.argv) != 3:
        print(f"Usage: {sys.argv[0]} input.txt output.[html|md|rtf]")
        sys.exit(1)
    inp, outp = sys.argv[1], sys.argv[2]
    if not os.path.exists(inp):
        print(f"Input not found: {inp}"); sys.exit(1)
    with open(inp, 'r', encoding='utf-8') as f:
        text = f.read()
    ext = os.path.splitext(outp)[1].lower()
    if ext == '.html':
        result = lighttext_to_html_full(text, title=os.path.basename(inp))
    elif ext == '.md':
        result = lighttext_to_markdown(text)
    elif ext == '.rtf':
        result = lighttext_to_rtf(text)
    else:
        print(f"Unknown output extension {ext}. Use .html, .md, or .rtf"); sys.exit(1)
    with open(outp, 'w', encoding='utf-8') as out:
        out.write(result)
    print(f"Wrote {outp}")

if __name__ == '__main__':
    main()
