import subprocess, json, os, sys, time, requests, websocket
from datetime import datetime, timezone

BASE = os.path.expanduser("~/Claude/Projects/에이전트구축")
XPROF = os.path.join(BASE, ".xchrome_profile")
FEED = os.path.join(BASE, "x_feed.jsonl")
LOGDIR = os.path.join(BASE, "logs")
LOG = os.path.join(LOGDIR, "xfeed.log")
DUMP = os.path.join(LOGDIR, "xfeed_dump.txt")
CHROME = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
PORT = 9333

PAGE_WAIT = 60
ARTICLE_WAIT = 45


def log(msg):
    os.makedirs(LOGDIR, exist_ok=True)
    line = f"[{datetime.now().astimezone().strftime('%Y-%m-%d %H:%M:%S')}] {msg}"
    print(line)
    with open(LOG, "a") as f:
        f.write(line + "\n")


EXTRACT_JS = r"""
(function(){
  var a=document.getElementsByTagName('article');var o=[];
  for(var i=0;i<a.length;i++){
    var tm=a[i].getElementsByTagName('time')[0];var link='';
    if(tm){var p=tm.parentNode;while(p&&p.tagName!='A'){p=p.parentNode}if(p)link=p.href}
    o.push((tm?tm.getAttribute('datetime'):'')+'@@@'+a[i].innerText.replace(/\n/g,' ')+'@@@'+link)
  }
  return o.join('###');
})()
"""

PROBE_JS = r"""
(function(){
  return JSON.stringify({
    ua: navigator.userAgent,
    wd: navigator.webdriver,
    url: location.href,
    articles: document.getElementsByTagName('article').length,
    body: (document.body ? document.body.innerText : '').slice(0,300)
  });
})()
"""


def ensure_session():
    cookies = os.path.join(XPROF, "Default", "Cookies")
    if os.path.exists(cookies):
        log("세션: 기존 프로필 사용 (복사 안 함)")
        return
    src = os.path.expanduser("~/Library/Application Support/Google/Chrome")
    dst = os.path.join(XPROF, "Default")
    os.makedirs(dst, exist_ok=True)
    subprocess.run(["cp", os.path.join(src, "Default", "Cookies"), dst], capture_output=True)
    subprocess.run(["cp", "-R", os.path.join(src, "Default", "Local Storage"), dst], capture_output=True)
    subprocess.run(["cp", os.path.join(src, "Local State"), XPROF], capture_output=True)
    log("세션: 프로필 신규 생성 (메인 크롬에서 쿠키 복사)")


def kill_chrome():
    subprocess.run(["pkill", "-f", f"remote-debugging-port={PORT}"], capture_output=True)


def launch_chrome():
    kill_chrome()
    time.sleep(1)
    return subprocess.Popen([
        CHROME,
        f"--remote-debugging-port={PORT}",
        "--remote-allow-origins=*",
        f"--user-data-dir={XPROF}",
        "--window-position=-3000,-3000",
        "--window-size=1400,1000",
        "--disable-blink-features=AutomationControlled",
        "--disable-background-timer-throttling",
        "--disable-backgrounding-occluded-windows",
        "--disable-renderer-backgrounding",
        "--no-first-run",
        "--no-default-browser-check",
        "--disable-features=Translate,OptimizationHints",
        "https://x.com/home",
    ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)


def get_page():
    last = "탭 조회 시도 없음"
    deadline = time.time() + PAGE_WAIT
    while time.time() < deadline:
        try:
            tabs = requests.get(f"http://localhost:{PORT}/json", timeout=5).json()
            page = next((t for t in tabs if t.get("type") == "page" and t.get("webSocketDebuggerUrl")), None)
            if page:
                return page
            last = f"page 타입 탭 없음 (탭 {len(tabs)}개: {[t.get('type') for t in tabs]})"
        except Exception as e:
            last = f"{type(e).__name__}: {e}"
        time.sleep(1)
    raise RuntimeError(f"CDP 페이지 {PAGE_WAIT}초 내 못 붙음 — 마지막 사유: {last}")


def cdp_extract():
    page = get_page()
    ws = websocket.create_connection(page["webSocketDebuggerUrl"], timeout=30,
                                     header=[f"Origin: http://localhost:{PORT}"])
    mid = [0]

    def cmd(m, p=None, retries=2):
        for attempt in range(retries + 1):
            try:
                mid[0] += 1
                myid = mid[0]
                ws.send(json.dumps({"id": myid, "method": m, "params": p or {}}))
                while True:
                    r = json.loads(ws.recv())
                    if r.get("id") == myid:
                        return r
            except (ConnectionResetError, websocket.WebSocketException, OSError):
                if attempt >= retries:
                    raise
                time.sleep(1.5)

    def evaluate(js):
        r = cmd("Runtime.evaluate", {"expression": js, "returnByValue": True})
        return r["result"]["result"].get("value", "")

    cmd("Runtime.enable")

    probe = {}
    deadline = time.time() + ARTICLE_WAIT
    while time.time() < deadline:
        try:
            probe = json.loads(evaluate(PROBE_JS) or "{}")
        except Exception:
            probe = {}
        if probe.get("articles", 0) > 0:
            break
        time.sleep(2)

    ua = probe.get("ua", "")
    log(f"페이지 상태: url={probe.get('url')} articles={probe.get('articles')} webdriver={probe.get('wd')}")
    if "HeadlessChrome" in ua:
        log("경고: UA에 HeadlessChrome 토큰 남아있음 — X가 차단할 가능성 높음")

    if not probe.get("articles"):
        os.makedirs(LOGDIR, exist_ok=True)
        with open(DUMP, "w") as f:
            f.write(json.dumps(probe, ensure_ascii=False, indent=2))
        body = (probe.get("body") or "").replace("\n", " ")[:200]
        try:
            ws.close()
        except Exception:
            pass
        raise RuntimeError(f"article 0개 — url={probe.get('url')} / 본문앞부분={body!r} (전체는 {DUMP})")

    for _ in range(6):
        try:
            evaluate("window.scrollBy(0,1000)")
        except Exception:
            break
        time.sleep(1.5)

    try:
        val = evaluate(EXTRACT_JS)
    except Exception as e:
        raise RuntimeError(f"추출 실패: {type(e).__name__}: {e}")
    finally:
        try:
            ws.close()
        except Exception:
            pass
    return val


def main():
    ensure_session()
    launch_chrome()
    try:
        raw = cdp_extract()
    except Exception as e:
        log(f"실패: {e}")
        kill_chrome()
        sys.exit(1)
    finally:
        kill_chrome()

    existing = set()
    if os.path.exists(FEED):
        for line in open(FEED):
            try:
                o = json.loads(line)
                existing.add(o.get("link") or o.get("text", "")[:80])
            except Exception:
                pass

    seen, new = 0, 0
    with open(FEED, "a") as f:
        for item in raw.split("###"):
            parts = item.split("@@@")
            if len(parts) < 2:
                continue
            dt, text, link = (parts + ["", ""])[:3]
            key = link or text[:80]
            if not text.strip():
                continue
            seen += 1
            if key in existing:
                continue
            existing.add(key)
            f.write(json.dumps({
                "datetime": dt, "text": text.strip(), "link": link,
                "collected_at": datetime.now(timezone.utc).isoformat()
            }, ensure_ascii=False) + "\n")
            new += 1

    if new == 0:
        log(f"경고: 신규 0건 (파싱된 트윗 {seen}개, 전부 기존과 중복) — 피드가 안 갱신되는지 확인 필요")
    else:
        log(f"{new}개 신규 수집 (파싱 {seen}개)")


if __name__ == "__main__":
    main()