import time
import random
from typing import Dict, Any, Optional

class BasePlatformHandler:
    """
    Base class for specific social media posting automation.
    """
    PLATFORM_NAME = "Generic"
    BASE_URL = "https://example.com"

    def __init__(self, brain=None):
        self.brain = brain
        self._logger = None
        self.platform_name = getattr(self, "PLATFORM_NAME", "Generic")

    def set_logger(self, logger):
        """Attach thread-safe live logger from PosterEngine."""
        self._logger = logger

    def log(self, message: str, level: str = "INFO"):
        """Emit live status update to both web dashboard and terminal."""
        formatted = f"[{self.PLATFORM_NAME}] {message}"
        if self._logger:
            try:
                self._logger(formatted, level)
            except Exception:
                print(formatted)
        else:
            print(formatted)

    def setup_clipboard_interceptor(self, page):
        """
        Injects a lightweight JavaScript interceptor into the page to capture any text
        written via navigator.clipboard.writeText. This completely bypasses browser permission
        prompts for reading clipboard data.
        """
        try:
            interceptor_script = """
            (() => {
                if (window.__clipboard_interceptor_installed) return;
                window.__last_copied_text = "";
                if (navigator.clipboard && navigator.clipboard.writeText) {
                    const originalWrite = navigator.clipboard.writeText.bind(navigator.clipboard);
                    navigator.clipboard.writeText = function(text) {
                        window.__last_copied_text = text;
                        return originalWrite(text);
                    };
                }
                document.addEventListener('copy', (e) => {
                    const sel = window.getSelection();
                    if (sel) {
                        window.__last_copied_text = sel.toString();
                    }
                }, true);
                window.__clipboard_interceptor_installed = true;
            })();
            """
            page.evaluate(interceptor_script)
        except Exception:
            pass

    def get_intercepted_clipboard(self, page) -> Optional[str]:
        """
        Reads the text captured by setup_clipboard_interceptor().
        """
        try:
            val = page.evaluate("() => window.__last_copied_text || ''")
            if val and isinstance(val, str) and val.strip():
                return val.strip()
        except Exception:
            pass
        return None

    def check_login_required(self, page, guide: Optional[Dict[str, Any]] = None) -> bool:
        """
        Universal, robust detection of unauthenticated / login / guest state across all platforms.
        Returns True if login is required (user is NOT logged in), False otherwise.
        """
        curr_url = page.url.lower()

        # 1. URL patterns indicating login/auth screen
        login_url_keywords = [
            "/login", "/signin", "/sign-in", "/log-in",
            "accounts/login", "i/flow/login", "/session/new",
            "checkpoint", "auth/realms", "prompt=login",
            "/join", "/signup", "/register"
        ]
        if any(kw in curr_url for kw in login_url_keywords):
            self.log(f"URL browser mengarah ke halaman login/registrasi ({curr_url})!", "WARN")
            return True

        # 2. Check login indicators from site_knowledge guide if provided
        if guide:
            indicators = guide.get("login_indicators") or []
            for ind in indicators:
                try:
                    if page.locator(ind).first.is_visible(timeout=1000):
                        self.log(f"Indikator login terdeteksi via guide selector ({ind}).", "WARN")
                        return True
                except Exception:
                    continue

        # 3. Universal DOM checks: password input is almost always a login/auth form
        try:
            pwd_input = page.locator("input[type='password']").first
            if pwd_input.is_visible(timeout=1000):
                self.log("Form kata sandi (password) terdeteksi. Akun belum login!", "WARN")
                return True
        except Exception:
            pass

        # 4. Universal login / sign in button checks
        login_btn_selectors = [
            "button:has-text('Log In')",
            "button:has-text('Sign In')",
            "button:has-text('Masuk')",
            "a[href*='login']:has-text('Log In')",
            "a[href*='login']:has-text('Masuk')",
            "a[href*='signin']:has-text('Sign in')",
            "button[data-test-id*='login']",
            "div[role='button']:has-text('Log in')",
            "div[role='button']:has-text('Masuk')"
        ]
        for sel in login_btn_selectors:
            try:
                btn = page.locator(sel).first
                if btn.is_visible(timeout=800):
                    self.log(f"Tombol login terdeteksi ({sel}). Akun belum login!", "WARN")
                    return True
            except Exception:
                continue

        # 5. Check guest banner / welcome modals (e.g., Pinterest, Twitter, Reddit)
        guest_banner_selectors = [
            "div[data-test-id='login-modal-default']",
            "div#login-modal",
            "div[role='dialog']:has-text('Log in to continue')",
            "div[role='dialog']:has-text('Masuk untuk melanjutkan')",
            "div[role='dialog']:has-text('Welcome to Pinterest')"
        ]
        for gb in guest_banner_selectors:
            try:
                modal = page.locator(gb).first
                if modal.is_visible(timeout=800):
                    self.log(f"Modal tamu/login terdeteksi ({gb}).", "WARN")
                    return True
            except Exception:
                continue

        return False

    def consult_vision(self, page, question_or_prompt: str) -> Dict[str, Any]:
        """
        Takes an active page screenshot and asks Gemini Multimodal Vision for guidance.
        Saves full debug screenshot and annotated visual target overlay.
        Emits rich real-time feedback with coordinates to the live execution log.
        """
        if not self.brain:
            self.log("AI Brain tidak tersedia untuk Vision.", "WARN")
            return {"success": False, "notes": "No brain configured"}

        self.log(f"🤖 Mengaktifkan Gemini Multimodal Vision: '{question_or_prompt}'...")
        try:
            ss = page.screenshot(type="png")
            tag = getattr(self, "platform_name", getattr(self, "PLATFORM_NAME", "generic")).lower()
            v_res = self.brain.analyze_screenshot_with_vision(ss, question_or_prompt, source_tag=tag)
            
            fb = v_res.get("feedback") or v_res.get("notes") or "Analisis visual selesai."
            state = v_res.get("page_state", "unknown")
            action = v_res.get("action_needed", "none")
            target = v_res.get("target_element_text")
            loc = v_res.get("visual_location")
            coords = v_res.get("coordinates_percent")
            ann_url = v_res.get("annotated_image_url")

            info_text = f"👁️ Gemini Vision: {fb} [Kondisi: {state}, Aksi: {action}"
            if target:
                info_text += f", Target: '{target}'"
            if coords and isinstance(coords, dict) and "x" in coords and "y" in coords:
                info_text += f", Koordinat: ({coords['x']:.1f}%, {coords['y']:.1f}%)"
            elif loc:
                info_text += f", Posisi: {loc}"
            if ann_url:
                info_text += f" | Debug: {ann_url}"
            info_text += "]"

            self.log(info_text, "INFO")
            return v_res
        except Exception as ve:
            self.log(f"Gagal saat analisis Vision: {ve}", "WARN")
            return {"success": False, "notes": str(ve)}

    def patient_wait_or_consult_vision(
        self,
        page,
        question_or_goal: str,
        step_name: str = "general_step",
        max_patient_wait_sec: int = 12
    ) -> Dict[str, Any]:
        """
        Vision-First Patient Engine:
        Instead of spamming clicks and scrolling up and down, pauses calmly, waits for
        the page to settle, and if element is missing or stuck, captures full screenshot
        and consults Gemini Multimodal Vision. Checks for roadblocks (captcha, 2FA, disabled buttons).
        """
        self.log(f"⏳ Jeda tenang & sabar ({max_patient_wait_sec}s) menunggu kestabilan halaman: '{question_or_goal}'...", "INFO")
        time.sleep(3.0)

        # Let network settle if possible
        try:
            page.wait_for_load_state("networkidle", timeout=min(max_patient_wait_sec * 1000, 8000))
        except Exception:
            pass

        # Consult Gemini Vision with full screenshot
        v_res = self.consult_vision(page, question_or_goal)

        roadblock = v_res.get("roadblock_type", "none")
        action = v_res.get("action_needed", "none")

        # Check roadblocks
        if roadblock in ["login_required", "captcha_detected", "security_checkpoint"]:
            self.log(f"⚠️ Roadblock terdeteksi oleh Vision ({roadblock}): {v_res.get('feedback')}", "WARN")
            return v_res

        # If button is still disabled or media still uploading, give generous patience
        if roadblock in ["button_disabled", "media_uploading"] or action == "wait_patiently":
            self.log("⏳ Media masih diproses atau tombol masih disabled. Menunggu sabar 8 detik tambahan...", "INFO")
            time.sleep(8)
            # Re-check with Vision
            v_res = self.consult_vision(page, f"Apakah tombol sekarang sudah aktif untuk '{question_or_goal}'?")

        return v_res

    def click_visual_target(self, page, v_res: Dict[str, Any], step_name: str = "") -> bool:
        """
        Direct Spatial Click Fallback with Learning Engine:
        Translates Gemini Vision percentage coordinates into physical browser viewport pixels,
        executes humanized click, and records success into site_knowledge.json.
        """
        if not v_res or not isinstance(v_res, dict):
            return False

        coords = v_res.get("coordinates_percent")
        if not coords or not isinstance(coords, dict) or "x" not in coords or "y" not in coords:
            return False

        try:
            vp = page.viewport_size
            if not vp or "width" not in vp or "height" not in vp:
                try:
                    inner_w = page.evaluate("() => window.innerWidth")
                    inner_h = page.evaluate("() => window.innerHeight")
                    vp = {"width": inner_w or 1280, "height": inner_h or 800}
                except Exception:
                    vp = {"width": 1280, "height": 800}

            target_x = int(vp["width"] * (float(coords["x"]) / 100.0))
            target_y = int(vp["height"] * (float(coords["y"]) / 100.0))

            target_label = v_res.get("target_element_text") or "Target Visual"
            self.log(f"🎯 Mengklik target visual '{target_label}' pada ({target_x}px, {target_y}px)...", "INFO")

            # Move mouse smoothly
            page.mouse.move(target_x, target_y, steps=7)
            time.sleep(0.2)
            page.mouse.click(target_x, target_y)
            time.sleep(1.0)

            # Record learning success
            if self.brain and step_name:
                domain = getattr(self, "domain", getattr(self, "platform_name", "general")).lower()
                self.brain.record_vision_success(
                    domain=domain,
                    step_name=step_name,
                    target_text=target_label,
                    coordinates=coords
                )
                self.log(f"🧠 Pola sukses vision disimpan ke site_knowledge.json untuk langkah '{step_name}'.", "INFO")

            return True
        except Exception as ce:
            self.log(f"Gagal saat klik koordinat visual: {ce}", "WARN")
            return False

    def human_type(self, element, text: str, min_delay: float = 0.02, max_delay: float = 0.08):
        """Type text with randomized natural human key intervals."""
        element.click()
        time.sleep(0.3)
        for char in text:
            element.type(char, delay=random.uniform(min_delay, max_delay) * 1000)
            if char in ("\n", ".", ","):
                time.sleep(random.uniform(0.1, 0.3))

    def dismiss_common_modals(self, page):
        """Dismiss common cookie / notification / promotional banners."""
        selectors_to_dismiss = [
            "button:has-text('Not Now')",
            "button:has-text('Lain Kali')",
            "button:has-text('Nanti')",
            "button:has-text('Accept All')",
            "button:has-text('Terima Semua')",
            "button:has-text('Allow all cookies')",
            "button:has-text('Dismiss')",
            "button[aria-label='Close']",
            "button[aria-label='Tutup']",
            "div[role='dialog'] button:has-text('Batal')",
            "div[role='dialog'] button:has-text('Cancel')"
        ]
        for sel in selectors_to_dismiss:
            try:
                locator = page.locator(sel).first
                if locator.is_visible(timeout=1500):
                    locator.click()
                    time.sleep(0.5)
            except Exception:
                pass

    def check_login(self, page) -> bool:
        """Override to implement custom login detection."""
        return True

    def post(
        self,
        page,
        flyer_path: str,
        title: str,
        caption: str,
        target_link: Optional[str] = None,
        guide: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """
        Standardized 4-Phase Execution Pipeline:
        Phase 1: Upload Media
        Phase 2: Content & Metadata
        Phase 3: Execute Publication
        Phase 4: Extract Verified Post URL
        """
        self.log(f"=== Memulai Alur 4 Sesi Posting untuk {self.PLATFORM_NAME} ===")

        # Sesi 1: Upload Media
        self.log(f"--- [SESI 1/4: UPLOAD MEDIA] Memproses pengunggahan flyer di {self.PLATFORM_NAME}... ---")
        try:
            upload_ok = self.phase_upload(page, flyer_path, guide)
        except Exception as ue:
            self.log(f"Error fatal di Sesi Upload: {ue}", "ERROR")
            upload_ok = False

        if not upload_ok:
            return {
                "success": False,
                "post_url": None,
                "message": f"FAILED: Sesi 1 (Upload Media) {self.PLATFORM_NAME} gagal diverifikasi."
            }

        # Sesi 2: Content & Metadata
        self.log(f"--- [SESI 2/4: KONTEN & METADATA] Mengisi judul, teks, dan atribut {self.PLATFORM_NAME}... ---")
        try:
            metadata_ok = self.phase_metadata(page, title, caption, target_link, guide)
        except Exception as me:
            self.log(f"Error fatal di Sesi Metadata: {me}", "ERROR")
            metadata_ok = False

        if not metadata_ok:
            return {
                "success": False,
                "post_url": None,
                "message": f"FAILED: Sesi 2 (Konten/Metadata) {self.PLATFORM_NAME} gagal."
            }

        # Sesi 3: Publish
        self.log(f"--- [SESI 3/4: PUBLIKASI] Menjalankan perintah publikasi/post {self.PLATFORM_NAME}... ---")
        try:
            publish_ok = self.phase_publish(page, guide)
        except Exception as pe:
            self.log(f"Error fatal di Sesi Publish: {pe}", "ERROR")
            publish_ok = False

        if not publish_ok:
            return {
                "success": False,
                "post_url": None,
                "message": f"FAILED: Sesi 3 (Publikasi) {self.PLATFORM_NAME} gagal atau tombol tidak aktif."
            }

        # Sesi 4: Ekstraksi Tautan
        self.log(f"--- [SESI 4/4: EKSTRAKSI TAUTAN] Menyalin tautan postingan spesifik {self.PLATFORM_NAME}... ---")
        try:
            link_result = self.phase_extract_link(page, title, guide)
        except Exception as le:
            self.log(f"Error fatal di Sesi Ekstraksi Link: {le}", "ERROR")
            link_result = {
                "success": False,
                "post_url": None,
                "message": f"FAILED: Sesi 4 (Ekstraksi Tautan) {self.PLATFORM_NAME} error: {le}"
            }

        return link_result

    def phase_upload(self, page, flyer_path: str, guide: Optional[Dict[str, Any]] = None) -> bool:
        """Override to implement Phase 1: Upload Media."""
        return True

    def phase_metadata(self, page, title: str, caption: str, target_link: Optional[str] = None, guide: Optional[Dict[str, Any]] = None) -> bool:
        """Override to implement Phase 2: Content & Metadata."""
        return True

    def phase_publish(self, page, guide: Optional[Dict[str, Any]] = None) -> bool:
        """Override to implement Phase 3: Publish."""
        return True

    def phase_extract_link(self, page, title: str, guide: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """Override to implement Phase 4: Extract Verified Post Link."""
        return {"success": False, "post_url": None, "message": "Phase 4 not implemented"}
