#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Handle Dialogs - Detect, read, and interact with Windows dialogs
Usage: py handle_dialog.py list              # List all open dialogs
       py handle_dialog.py read              # Read current active dialog
       py handle_dialog.py click "OK"        # Click button in dialog
       py handle_dialog.py click "Save"      # Click Save button
       py handle_dialog.py type "filename"   # Type into dialog text field
       py handle_dialog.py dismiss           # Click OK/Close/Cancel

Handles: Save dialogs, Open dialogs, Message boxes, Alerts, Confirmations, etc.
"""
import argparse
import io
import json
import sys
import time

from pywinauto import Desktop
from pywinauto.findwindows import ElementNotFoundError

# Fix Windows console encoding
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")

# Common dialog control types and window classes
DIALOG_CLASSES = [
    "#32770",  # Standard Windows dialog
    "Dialog",
    "MessageBox",
    "Alert",
    "Popup",
]

DIALOG_TYPES = ["Window", "Dialog", "Pane"]

# Common button names for dismissing dialogs
DISMISS_BUTTONS = [
    "OK",
    "Close",
    "Cancel",
    "Yes",
    "No",
    "Dismiss",
    "Got it",
    "Accept",
    "Done",
]


def find_dialogs(desktop):
    """Find all open dialogs/popups."""
    dialogs = []

    for window in desktop.windows():
        try:
            title = window.window_text()
            ctrl_type = window.element_info.control_type
            class_name = window.element_info.class_name

            # Check if it's a dialog-like window
            is_dialog = (
                ctrl_type in DIALOG_TYPES
                or any(dc in class_name for dc in DIALOG_CLASSES)
                or "dialog" in title.lower()
                or "save" in title.lower()
                or "open" in title.lower()
                or "confirm" in title.lower()
                or "warning" in title.lower()
                or "error" in title.lower()
                or "alert" in title.lower()
            )

            if is_dialog and title:
                # Get dialog info
                rect = window.rectangle()
                dialogs.append(
                    {
                        "title": title,
                        "class": class_name,
                        "type": ctrl_type,
                        "rect": {
                            "left": rect.left,
                            "top": rect.top,
                            "right": rect.right,
                            "bottom": rect.bottom,
                        },
                        "window": window,
                    }
                )
        except:
            continue

    return dialogs


def read_dialog(window):
    """Read all content from a dialog."""
    content = {
        "title": window.window_text(),
        "message": [],
        "buttons": [],
        "text_fields": [],
        "checkboxes": [],
        "dropdowns": [],
        "list_items": [],
    }

    try:
        for ctrl in window.descendants():
            try:
                ctrl_type = ctrl.element_info.control_type
                name = ctrl.window_text().strip() if ctrl.window_text() else ""

                elem = {
                    "name": name,
                    "type": ctrl_type,
                    "enabled": (
                        ctrl.is_enabled() if hasattr(ctrl, "is_enabled") else True
                    ),
                }

                # Get coordinates
                try:
                    rect = ctrl.rectangle()
                    elem["center"] = (
                        (rect.left + rect.right) // 2,
                        (rect.top + rect.bottom) // 2,
                    )
                except Exception:  # noqa: BLE001
                    pass  # best-effort; suppress all errors

                if ctrl_type == "Button" and name:
                    content["buttons"].append(elem)
                elif ctrl_type in ["Text", "Static"] and name:
                    content["message"].append(name)
                elif ctrl_type in ["Edit", "ComboBox"]:
                    try:
                        elem["value"] = (
                            ctrl.get_value() if hasattr(ctrl, "get_value") else name
                        )
                    except:
                        elem["value"] = name
                    content["text_fields"].append(elem)
                elif ctrl_type == "CheckBox":
                    try:
                        elem["checked"] = ctrl.get_toggle_state() == 1
                    except Exception:  # noqa: BLE001
                        pass  # best-effort; suppress all errors
                    content["checkboxes"].append(elem)
                elif ctrl_type == "ListItem" and name:
                    content["list_items"].append(elem)

            except:
                continue

    except Exception as e:
        print(f"Error reading dialog: {e}", file=sys.stderr)

    # Clean up empty fields
    return {k: v for k, v in content.items() if v}


def click_button(window, button_name):
    """Click a button in the dialog by name."""
    button_name_lower = button_name.lower()

    for ctrl in window.descendants():
        try:
            ctrl_type = ctrl.element_info.control_type
            name = ctrl.window_text().strip() if ctrl.window_text() else ""

            if ctrl_type == "Button" and button_name_lower in name.lower():
                if ctrl.is_enabled():
                    ctrl.click()
                    return True, f"Clicked button: {name}"
                else:
                    return False, f"Button '{name}' is disabled"
        except:
            continue

    return False, f"Button '{button_name}' not found"


def type_in_field(window, text, field_index=0):
    """Type text into a text field in the dialog."""
    fields = []

    for ctrl in window.descendants():
        try:
            ctrl_type = ctrl.element_info.control_type
            if ctrl_type in ["Edit", "ComboBox"] and ctrl.is_enabled():
                fields.append(ctrl)
        except:
            continue

    if not fields:
        return False, "No text fields found in dialog"

    if field_index >= len(fields):
        return (
            False,
            f"Field index {field_index} out of range (found {len(fields)} fields)",
        )

    try:
        field = fields[field_index]
        field.set_focus()
        time.sleep(0.1)
        field.type_keys(text, with_spaces=True)
        return True, f"Typed into field {field_index}"
    except Exception as e:
        return False, f"Failed to type: {e}"


def dismiss_dialog(window):
    """Try to dismiss a dialog by clicking common buttons."""
    for button_name in DISMISS_BUTTONS:
        success, msg = click_button(window, button_name)
        if success:
            return True, msg

    # Try pressing Escape as fallback
    try:
        window.type_keys("{ESC}")
        return True, "Sent Escape key to dialog"
    except Exception:  # noqa: BLE001
        pass  # best-effort; suppress all errors

    return False, "Could not find any dismiss button"


def main():
    parser = argparse.ArgumentParser(description="Handle Windows dialogs")
    parser.add_argument(
        "action",
        choices=["list", "read", "click", "type", "dismiss", "wait"],
        help="Action to perform",
    )
    parser.add_argument(
        "value", nargs="?", default="", help="Button name or text to type"
    )
    parser.add_argument("--window", "-w", help="Target specific window by title")
    parser.add_argument(
        "--field", "-f", type=int, default=0, help="Field index for typing"
    )
    parser.add_argument(
        "--timeout", "-t", type=int, default=10, help="Timeout for wait action"
    )
    parser.add_argument("--json", action="store_true", help="Output as JSON")

    args = parser.parse_args()
    desktop = Desktop(backend="uia")

    if args.action == "list":
        dialogs = find_dialogs(desktop)
        if args.json:
            # Remove window objects for JSON serialization
            output = [{k: v for k, v in d.items() if k != "window"} for d in dialogs]
            print(json.dumps(output, indent=2, ensure_ascii=False))
        else:
            if not dialogs:
                print("No dialogs found")
            else:
                print(f"Found {len(dialogs)} dialog(s):\n")
                for i, d in enumerate(dialogs):
                    print(f"{i+1}. {d['title']}")
                    print(f"   Class: {d['class']}, Type: {d['type']}")
                    print()

    elif args.action == "read":
        dialogs = find_dialogs(desktop)

        if args.window:
            target = None
            for d in dialogs:
                if args.window.lower() in d["title"].lower():
                    target = d["window"]
                    break
            if not target:
                print(f"Dialog '{args.window}' not found")
                sys.exit(1)
        elif dialogs:
            # Get the frontmost/most recent dialog
            target = dialogs[0]["window"]
        else:
            print("No dialogs found")
            sys.exit(1)

        content = read_dialog(target)
        if args.json:
            # Convert tuples to lists for JSON
            for key in ["buttons", "text_fields", "checkboxes"]:
                if key in content:
                    for item in content[key]:
                        if "center" in item:
                            item["center"] = list(item["center"])
            print(json.dumps(content, indent=2, ensure_ascii=False))
        else:
            print(f"Dialog: {content.get('title', 'Unknown')}\n")
            if "message" in content:
                print("Message:")
                for msg in content["message"]:
                    print(f"  {msg}")
                print()
            if "buttons" in content:
                print("Buttons:")
                for btn in content["buttons"]:
                    enabled = "" if btn["enabled"] else " [DISABLED]"
                    coords = f" @ {btn['center']}" if "center" in btn else ""
                    print(f"  [{btn['name']}]{coords}{enabled}")
                print()
            if "text_fields" in content:
                print("Text Fields:")
                for i, field in enumerate(content["text_fields"]):
                    print(f"  {i}: {field.get('value', '(empty)')}")
                print()

    elif args.action == "click":
        if not args.value:
            print("Error: Button name required")
            sys.exit(1)

        dialogs = find_dialogs(desktop)
        if args.window:
            target = None
            for d in dialogs:
                if args.window.lower() in d["title"].lower():
                    target = d["window"]
                    break
        elif dialogs:
            target = dialogs[0]["window"]
        else:
            # Try finding button in active window
            target = desktop.window(active_only=True)

        if not target:
            print("No dialog found")
            sys.exit(1)

        success, msg = click_button(target, args.value)
        print(msg)
        sys.exit(0 if success else 1)

    elif args.action == "type":
        if not args.value:
            print("Error: Text to type required")
            sys.exit(1)

        dialogs = find_dialogs(desktop)
        if args.window:
            target = None
            for d in dialogs:
                if args.window.lower() in d["title"].lower():
                    target = d["window"]
                    break
        elif dialogs:
            target = dialogs[0]["window"]
        else:
            target = desktop.window(active_only=True)

        if not target:
            print("No dialog found")
            sys.exit(1)

        success, msg = type_in_field(target, args.value, args.field)
        print(msg)
        sys.exit(0 if success else 1)

    elif args.action == "dismiss":
        dialogs = find_dialogs(desktop)
        if not dialogs:
            print("No dialogs to dismiss")
            sys.exit(0)

        target = dialogs[0]["window"]
        if args.window:
            for d in dialogs:
                if args.window.lower() in d["title"].lower():
                    target = d["window"]
                    break

        success, msg = dismiss_dialog(target)
        print(msg)
        sys.exit(0 if success else 1)

    elif args.action == "wait":
        """Wait for a dialog to appear."""
        start_time = time.time()
        dialog_title = args.value if args.value else None

        while time.time() - start_time < args.timeout:
            dialogs = find_dialogs(desktop)

            if dialog_title:
                for d in dialogs:
                    if dialog_title.lower() in d["title"].lower():
                        print(f"Dialog found: {d['title']}")
                        sys.exit(0)
            elif dialogs:
                print(f"Dialog found: {dialogs[0]['title']}")
                sys.exit(0)

            time.sleep(0.5)

        print(f"Timeout: No dialog found after {args.timeout}s")
        sys.exit(1)


if __name__ == "__main__":
    main()
