# -*- coding: utf-8 -*-
"""
STEP105-15 V2 Publish Dialog Handler - Latest DOM 대응

역할:
- V2 전용 발행 설정창 분석 모듈.
- 기존 layer_popup/layer_publish 구조뿐 아니라
  최신 네이버 DOM의 publish_btn_area / confirm_btn / publish_fold_btn 구조도 허용한다.
- 실제 최종 발행은 하지 않는다.

V1 publish_worker.py는 수정하지 않는다.
"""


class V2PublishDialogHandler:
    def __init__(self, verbose=True):
        self.verbose = verbose

    def inspect(self, page, write_frame, logger=None, session=None):
        def _run():
            return self._inspect_impl(page=page, write_frame=write_frame)

        if session and logger:
            with session.step("publish.dialog.inspect", logger=logger):
                return _run()

        return _run()

    def _inspect_impl(self, page, write_frame):
        layer = self._find_publish_layer(write_frame)

        if not layer:
            raise RuntimeError("발행 설정창을 찾지 못했습니다.")

        state = {
            "found": True,
            "url": page.url,
            "layer": layer,
            "visibility": self._inspect_visibility(write_frame),
            "options": self._inspect_options(write_frame),
            "tags": self._inspect_tags(write_frame),
            "category": self._inspect_category(write_frame),
            "topic": self._inspect_topic(write_frame),
            "final_button": self._find_final_publish_button(write_frame),
            "close_button": self._find_close_button(write_frame),
        }

        print("[V2 PUBLISH DIALOG INSPECT]", state)

        return state

    def _find_publish_layer(self, write_frame):
        """
        최신 DOM 대응:
        - 예전: layer_popup__... is_show, layer_publish__...
        - 현재 로그 확인: publish_btn_area__... 안에 카테고리/공개설정/태그/발행 버튼이 있음
        """
        selectors = [
            ".layer_popup__i0QOY.is_show__TMSLq",
            "[class*='layer_popup'][class*='is_show']",
            "[class*='layer_publish']",
            "[class*='layer_content_set_publish']",
            "[class*='publish_btn_area']",
        ]

        for selector in selectors:
            try:
                locator = write_frame.locator(selector)
                count = locator.count()

                if self.verbose:
                    print("[V2 PUBLISH DIALOG LAYER CHECK]", selector, count)

                if count <= 0:
                    continue

                for idx in range(min(count, 10)):
                    item = locator.nth(idx)

                    visible = False
                    text = ""
                    cls = ""

                    try:
                        visible = item.is_visible(timeout=1000)
                    except Exception:
                        pass

                    try:
                        text = str(item.inner_text(timeout=1000) or "").strip()
                    except Exception:
                        pass

                    try:
                        cls = str(item.get_attribute("class") or "").strip()
                    except Exception:
                        pass

                    if not visible:
                        continue

                    # 설정창으로 인정할 핵심 신호
                    has_publish_settings = (
                        "카테고리" in text
                        and "공개 설정" in text
                        and "태그" in text
                        and "발행" in text
                    )

                    has_final_button = False
                    try:
                        has_final_button = write_frame.locator("button[class*='confirm_btn']").count() > 0
                    except Exception:
                        pass

                    if has_publish_settings or has_final_button:
                        return {
                            "selector": selector,
                            "index": idx,
                            "visible": visible,
                            "class": cls,
                            "text_sample": text[:700],
                            "detected_by": (
                                "publish_settings_text"
                                if has_publish_settings
                                else "confirm_button"
                            ),
                        }

            except Exception as e:
                if self.verbose:
                    print("[V2 PUBLISH DIALOG LAYER ERROR]", selector, str(e))

        # 최후 fallback: confirm_btn이 보이면 발행 설정창이 열려 있다고 판단
        try:
            confirm = write_frame.locator("button[class*='confirm_btn']").first
            if confirm.count() > 0 and confirm.is_visible(timeout=1000):
                text = str(confirm.inner_text(timeout=1000) or "").strip()
                if text == "발행":
                    return {
                        "selector": "button[class*='confirm_btn']",
                        "index": 0,
                        "visible": True,
                        "class": str(confirm.get_attribute("class") or "").strip(),
                        "text_sample": text,
                        "detected_by": "visible_confirm_button_fallback",
                    }
        except Exception:
            pass

        return None

    def _get_dialog_text(self, write_frame):
        selectors = [
            "[class*='layer_popup'][class*='is_show']",
            "[class*='layer_publish']",
            "[class*='layer_content_set_publish']",
            "[class*='publish_btn_area']",
        ]

        for selector in selectors:
            try:
                locator = write_frame.locator(selector)
                count = locator.count()
                if count <= 0:
                    continue

                for idx in range(min(count, 10)):
                    item = locator.nth(idx)
                    try:
                        if not item.is_visible(timeout=500):
                            continue
                    except Exception:
                        pass

                    text = str(item.inner_text(timeout=1000) or "").strip()
                    if "카테고리" in text and "발행" in text:
                        return text
            except Exception:
                pass

        try:
            return str(write_frame.locator("body").inner_text(timeout=1000) or "").strip()
        except Exception:
            return ""

    def _inspect_category(self, write_frame):
        result = {"text": "", "found": False}
        lines = [x.strip() for x in self._get_dialog_text(write_frame).splitlines() if x.strip()]

        for i, line in enumerate(lines):
            if line == "카테고리" and i + 1 < len(lines):
                result["text"] = lines[i + 1]
                result["found"] = True
                return result

        return result

    def _inspect_topic(self, write_frame):
        result = {"text": "", "found": False}
        lines = [x.strip() for x in self._get_dialog_text(write_frame).splitlines() if x.strip()]

        for i, line in enumerate(lines):
            if line == "주제" and i + 1 < len(lines):
                result["text"] = lines[i + 1]
                result["found"] = True
                return result

        return result

    def _inspect_visibility(self, write_frame):
        result = {
            "found": False,
            "selected": "",
            "candidates": [],
        }

        visibility_labels = [
            "전체공개",
            "이웃공개",
            "서로이웃공개",
            "비공개",
        ]

        dialog_text = self._get_dialog_text(write_frame)

        for label in visibility_labels:
            info = {
                "label": label,
                "count": 0,
                "visible": False,
                "selected_like": False,
            }

            try:
                locator = write_frame.locator(f"text={label}")
                count = locator.count()
                info["count"] = count

                if count > 0:
                    try:
                        info["visible"] = locator.first.is_visible(timeout=1000)
                    except Exception:
                        pass

                    try:
                        parent_class = locator.first.evaluate(
                            """el => {
                                let p = el;
                                for (let i = 0; i < 5 && p; i++, p = p.parentElement) {
                                    const cls = p.getAttribute && p.getAttribute('class');
                                    const aria = p.getAttribute && p.getAttribute('aria-checked');
                                    if (
                                        aria === 'true' ||
                                        (cls && (
                                            cls.includes('is_active') ||
                                            cls.includes('selected') ||
                                            cls.includes('checked') ||
                                            cls.includes('on')
                                        ))
                                    ) {
                                        return cls || ('aria-checked=' + aria);
                                    }
                                }
                                return '';
                            }"""
                        )
                        if parent_class:
                            info["selected_like"] = True
                            info["parent_class"] = parent_class
                    except Exception:
                        pass

            except Exception:
                pass

            if info["count"] > 0 or label in dialog_text:
                result["candidates"].append(info)

            if info.get("selected_like"):
                result["selected"] = label

        result["found"] = len(result["candidates"]) > 0

        # 선택 상태를 못 읽으면 기본값으로 전체공개 추정
        if not result["selected"] and "전체공개" in dialog_text:
            result["selected"] = "전체공개"

        return result

    def _inspect_options(self, write_frame):
        labels = [
            "댓글허용",
            "공감허용",
            "검색허용",
            "블로그/카페 공유",
            "링크 허용",
            "외부 공유 허용",
            "이 설정을 기본값으로 유지",
            "공지사항으로 등록",
        ]

        result = {}
        dialog_text = self._get_dialog_text(write_frame)

        for label in labels:
            info = {
                "found": label in dialog_text,
                "count": 0,
                "visible": False,
                "checked_like": None,
            }

            try:
                locator = write_frame.locator(f"text={label}")
                count = locator.count()

                info["count"] = count
                info["found"] = info["found"] or count > 0

                if count > 0:
                    try:
                        info["visible"] = locator.first.is_visible(timeout=1000)
                    except Exception:
                        pass

                    try:
                        parent = locator.first.evaluate(
                            """el => {
                                let p = el;
                                for (let i = 0; i < 5 && p; i++, p = p.parentElement) {
                                    const cls = p.getAttribute && p.getAttribute('class');
                                    const aria = p.getAttribute && p.getAttribute('aria-checked');
                                    const checked = p.getAttribute && p.getAttribute('checked');
                                    if (
                                        aria ||
                                        checked ||
                                        (cls && (
                                            cls.includes('checked') ||
                                            cls.includes('active') ||
                                            cls.includes('on')
                                        ))
                                    ) {
                                        return {className: cls || '', ariaChecked: aria || '', checked: checked || ''};
                                    }
                                }
                                return null;
                            }"""
                        )
                        if parent:
                            info["checked_like"] = parent
                    except Exception:
                        pass

            except Exception:
                pass

            result[label] = info

        return result

    def _inspect_tags(self, write_frame):
        result = {
            "found": False,
            "tags": [],
            "raw_text": "",
        }

        text = self._get_dialog_text(write_frame)
        result["raw_text"] = text[:1200]

        tags = []
        for token in text.replace("\n", " ").split():
            if token.startswith("#"):
                # 네이버 태그 영역이 붙어서 나올 수 있으므로 일단 raw token 보존
                tags.append(token.strip())

        result["tags"] = tags
        result["found"] = len(tags) > 0

        return result

    def _find_final_publish_button(self, write_frame):
        selectors = [
            "[class*='layer_btn_area'] button:has-text('발행')",
            "button[class*='confirm_btn']",
            "[class*='publish_btn_area'] button[class*='confirm_btn']",
            "[class*='layer_publish'] button:has-text('발행')",
            "button:has-text('발행')",
        ]

        candidates = []

        for selector in selectors:
            try:
                locator = write_frame.locator(selector)
                count = locator.count()

                if self.verbose:
                    print("[V2 FINAL PUBLISH BUTTON CHECK]", selector, count)

                if count <= 0:
                    continue

                for idx in range(min(count, 10)):
                    item = locator.nth(idx)

                    info = {
                        "selector": selector,
                        "index": idx,
                        "count": count,
                        "visible": False,
                        "enabled": False,
                        "text": "",
                        "class": "",
                    }

                    try:
                        info["visible"] = item.is_visible(timeout=1000)
                    except Exception:
                        pass

                    try:
                        info["enabled"] = item.is_enabled(timeout=1000)
                    except Exception:
                        pass

                    try:
                        info["text"] = str(item.inner_text(timeout=1000) or "").strip()
                    except Exception:
                        pass

                    try:
                        info["class"] = str(item.get_attribute("class") or "").strip()
                    except Exception:
                        pass

                    if info["visible"] and info["enabled"] and info["text"] == "발행":
                        candidates.append(info)

            except Exception as e:
                if self.verbose:
                    print("[V2 FINAL PUBLISH BUTTON ERROR]", selector, str(e))

        if not candidates:
            return {
                "found": False,
                "candidates": [],
            }

        # confirm_btn 우선, publish_btn은 1차 발행 버튼이라 후순위
        candidates = sorted(
            candidates,
            key=lambda x: (
                2 if "confirm_btn" in str(x.get("class") or "") else 0,
                1 if "layer_btn_area" in str(x.get("selector") or "") else 0,
                -1 if "publish_btn__" in str(x.get("class") or "") else 0,
            ),
            reverse=True,
        )

        return {
            "found": True,
            "best": candidates[0],
            "candidates": candidates,
        }

    def _find_close_button(self, write_frame):
        selectors = [
            "button[class*='publish_fold_btn']",
            "[class*='publish_fold_btn']",
            "button:has-text('발행 설정 닫기')",
            "button:has-text('발행 설정 열기')",
        ]

        for selector in selectors:
            try:
                locator = write_frame.locator(selector)
                count = locator.count()

                if count <= 0:
                    continue

                for idx in range(min(count, 5)):
                    item = locator.nth(idx)

                    visible = False
                    text = ""
                    cls = ""

                    try:
                        visible = item.is_visible(timeout=1000)
                    except Exception:
                        pass

                    try:
                        text = str(item.inner_text(timeout=1000) or "").strip()
                    except Exception:
                        pass

                    try:
                        cls = str(item.get_attribute("class") or "").strip()
                    except Exception:
                        pass

                    if visible:
                        return {
                            "found": True,
                            "selector": selector,
                            "index": idx,
                            "text": text,
                            "class": cls,
                        }

            except Exception:
                pass

        return {
            "found": False,
        }


def inspect_publish_dialog(page, write_frame, logger=None, session=None, verbose=True):
    handler = V2PublishDialogHandler(verbose=verbose)
    return handler.inspect(
        page=page,
        write_frame=write_frame,
        logger=logger,
        session=session,
    )
