import os
import time
import logging


def execute_analyze_docs_action(app, stop_requested=None, set_status=None):
    """Capture current screen and run supporting-docs flow.

    Parameters
    - app: Main application instance (must expose `status_var` and analyzer access)
    - stop_requested: Optional callable returning True when action should stop
    - set_status: Optional callable to update UI status text
    """
    def _is_stop_requested():
        try:
            return bool(stop_requested and stop_requested())
        except Exception:
            return False

    def _set_status_safe(text):
        try:
            if set_status:
                set_status(text)
            else:
                app.status_var.set(text)
        except Exception:
            pass

    try:
        # Wait briefly for UI to settle
        try:
            wait_time = float(os.getenv("SCREENSHOT_PRE_DELAY_SEC", "5.0"))
        except Exception:
            wait_time = 5.0
        slept = 0.0
        while slept < wait_time:
            if _is_stop_requested():
                return
            time.sleep(0.1)
            slept += 0.1

        if not _is_stop_requested():
            _set_status_safe('Analyzing documents...')

        # Prefer the app's analyzer for capture
        from ..openai_analyzer import OpenAIAnalyzer
        analyzer = None
        try:
            manager = getattr(app, 'screenshot_manager', None)
            analyzer = getattr(manager, 'openai_analyzer', None)
            if analyzer is None:
                analyzer = OpenAIAnalyzer(app)
        except Exception:
            try:
                analyzer = OpenAIAnalyzer(app)
            except Exception:
                analyzer = None
        if analyzer is None:
            return

        # MYOB-specific: if current app_id is 11, scroll to the bottom before first detection
        try:
            app_id = getattr(app, 'current_application_id', None) or getattr(app, 'application_id', None)
            app_id = int(app_id) if app_id is not None else None
        except Exception:
            app_id = None

        def _scroll_to_bottom_myob():
            try:
                import pyautogui as _pg
                # Bring focus to content first
                try:
                    sw, sh = analyzer._get_screen_size()
                except Exception:
                    sw, sh = 1920, 1080
                try:
                    cx = int(sw // 2); cy = int(sh // 2)
                    _pg.moveTo(cx, max(10, cy))
                    _pg.click()
                except Exception:
                    pass
                # Primary: Ctrl+End
                try:
                    _pg.hotkey('ctrl', 'end')
                except Exception:
                    pass
                # Secondary: End
                try:
                    _pg.press('end')
                except Exception:
                    pass
                # Fallback: wheel scroll down bursts
                try:
                    for _ in range(6):
                        _pg.scroll(-900)
                except Exception:
                    pass
            except Exception:
                pass
            try:
                time.sleep(1.2)
            except Exception:
                pass

        # QuickBooks-specific: scroll to the bottom before analysis and only analyze bottom area
        def _scroll_to_bottom_quickbooks():
            try:
                import pyautogui as _pg
                # Bring focus to content first
                try:
                    sw, sh = analyzer._get_screen_size()
                except Exception:
                    sw, sh = 1920, 1080
                try:
                    cx = int(sw // 2); cy = int(sh // 2)
                    _pg.moveTo(cx, max(10, cy))
                    _pg.click()
                except Exception:
                    pass
                # Primary: Ctrl+End
                try:
                    _pg.hotkey('ctrl', 'end')
                except Exception:
                    pass
                # Secondary: End
                try:
                    _pg.press('end')
                except Exception:
                    pass
                # Fallback: wheel scroll down bursts
                try:
                    for _ in range(6):
                        _pg.scroll(-900)
                except Exception:
                    pass
            except Exception:
                pass
            try:
                time.sleep(1.2)
            except Exception:
                pass

        qb_bottom_only = False
        if app_id == 11:
            try:
                _set_status_safe('MYOB: scrolling to bottom before analyzing...')
            except Exception:
                pass
            _scroll_to_bottom_myob()
        elif app_id == 10:
            try:
                _set_status_safe('QuickBooks: scrolling to bottom before analyzing...')
            except Exception:
                pass
            _scroll_to_bottom_quickbooks()
            qb_bottom_only = True

        # Helper: run vision on a given screenshot and return validated links
        def _find_validated_links_for_screenshot(path: str):
            try:
                sw, sh = analyzer._get_screen_size()
                mode = str(os.getenv("ANALYZE_DOCS_MODE", "links")).strip().lower()
                if mode == 'attachments':
                    from ..prompt_builder import get_attachment_links_prompt
                    prompt = get_attachment_links_prompt(sw, sh)
                else:
                    from ..prompt_builder import get_links_prompt
                    prompt = get_links_prompt(sw, sh)
            except Exception:
                # Fallback strictly to links prompt if anything goes wrong
                from ..prompt_builder import get_links_prompt
                sw, sh = analyzer._get_screen_size()
                prompt = get_links_prompt(sw, sh)

            try:
                with open(path, 'rb') as _f:
                    _b = _f.read()
                import base64 as _b64
                image_url = f"data:image/png;base64,{_b64.b64encode(_b).decode('utf-8')}"
                from ..constants import OPENAI_VISION_TEMPERATURE as _TEMP
            except Exception:
                logging.exception('[Actions] Failed preparing image for attachments prompt')
                return []

            try:
                if _is_stop_requested():
                    return []
                try:
                    logging.info('[Actions] Vision call: start')
                except Exception:
                    pass
                content = analyzer._post_vision(prompt, image_url, max_tokens=2000, temperature=float(_TEMP))
                try:
                    logging.info('[Actions] Vision call: complete')
                except Exception:
                    pass
            except Exception:
                logging.exception('[Actions] OpenAI vision call failed (attachments)')
                return []

            # Log response preview for debugging
            try:
                try:
                    max_log_chars = int(os.getenv("ANALYZE_DOCS_LOG_CONTENT_CHARS", "2000"))
                except Exception:
                    max_log_chars = 2000
                preview = content[:max_log_chars] if isinstance(content, str) else str(type(content))
                # Log to root/app logger
                logging.info(f"[Actions] Vision response length={len(content) if isinstance(content, str) else 'n/a'} preview={preview}")
                # Also log to OpenAIAnalyzer logger so it lands in openai.log
                try:
                    _oa_log = logging.getLogger('OpenAIAnalyzer')
                    _oa_log.info(f"[Actions] Vision response length={len(content) if isinstance(content, str) else 'n/a'} preview={preview}")
                except Exception:
                    pass
                # Optional: dump full response to a file for deep debugging
                try:
                    dump_flag = os.getenv('ANALYZE_DOCS_DUMP_RESPONSE', '0').strip() in ('1', 'true', 'True')
                except Exception:
                    dump_flag = False
                if dump_flag and isinstance(content, str):
                    try:
                        import datetime as _dt
                        dump_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), '..', 'logs', 'openai_debug')
                        dump_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'logs', 'openai_debug'))
                        os.makedirs(dump_dir, exist_ok=True)
                        ts = _dt.datetime.utcnow().strftime('%Y%m%dT%H%M%S%fZ')
                        dump_path = os.path.join(dump_dir, f'vision_response_{ts}.txt')
                        with open(dump_path, 'w', encoding='utf-8') as _out:
                            _out.write(content)
                        logging.info(f"[Actions] Vision response dumped to {dump_path}")
                    except Exception:
                        logging.exception('[Actions] Failed to dump vision response')
            except Exception:
                pass

            try:
                start_idx = content.find('['); end_idx = content.rfind(']') + 1
                links = []
                if start_idx != -1 and end_idx != -1:
                    import json as _json
                    links = _json.loads(content[start_idx:end_idx])
            except Exception:
                try:
                    logging.exception('[Actions] Failed to parse links JSON from vision response')
                except Exception:
                    pass
                links = []

            try:
                logging.info(f"[Actions] Parsed links count={len(links) if links else 0}")
            except Exception:
                pass

            validated_local = []
            for link in links or []:
                try:
                    if isinstance(link, dict) and 'link_words' not in link:
                        desc = str(link.get('description') or '').strip()
                        link['link_words'] = desc
                except Exception:
                    try:
                        link['link_words'] = ''
                    except Exception:
                        pass
                if 'button' not in link:
                    link['button'] = False
                if analyzer._validate_coordinates(link.get('coordinates', [])):
                    validated_local.append(link)
            try:
                logging.info(f"[Actions] Validated links count={len(validated_local)}")
            except Exception:
                pass
            return validated_local

        # Helper: compute viewport change ratio using Pillow only
        def _change_ratio(a_path: str, b_path: str) -> float:
            try:
                from PIL import Image
            except Exception:
                # If Pillow is unavailable, assume change to avoid false negatives
                return 1.0
            try:
                target_size = (256, 144)
                a = Image.open(a_path).convert('L').resize(target_size)
                b = Image.open(b_path).convert('L').resize(target_size)
                a_bytes = a.tobytes()
                b_bytes = b.tobytes()
                # Mean absolute difference normalized to [0,1]
                total = 0
                count = len(a_bytes)
                for i in range(count):
                    total += abs(a_bytes[i] - b_bytes[i])
                return float(total) / float(count) / 255.0 if count else 0.0
            except Exception:
                return 1.0

        # Config
        try:
            scroll_wait = float(os.getenv("ANALYZE_DOCS_SCROLL_WAIT_SEC", "0.7"))
        except Exception:
            scroll_wait = 0.7
        try:
            min_change = float(os.getenv("ANALYZE_DOCS_MIN_CHANGE_RATIO", "0.01"))
        except Exception:
            min_change = 0.01

        # Helper: bring focus to content region by clicking near screen center
        def _focus_content_region():
            try:
                import pyautogui as _pg
                sw, sh = analyzer._get_screen_size()
                cx = int(sw // 2)
                cy = int(sh // 2)
                _pg.moveTo(cx, max(10, cy))
                _pg.click()
            except Exception:
                pass

        # First attempt (no scroll)
        if _is_stop_requested():
            return
        _focus_content_region()
        screenshot_path = analyzer.capture_full_resolution_screenshot()
        if not screenshot_path:
            return
        validated = _find_validated_links_for_screenshot(screenshot_path)

        # Keep paging down while no links are found and viewport actually changes
        scroll_attempts = 0
        while not validated and not qb_bottom_only:
            if _is_stop_requested():
                return
            prev_path = screenshot_path
            try:
                import pyautogui as _pg
                # Ensure focus is in content area before attempting to scroll
                _focus_content_region()
                _pg.press('pagedown')
            except Exception:
                logging.info('[Actions] PageDown not possible or failed; stopping scroll loop')
                break
            scroll_attempts += 1

            # Allow UI to settle
            slept = 0.0
            while slept < scroll_wait and not _is_stop_requested():
                time.sleep(0.1)
                slept += 0.1

            if _is_stop_requested():
                return
            screenshot_path = analyzer.capture_full_resolution_screenshot()
            if not screenshot_path:
                return

            # Detect if viewport actually changed; if not, try alternative scroll methods
            try:
                changed = _change_ratio(prev_path, screenshot_path) >= min_change
            except Exception:
                changed = True
            try:
                logging.info(f"[Actions] Scroll changed viewport: {changed}")
            except Exception:
                pass
            if not changed:
                # Fallback 1: mouse wheel scroll
                try:
                    _focus_content_region()
                    _pg.scroll(-800)
                    slept = 0.0
                    while slept < scroll_wait and not _is_stop_requested():
                        time.sleep(0.1)
                        slept += 0.1
                    next_path = analyzer.capture_full_resolution_screenshot()
                    if next_path:
                        try:
                            changed = _change_ratio(prev_path, next_path) >= min_change
                        except Exception:
                            changed = True
                        screenshot_path = next_path if changed else screenshot_path
                        logging.info(f"[Actions] Wheel scroll changed viewport: {changed}")
                except Exception:
                    pass

                # Fallback 2: arrow down bursts (if still not changed)
                if not changed:
                    try:
                        _focus_content_region()
                        for _ in range(8):
                            _pg.press('down')
                        time.sleep(min(0.5, scroll_wait))
                        next_path = analyzer.capture_full_resolution_screenshot()
                        if next_path:
                            try:
                                changed = _change_ratio(prev_path, next_path) >= min_change
                            except Exception:
                                changed = True
                            screenshot_path = next_path if changed else screenshot_path
                            logging.info(f"[Actions] Arrow-down scroll changed viewport: {changed}")
                    except Exception:
                        pass

                if not changed:
                    break

            # Viewport changed; try vision again
            validated = _find_validated_links_for_screenshot(screenshot_path)

        if not validated or _is_stop_requested():
            try:
                logging.info(f"[Actions] No links found after scrolling attempts={scroll_attempts}")
            except Exception:
                pass
            return

        # Execute clicks
        try:
            if not _is_stop_requested():
                analyzer._perform_automated_link_clicking(
                    original_screenshot_path=screenshot_path,
                    clickable_links=validated,
                    playlist_name=None,
                )
        except Exception:
            try:
                if not _is_stop_requested():
                    analyzer.perform_automated_clicking(validated, screenshot_path)
            except Exception:
                pass
    finally:
        try:
            if app.status_var.get() != 'Paused':
                _set_status_safe('Playing')
        except Exception:
            pass


