# -*- coding: utf-8 -*-
"""
STEP107-39: V2 Naver Auto Relogin

V1 publish_worker의 자동 재로그인 흐름을 V2 전용 서비스로 분리.

원칙:
- 사용자 직접 로그인 대기 없음.
- DB blog_naver_accounts의 naver_id/naver_password_enc를 사용.
- APP_CRYPTO_KEY로 PHP openssl_encrypt(AES-256-CBC) 저장값 복호화.
- 로그인 성공 시 storage/naver_sessions/naver_session_{realtor_id}.json 재저장.
"""

import sys
from pathlib import Path
from urllib.parse import urlparse, parse_qs, unquote, quote

ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
    sys.path.insert(0, str(ROOT))

from db import get_conn

try:
    from config import APP_CRYPTO_KEY
except Exception:
    APP_CRYPTO_KEY = ""

try:
    from Crypto.Cipher import AES
    CRYPTO_AVAILABLE = True
except Exception:
    AES = None
    CRYPTO_AVAILABLE = False


def is_login_page_url(url):
    url = str(url or "").lower()
    return (
        "nid.naver.com" in url
        or "nidlogin.login" in url
        or ("login" in url and "naver.com" in url)
    )


def normalize_aes_key(key_text):
    key = str(key_text or "").encode("utf-8")
    if len(key) >= 32:
        return key[:32]
    return key.ljust(32, b"\0")


def php_openssl_decrypt(encrypted_text):
    if not encrypted_text:
        return ""

    if not CRYPTO_AVAILABLE:
        raise Exception("pycryptodome 설치 필요: pip install pycryptodome")

    raw = str(encrypted_text or "").strip()

    try:
        import base64
        raw_bytes = base64.b64decode(raw, validate=True)
    except Exception:
        return ""

    if len(raw_bytes) <= 16:
        return ""

    iv = raw_bytes[:16]
    cipher_text = raw_bytes[16:]

    cipher = AES.new(
        normalize_aes_key(APP_CRYPTO_KEY),
        AES.MODE_CBC,
        iv,
    )

    decrypted = cipher.decrypt(cipher_text)
    pad_len = decrypted[-1]

    if pad_len < 1 or pad_len > 16:
        return ""

    return decrypted[:-pad_len].decode("utf-8", errors="ignore")


def update_naver_session_status(realtor_id, status, message="", session_file=""):
    conn = get_conn()
    try:
        # blog_naver_sessions 우선
        try:
            with conn.cursor() as cur:
                cur.execute("SHOW TABLES LIKE 'blog_naver_sessions'")
                if cur.fetchone():
                    cur.execute("""
                        INSERT INTO blog_naver_sessions
                        (
                            realtor_id,
                            status,
                            session_file,
                            last_checked_at,
                            last_login_ok_at,
                            last_error_message,
                            created_at,
                            updated_at
                        )
                        VALUES
                        (
                            %s,
                            %s,
                            %s,
                            NOW(),
                            CASE WHEN %s='linked' THEN NOW() ELSE NULL END,
                            %s,
                            NOW(),
                            NOW()
                        )
                        ON DUPLICATE KEY UPDATE
                            status=VALUES(status),
                            session_file=VALUES(session_file),
                            last_checked_at=NOW(),
                            last_login_ok_at=CASE WHEN VALUES(status)='linked' THEN NOW() ELSE last_login_ok_at END,
                            last_error_message=VALUES(last_error_message),
                            updated_at=NOW()
                    """, (
                        int(realtor_id),
                        str(status or "")[:30],
                        str(session_file or "")[:500],
                        str(status or "")[:30],
                        str(message or "")[:1000],
                    ))
                    conn.commit()
                    return True
        except Exception:
            pass

        # blog_naver_accounts fallback
        try:
            with conn.cursor() as cur:
                cur.execute("SHOW TABLES LIKE 'blog_naver_accounts'")
                if cur.fetchone():
                    cur.execute("""
                        UPDATE blog_naver_accounts
                        SET login_status=%s,
                            last_error_message=%s,
                            updated_at=NOW()
                        WHERE realtor_id=%s
                    """, (
                        str(status or "")[:30],
                        str(message or "")[:1000],
                        int(realtor_id),
                    ))
                    conn.commit()
                    return True
        except Exception:
            pass

        return False
    finally:
        try:
            conn.close()
        except Exception:
            pass


def get_naver_account_credentials(realtor_id):
    conn = get_conn()
    try:
        with conn.cursor() as cur:
            cur.execute("""
                SELECT
                    naver_id,
                    naver_password_enc,
                    consent_status,
                    login_status
                FROM blog_naver_accounts
                WHERE realtor_id = %s
                LIMIT 1
            """, (int(realtor_id),))
            row = cur.fetchone()

        if not row:
            return None, None, "등록된 네이버 계정 정보가 없습니다."

        if str(row.get("consent_status") or "").strip() != "agreed":
            return None, None, "네이버 자동발행 동의 상태가 아닙니다."

        naver_id = str(row.get("naver_id") or "").strip()
        encrypted_pw = str(row.get("naver_password_enc") or "").strip()

        if not naver_id:
            return None, None, "네이버 ID가 비어 있습니다."

        if not encrypted_pw:
            return None, None, "네이버 비밀번호 암호화 값이 비어 있습니다."

        try:
            naver_pw = php_openssl_decrypt(encrypted_pw)
        except Exception as e:
            return None, None, f"네이버 비밀번호 복호화 실패: {e}"

        if not naver_pw:
            return None, None, "네이버 비밀번호 복호화 결과가 비어 있습니다."

        return naver_id, naver_pw, ""

    finally:
        conn.close()


def detect_login_fail_reason(page):
    try:
        page_text = page.locator("body").inner_text(timeout=5000)
    except Exception:
        page_text = ""

    page_text_lower = page_text.lower()
    current_url = str(page.url or "")

    if "2단계 인증" in page_text or "2차 인증" in page_text or "otp" in page_text_lower or "인증번호" in page_text:
        return "2차 인증 필요"

    if "새로운 환경에서 로그인" in page_text or "본인 확인" in page_text or "새 기기" in page_text:
        return "새 기기 로그인 인증 필요"

    if "자동입력 방지문자" in page_text or "captcha" in page_text_lower or "보안문자" in page_text:
        return "캡차 발생"

    if "비밀번호가 일치하지 않습니다" in page_text or "아이디 또는 비밀번호" in page_text:
        return "아이디 또는 비밀번호 오류"

    if "해외" in page_text or "지역" in page_text or "로그인 차단" in page_text:
        return "타지역 로그인 제한"

    if "nid.naver.com" in current_url.lower():
        return f"로그인 미완료 / current_url={current_url}"

    return f"로그인 실패 원인 미확인 / current_url={current_url}"


def classify_naver_login_status(message, default_status="expired"):
    text = str(message or "")

    if (
        "아이디 또는 비밀번호" in text
        or "비밀번호가 일치" in text
        or "비밀번호 오류" in text
        or "비밀번호 복호화" in text
        or "비밀번호 암호화 값" in text
        or "비밀번호 복호화 결과" in text
    ):
        return "password_error"

    if (
        "2차 인증" in text
        or "2단계 인증" in text
        or "otp" in text.lower()
        or "인증번호" in text
        or "본인 확인" in text
        or "새 기기" in text
        or "새로운 환경" in text
        or "캡차" in text
        or "captcha" in text.lower()
        or "보안문자" in text
        or "타지역" in text
        or "로그인 차단" in text
        or "보호조치" in text
    ):
        return "naver_block"

    return default_status


def normalize_naver_login_target_url(target_url):
    raw = str(target_url or "").strip()

    if not raw:
        return ""

    if not is_login_page_url(raw):
        return raw

    try:
        parsed = urlparse(raw)
        qs = parse_qs(parsed.query or "")
        real_url = ""

        if "url" in qs and qs["url"]:
            real_url = str(qs["url"][0] or "").strip()

        if real_url:
            real_url = unquote(real_url)

            if real_url and not is_login_page_url(real_url):
                print("[V2 LOGIN TARGET NORMALIZED]", raw, "=>", real_url)
                return real_url

    except Exception as e:
        print("[V2 LOGIN TARGET NORMALIZE ERROR]", str(e))

    if "GoBlogWrite" in raw or "PostWriteForm" in raw:
        fallback = "https://blog.naver.com/GoBlogWrite.naver"
        print("[V2 LOGIN TARGET FALLBACK]", fallback)
        return fallback

    return ""


def auto_relogin_naver_v2(page, context, realtor_id, session_file, reason="", target_url=""):
    target_url = normalize_naver_login_target_url(target_url) or "https://blog.naver.com/GoBlogWrite.naver"

    print("[V2 AUTO RELOGIN START]", f"realtor_id={realtor_id}", f"reason={reason}", f"target_url={target_url}")

    naver_id, naver_pw, cred_error = get_naver_account_credentials(realtor_id)

    if cred_error:
        classified_status = classify_naver_login_status(cred_error, default_status="expired")
        update_naver_session_status(
            realtor_id,
            classified_status,
            f"자동 재로그인 불가: {cred_error}",
            session_file=session_file,
        )
        print("[V2 AUTO RELOGIN CREDENTIAL ERROR]", classified_status, cred_error)
        return False, cred_error

    try:
        if not is_login_page_url(page.url):
            login_url = "https://nid.naver.com/nidlogin.login"
            if target_url:
                login_url += "?mode=form&url=" + quote(target_url, safe="")
            page.goto(login_url, wait_until="domcontentloaded", timeout=60000)
            page.wait_for_timeout(1200)

        print("[V2 AUTO RELOGIN PAGE]", page.url)

        page.evaluate("""
            ([naverId, naverPw]) => {
                const id = document.querySelector('#id');
                const pw = document.querySelector('#pw');

                if (id) {
                    id.focus();
                    id.value = naverId;
                    id.dispatchEvent(new Event('input', { bubbles: true }));
                    id.dispatchEvent(new Event('change', { bubbles: true }));
                }

                if (pw) {
                    pw.focus();
                    pw.value = naverPw;
                    pw.dispatchEvent(new Event('input', { bubbles: true }));
                    pw.dispatchEvent(new Event('change', { bubbles: true }));
                }
            }
        """, [naver_id, naver_pw])

        page.wait_for_timeout(600)

        clicked = False
        for selector in ["#log\\.login", "button:has-text('로그인')", "input[type='submit']"]:
            try:
                loc = page.locator(selector).first
                if loc.count() > 0:
                    loc.click(force=True, timeout=5000)
                    clicked = True
                    break
            except Exception:
                continue

        if not clicked:
            try:
                page.keyboard.press("Enter")
                clicked = True
            except Exception:
                pass

        if not clicked:
            raise Exception("로그인 버튼을 찾지 못했습니다.")

        login_success = False

        for i in range(90):
            page.wait_for_timeout(1000)

            try:
                cookies = context.cookies()
                cookie_names = [
                    c.get("name", "")
                    for c in cookies
                    if "naver.com" in c.get("domain", "")
                ]

                if "NID_AUT" in cookie_names or "NID_SES" in cookie_names:
                    login_success = True
                    break
            except Exception:
                pass

            if i in [5, 15, 30, 60]:
                print(f"[V2 AUTO RELOGIN WAIT] {i}/90 url={page.url}")

        if not login_success:
            fail_reason = detect_login_fail_reason(page)
            classified_status = classify_naver_login_status(fail_reason, default_status="expired")
            update_naver_session_status(
                realtor_id,
                classified_status,
                f"자동 재로그인 실패: {fail_reason}",
                session_file=session_file,
            )
            print("[V2 AUTO RELOGIN FAIL]", classified_status, fail_reason)
            return False, fail_reason

        context.storage_state(path=session_file)

        update_naver_session_status(
            realtor_id,
            "linked",
            "V2 자동 재로그인 성공 / 세션 재저장",
            session_file=session_file,
        )

        print("[V2 AUTO RELOGIN SUCCESS] session saved:", session_file)

        if target_url:
            try:
                page.goto(target_url, wait_until="domcontentloaded", timeout=60000)
                page.wait_for_timeout(2500)
                print("[V2 AUTO RELOGIN TARGET GOTO]", page.url)

                if is_login_page_url(page.url):
                    normalized_target = normalize_naver_login_target_url(page.url) or target_url
                    print("[V2 AUTO RELOGIN TARGET RETRY]", normalized_target)
                    page.goto(normalized_target, wait_until="domcontentloaded", timeout=60000)
                    page.wait_for_timeout(2500)
                    print("[V2 AUTO RELOGIN TARGET RETRY URL]", page.url)

            except Exception as e:
                print("[V2 AUTO RELOGIN TARGET GOTO ERROR]", str(e))

        return True, "자동 재로그인 성공"

    except Exception as e:
        message = f"자동 재로그인 예외: {e}"

        classified_status = classify_naver_login_status(message, default_status="expired")
        update_naver_session_status(
            realtor_id,
            classified_status,
            message,
            session_file=session_file,
        )

        print("[V2 AUTO RELOGIN ERROR]", classified_status, message)
        return False, message


def recover_login_page_once_v2(page, context, realtor_id, session_file, reason, target_url=""):
    if not is_login_page_url(page.url):
        return True, "login page 아님"

    target_url = normalize_naver_login_target_url(target_url) or normalize_naver_login_target_url(page.url)

    ok, message = auto_relogin_naver_v2(
        page=page,
        context=context,
        realtor_id=realtor_id,
        session_file=session_file,
        reason=reason,
        target_url=target_url,
    )

    if ok and not is_login_page_url(page.url):
        return True, message

    return False, message
