"""
United/DentalHub Pre-Authorization Worker.
Portal: app.dentalhub.com (United SCO)

Flow (mirrors claim worker):
  1. Navigate to eligibility page, fill member ID + DOB + payer, continue through
     Select Insurance popup and Provider & Location page → land on Selected Patient page.
  2. Click Submit Pre-Authorization button on Selected Patient page.
  3. Pre-auth page is pre-filled — select payer and click Continue.
  4. Select Insurance popup — click Ok.
  5. Practitioner & Location page — click Continue only (no dropdowns).
  6. Date Entry / pre-auth form — fill CDT codes, tooth, billed amount, attach files, submit.
  7. Capture pre-auth number and PDF from confirmation page.
"""
from selenium import webdriver
from selenium.common.exceptions import WebDriverException, TimeoutException
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
import time
import os
import base64
import json
import re

from unitedsco_browser_manager import get_browser_manager

_SERVICE_DIR = os.path.dirname(os.path.abspath(__file__))
_BACKEND_CWD = os.path.normpath(os.path.join(_SERVICE_DIR, "..", "Backend"))


class AutomationUnitedDHPreAuth:
    def __init__(self, data):
        self.headless = False
        self.driver = None

        claim = data.get("claim", {}) if isinstance(data, dict) else {}

        self.memberId = claim.get("memberId", "")
        self.dateOfBirth = claim.get("dateOfBirth", "")
        self.firstName = claim.get("firstName", "")
        self.lastName = claim.get("lastName", "")
        self.serviceDate = claim.get("serviceDate", "")
        self.serviceLines = claim.get("serviceLines", [])
        self.claimFiles = claim.get("claimFiles", [])
        self.patientName = claim.get("patientName", "")
        self.remarks = claim.get("remarks", "")

        self.uniteddh_username = claim.get("uniteddhUsername", "")
        self.uniteddh_password = claim.get("uniteddhPassword", "")
        self.payment_group_id = claim.get("paymentGroupId", "")

        self.download_dir = get_browser_manager().download_dir
        os.makedirs(self.download_dir, exist_ok=True)

    def config_driver(self):
        self.driver = get_browser_manager().get_driver(self.headless)

    def _force_logout(self):
        try:
            print("[UnitedDH PreAuth login] Forcing logout due to credential change...")
            browser_manager = get_browser_manager()

            try:
                self.driver.get("https://app.dentalhub.com/app/dashboard")
                time.sleep(2)
                for selector in [
                    "//button[contains(text(),'Log out') or contains(text(),'Logout') or contains(text(),'Sign out')]",
                    "//a[contains(text(),'Log out') or contains(text(),'Logout')]",
                    "//button[@aria-label='Log out' or @aria-label='Logout']",
                ]:
                    try:
                        btn = WebDriverWait(self.driver, 3).until(EC.element_to_be_clickable((By.XPATH, selector)))
                        btn.click()
                        print("[UnitedDH PreAuth login] Clicked logout button")
                        time.sleep(2)
                        break
                    except TimeoutException:
                        continue
            except Exception as e:
                print(f"[UnitedDH PreAuth login] Could not click logout button: {e}")

            try:
                self.driver.delete_all_cookies()
                print("[UnitedDH PreAuth login] Cleared all cookies")
            except Exception as e:
                print(f"[UnitedDH PreAuth login] Error clearing cookies: {e}")

            browser_manager.clear_credentials_hash()
            return True
        except Exception as e:
            print(f"[UnitedDH PreAuth login] Error during forced logout: {e}")
            return False

    def login(self, url):
        wait = WebDriverWait(self.driver, 30)
        browser_manager = get_browser_manager()

        try:
            if self.uniteddh_username and browser_manager.credentials_changed(self.uniteddh_username):
                self._force_logout()
                self.driver.get(url)
                time.sleep(2)

            try:
                current_url = self.driver.current_url
                print(f"[UnitedDH PreAuth login] Current URL: {current_url}")
                if "app.dentalhub.com" in current_url and "login" not in current_url.lower():
                    try:
                        WebDriverWait(self.driver, 3).until(
                            EC.presence_of_element_located((By.XPATH,
                                '//input[contains(@placeholder,"Search")] | //*[contains(@class,"dashboard")] | //nav'))
                        )
                        print("[UnitedDH PreAuth login] Already logged in")
                        return "ALREADY_LOGGED_IN"
                    except TimeoutException:
                        pass
            except Exception as e:
                print(f"[UnitedDH PreAuth login] Error checking current state: {e}")

            self.driver.get(url)
            time.sleep(3)

            current_url = self.driver.current_url
            print(f"[UnitedDH PreAuth login] After navigation URL: {current_url}")

            if "app.dentalhub.com" in current_url and "login" not in current_url.lower():
                print("[UnitedDH PreAuth login] Already on dashboard")
                return "ALREADY_LOGGED_IN"

            try:
                WebDriverWait(self.driver, 3).until(
                    EC.presence_of_element_located((By.XPATH,
                        "//input[@type='tel' or contains(@placeholder,'code') or contains(@aria-label,'Verification')]"))
                )
                print("[UnitedDH PreAuth login] OTP input found")
                return "OTP_REQUIRED"
            except TimeoutException:
                pass

            if "app.dentalhub.com" in current_url:
                try:
                    login_btn = WebDriverWait(self.driver, 5).until(
                        EC.element_to_be_clickable((By.XPATH,
                            "//button[contains(text(),'LOGIN') or contains(text(),'Log In') or contains(text(),'Login')]"))
                    )
                    login_btn.click()
                    print("[UnitedDH PreAuth login] Clicked LOGIN button")
                    time.sleep(5)
                except TimeoutException:
                    print("[UnitedDH PreAuth login] No LOGIN button found, proceeding...")

            current_url = self.driver.current_url
            print(f"[UnitedDH PreAuth login] After LOGIN click URL: {current_url}")

            if "b2clogin.com" in current_url or "login" in current_url.lower():
                print("[UnitedDH PreAuth login] On B2C login page - filling credentials")

                try:
                    send_code_btn = self.driver.find_element(By.XPATH,
                        "//button[@id='sendCode'] | //input[@id='sendCode'] | "
                        "//button[contains(text(),'Text Me') or contains(text(),'Send Code')]"
                    )
                    if send_code_btn.is_displayed():
                        print("[UnitedDH PreAuth login] Already on phone verification page - clicking 'Text Me'")
                        self.driver.execute_script("arguments[0].click();", send_code_btn)
                        time.sleep(3)
                        return "OTP_REQUIRED"
                except Exception:
                    pass

                try:
                    email_field = WebDriverWait(self.driver, 10).until(
                        EC.element_to_be_clickable((By.XPATH,
                            "//input[@id='signInName' or @name='signInName' or @name='Email address' or @type='email']"))
                    )
                    email_field.clear()
                    email_field.send_keys(self.uniteddh_username)
                    print(f"[UnitedDH PreAuth login] Entered username: {self.uniteddh_username}")

                    password_field = WebDriverWait(self.driver, 10).until(
                        EC.presence_of_element_located((By.XPATH,
                            "//input[@id='password' or @type='password']"))
                    )
                    password_field.clear()
                    password_field.send_keys(self.uniteddh_password)
                    print("[UnitedDH PreAuth login] Entered password")

                    signin_button = WebDriverWait(self.driver, 10).until(
                        EC.element_to_be_clickable((By.XPATH,
                            "//button[@id='next'] | //button[@type='submit' and contains(text(),'Sign')]"))
                    )
                    signin_button.click()
                    print("[UnitedDH PreAuth login] Clicked Sign in button")

                    if self.uniteddh_username:
                        browser_manager.save_credentials_hash(self.uniteddh_username)

                    time.sleep(5)

                    try:
                        continue_btn = self.driver.find_element(By.XPATH, "//button[contains(text(),'Continue')]")
                        phone_elements = self.driver.find_elements(By.XPATH, "//*[contains(text(),'Phone')]")
                        if continue_btn and phone_elements:
                            print("[UnitedDH PreAuth login] MFA method selection page detected")
                            try:
                                phone_radio = self.driver.find_element(By.XPATH,
                                    "//input[@type='radio' and (contains(@value,'phone') or contains(@value,'Phone'))] | "
                                    "//label[contains(text(),'Phone')]/preceding-sibling::input[@type='radio'] | "
                                    "//input[@type='radio']"
                                )
                                if phone_radio and not phone_radio.is_selected():
                                    phone_radio.click()
                                    print("[UnitedDH PreAuth login] Selected 'Phone' radio button")
                            except Exception as radio_err:
                                print(f"[UnitedDH PreAuth login] Could not click Phone radio: {radio_err}")
                            time.sleep(1)
                            continue_btn.click()
                            print("[UnitedDH PreAuth login] Clicked 'Continue' on MFA selection page")
                            time.sleep(3)
                    except Exception:
                        pass

                    try:
                        send_code_btn = WebDriverWait(self.driver, 5).until(
                            EC.element_to_be_clickable((By.XPATH,
                                "//button[@id='sendCode'] | //input[@id='sendCode'] | "
                                "//button[contains(text(),'Text Me') or contains(text(),'Send Code')]"))
                        )
                        print("[UnitedDH PreAuth login] Found 'Text Me' / Send Code button")
                        self.driver.execute_script("arguments[0].click();", send_code_btn)
                        time.sleep(3)
                        return "OTP_REQUIRED"
                    except TimeoutException:
                        pass

                    try:
                        WebDriverWait(self.driver, 5).until(
                            EC.presence_of_element_located((By.XPATH,
                                "//input[@type='tel' or contains(@placeholder,'code') or contains(@aria-label,'Verification')]"))
                        )
                        print("[UnitedDH PreAuth login] OTP input appeared after sign-in")
                        return "OTP_REQUIRED"
                    except TimeoutException:
                        pass

                    current_url = self.driver.current_url
                    if "app.dentalhub.com" in current_url and "login" not in current_url.lower():
                        print("[UnitedDH PreAuth login] Login succeeded without OTP")
                        return "SUCCESS"

                    print(f"[UnitedDH PreAuth login] Unexpected state - URL: {current_url}")
                    return "SUCCESS"

                except Exception as e:
                    return f"ERROR: Login failed - {e}"

            if "app.dentalhub.com" in current_url:
                return "ALREADY_LOGGED_IN"

            return "SUCCESS"

        except Exception as e:
            return f"ERROR: Login exception - {e}"

    # ── Helpers ────────────────────────────────────────────────────────────────

    def _check_for_error_dialog(self):
        error_patterns = [
            ("Patient Not Found", "Patient Not Found - please check the Subscriber ID, DOB, and Payer selection"),
            ("Insufficient Information", "Insufficient Information - need Subscriber ID + DOB, or First Name + Last Name + DOB"),
            ("No Eligibility", "No eligibility information found for this patient"),
            ("Error", None),
        ]

        for pattern, default_msg in error_patterns:
            try:
                dialog_elem = self.driver.find_element(By.XPATH,
                    f"//modal-container//*[contains(text(),'{pattern}')] | "
                    f"//div[contains(@class,'modal')]//*[contains(text(),'{pattern}')]"
                )
                if dialog_elem.is_displayed():
                    try:
                        modal = self.driver.find_element(By.XPATH, "//modal-container | //div[contains(@class,'modal-dialog')]")
                        dialog_text = modal.text.strip()[:200]
                    except Exception:
                        dialog_text = dialog_elem.text.strip()[:200]

                    print(f"[UnitedDH PreAuth] Error dialog detected: {dialog_text}")

                    try:
                        dismiss_btn = self.driver.find_element(By.XPATH,
                            "//modal-container//button[contains(text(),'Ok') or contains(text(),'OK') or contains(text(),'Close')] | "
                            "//div[contains(@class,'modal')]//button[contains(text(),'Ok') or contains(text(),'OK') or contains(text(),'Close')]"
                        )
                        dismiss_btn.click()
                        print("[UnitedDH PreAuth] Dismissed error dialog")
                        time.sleep(1)
                    except Exception:
                        try:
                            close_btn = self.driver.find_element(By.XPATH, "//modal-container//button[@class='close']")
                            close_btn.click()
                        except Exception:
                            pass

                    error_msg = default_msg if default_msg else f"ERROR: {dialog_text}"
                    return f"ERROR: {error_msg}"
            except Exception:
                continue

        return None

    def _format_dob(self, dob_str):
        if dob_str and "-" in dob_str:
            dob_parts = dob_str.split("-")
            if len(dob_parts) == 3:
                return f"{dob_parts[1]}/{dob_parts[2]}/{dob_parts[0]}"
        return dob_str

    def _hide_browser(self):
        try:
            try:
                self.driver.get("about:blank")
                time.sleep(0.5)
            except Exception:
                pass
            try:
                self.driver.minimize_window()
                print("[UnitedDH PreAuth] Browser window minimized")
                return
            except Exception:
                pass
            try:
                self.driver.set_window_position(-10000, -10000)
                print("[UnitedDH PreAuth] Browser window moved off-screen")
                return
            except Exception:
                pass
            try:
                import subprocess
                subprocess.run(["xdotool", "getactivewindow", "windowminimize"],
                               timeout=3, capture_output=True)
                print("[UnitedDH PreAuth] Browser minimized via xdotool")
            except Exception:
                pass
        except Exception as e:
            print(f"[UnitedDH PreAuth] Could not hide browser: {e}")

    # ── Pre-auth steps ─────────────────────────────────────────────────────────

    def step1_search_patient(self):
        """
        Navigate to the eligibility page, fill member ID + DOB + payer, continue through
        the Select Insurance popup and Provider & Location dropdowns to land on the
        Selected Patient results page. (Identical to the claim worker's step1.)
        """
        from selenium.webdriver.common.action_chains import ActionChains

        try:
            print(f"[UnitedDH PreAuth] step1: memberId={self.memberId}, dob={self.dateOfBirth}")

            self.driver.get("https://app.dentalhub.com/app/patient/eligibility")
            time.sleep(3)
            print(f"[UnitedDH PreAuth] step1 URL: {self.driver.current_url}")

            try:
                WebDriverWait(self.driver, 10).until(
                    EC.presence_of_element_located((By.ID, "firstName_Back"))
                )
                print("[UnitedDH PreAuth] step1: Patient Information form loaded")
            except TimeoutException:
                print("[UnitedDH PreAuth] step1: Patient Information form not found")
                return "ERROR: step1 - Patient Information form not found"

            # Fill Subscriber ID
            if self.memberId:
                subscriber_id_selectors = [
                    "//input[@id='subscriberId_Front']",
                    "//input[@id='subscriberId_Back' or @id='subscriberID_Back']",
                    "//input[@id='memberId_Back' or @id='memberid_Back']",
                    "//input[@id='medicaidId_Back']",
                    "//label[contains(text(),'Subscriber ID')]/..//input[not(@id='firstName_Back') and not(@id='lastName_Back') and not(@id='dateOfBirth_Back')]",
                    "//input[contains(@placeholder,'Subscriber') or contains(@placeholder,'subscriber')]",
                    "//input[contains(@placeholder,'Medicaid') or contains(@placeholder,'medicaid')]",
                    "//input[contains(@placeholder,'Member') or contains(@placeholder,'member')]",
                ]
                subscriber_filled = False
                for sel in subscriber_id_selectors:
                    try:
                        sid_input = self.driver.find_element(By.XPATH, sel)
                        if sid_input.is_displayed():
                            sid_input.clear()
                            sid_input.send_keys(self.memberId)
                            field_id = sid_input.get_attribute("id") or "unknown"
                            print(f"[UnitedDH PreAuth] step1: Subscriber ID entered: {self.memberId} (field='{field_id}')")
                            subscriber_filled = True
                            break
                    except Exception:
                        continue

                if not subscriber_filled:
                    try:
                        all_inputs = self.driver.find_elements(By.XPATH, "//form//input[@type='text' or not(@type)]")
                        known_ids = {'firstName_Back', 'lastName_Back', 'dateOfBirth_Back', 'procedureDate_Back', 'insurerId'}
                        for inp in all_inputs:
                            inp_id = inp.get_attribute("id") or ""
                            if inp_id not in known_ids and inp.is_displayed():
                                inp.clear()
                                inp.send_keys(self.memberId)
                                print(f"[UnitedDH PreAuth] step1: Subscriber ID in fallback field id='{inp_id}'")
                                subscriber_filled = True
                                break
                    except Exception as e2:
                        print(f"[UnitedDH PreAuth] step1: Fallback subscriber field error: {e2}")

                if not subscriber_filled:
                    print(f"[UnitedDH PreAuth] step1: WARNING - Could not find Subscriber ID field")

            # Fill Date of Birth
            try:
                dob_input = self.driver.find_element(By.ID, "dateOfBirth_Back")
                dob_input.clear()
                dob_formatted = self._format_dob(self.dateOfBirth)
                dob_input.send_keys(dob_formatted)
                print(f"[UnitedDH PreAuth] step1: DOB entered: {dob_formatted}")
            except Exception as e:
                print(f"[UnitedDH PreAuth] step1: Error entering DOB: {e}")
                return "ERROR: step1 - Could not enter Date of Birth"

            time.sleep(1)

            # Dismiss any blocking overlays
            try:
                self.driver.execute_script("""
                    var dialogs = document.querySelectorAll('[role="dialog"], .cdk-overlay-container');
                    dialogs.forEach(function(d) { d.style.display = 'none'; });
                """)
            except Exception:
                pass

            # Select Payer: UnitedHealthcare Massachusetts
            print("[UnitedDH PreAuth] step1: Selecting Payer...")
            payer_selected = False

            try:
                payer_selectors = [
                    "//label[contains(text(),'Payer')]/following-sibling::ng-select",
                    "//label[contains(text(),'Payer')]/..//ng-select",
                    "//ng-select[contains(@placeholder,'Payer') or contains(@placeholder,'payer')]",
                    "//ng-select[.//input[contains(@placeholder,'Search by Payers')]]",
                ]
                payer_ng_select = None
                for sel in payer_selectors:
                    try:
                        elem = self.driver.find_element(By.XPATH, sel)
                        if elem.is_displayed():
                            payer_ng_select = elem
                            break
                    except Exception:
                        continue

                if payer_ng_select:
                    self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", payer_ng_select)
                    time.sleep(0.5)
                    payer_ng_select.click()
                    time.sleep(1)
                    search_input = payer_ng_select.find_element(By.XPATH,
                        ".//input[contains(@type,'text') or contains(@role,'combobox')]")
                    search_input.clear()
                    search_input.send_keys("UnitedHealthcare Massachusetts")
                    print("[UnitedDH PreAuth] step1: Typed payer search text")
                    time.sleep(2)
                    search_input.send_keys(Keys.ENTER)
                    print("[UnitedDH PreAuth] step1: Pressed Enter to select Payer")
                    time.sleep(0.5)
                    payer_selected = True
                else:
                    print("[UnitedDH PreAuth] step1: Could not find Payer ng-select element")
            except Exception as e:
                print(f"[UnitedDH PreAuth] step1: Payer selection error: {e}")

            if not payer_selected:
                print("[UnitedDH PreAuth] step1: WARNING - Could not select Payer")

            time.sleep(1)

            # Click Continue (Patient Info)
            try:
                continue_btn = WebDriverWait(self.driver, 10).until(
                    EC.element_to_be_clickable((By.XPATH, "//button[contains(text(),'Continue')]"))
                )
                continue_btn.click()
                print("[UnitedDH PreAuth] step1: Clicked Continue (Patient Info)")
                time.sleep(3)

                error_result = self._check_for_error_dialog()
                if error_result:
                    return error_result
            except Exception as e:
                return f"ERROR: step1 - Could not click Continue: {e}"

            # Click Ok on Select Insurance popup
            print("[UnitedDH PreAuth] step1: Checking for Select Insurance popup...")
            try:
                ok_btn = WebDriverWait(self.driver, 10).until(
                    EC.element_to_be_clickable((By.XPATH,
                        "//button[contains(@class,'btn-primary') and "
                        "(normalize-space(.)='Ok' or normalize-space(.)='OK' or normalize-space(.)='Okay')] | "
                        "//modal-container//button[normalize-space(.)='Ok' or normalize-space(.)='OK' or normalize-space(.)='Okay'] | "
                        "//div[contains(@class,'modal')]//button[normalize-space(.)='Ok' or normalize-space(.)='OK' or normalize-space(.)='Okay']"
                    ))
                )
                try:
                    self.driver.execute_script("arguments[0].click();", ok_btn)
                    print("[UnitedDH PreAuth] step1: Clicked OK on Select Insurance popup (JS)")
                except Exception:
                    ok_btn.click()
                    print("[UnitedDH PreAuth] step1: Clicked OK on Select Insurance popup (direct)")
                try:
                    WebDriverWait(self.driver, 10).until(EC.staleness_of(ok_btn))
                    print("[UnitedDH PreAuth] step1: Select Insurance modal closed")
                except TimeoutException:
                    print("[UnitedDH PreAuth] step1: Modal staleness timeout — continuing anyway")
                WebDriverWait(self.driver, 5).until(
                    EC.presence_of_element_located((By.XPATH,
                        "//label[@for='treatmentLocation'] | //label[@for='paymentGroupId']"))
                )
            except TimeoutException:
                print("[UnitedDH PreAuth] step1: Select Insurance popup not found — proceeding")

            # Provider & Location page — select Treatment Location and Billing Entity, then Continue
            print("[UnitedDH PreAuth] step1: Waiting for Provider & Location page...")
            try:
                WebDriverWait(self.driver, 20).until(
                    EC.visibility_of_element_located((By.XPATH, "//label[@for='paymentGroupId']"))
                )

                print("[UnitedDH PreAuth] step1: Selecting Treatment Location...")
                location_selected = False
                try:
                    location_ng = WebDriverWait(self.driver, 10).until(
                        EC.element_to_be_clickable((By.XPATH,
                            "//label[@for='treatmentLocation']/following-sibling::ng-select | "
                            "//label[@for='treatmentLocation']/..//ng-select"
                        ))
                    )
                    self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", location_ng)
                    arrow = location_ng.find_element(By.CSS_SELECTOR, ".ng-arrow-wrapper")
                    arrow.click()
                    time.sleep(1)
                    try:
                        summit_option = WebDriverWait(self.driver, 5).until(
                            EC.element_to_be_clickable((By.XPATH,
                                f"//ng-dropdown-panel//div[contains(@class,'ng-option') and contains(.,'{self.payment_group_id}')]"
                            ))
                        )
                        summit_option.click()
                        print(f"[UnitedDH PreAuth] step1: Selected Treatment Location: {self.payment_group_id}")
                        location_selected = True
                    except TimeoutException:
                        try:
                            first_option = self.driver.find_element(By.XPATH,
                                "//ng-dropdown-panel//div[contains(@class,'ng-option')]"
                            )
                            option_text = first_option.text.strip()
                            first_option.click()
                            print(f"[UnitedDH PreAuth] step1: Selected Treatment Location (fallback): {option_text}")
                            location_selected = True
                        except Exception:
                            print("[UnitedDH PreAuth] step1: No options available in Treatment Location dropdown")
                    time.sleep(1)
                except Exception as e:
                    print(f"[UnitedDH PreAuth] step1: Treatment Location selection failed: {e}")

                if not location_selected:
                    print("[UnitedDH PreAuth] step1: WARNING - Could not select Treatment Location")

                print("[UnitedDH PreAuth] step1: Selecting Billing Entity...")
                billing_selected = False
                try:
                    taxonomy_input = self.driver.find_element(By.ID, "paymentGroupId")
                    if taxonomy_input.is_displayed():
                        self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", taxonomy_input)
                        taxonomy_input.click()
                        print("[UnitedDH PreAuth] step1: Clicked Billing Entity dropdown")
                        time.sleep(1)
                        try:
                            summit_option = WebDriverWait(self.driver, 5).until(
                                EC.element_to_be_clickable((By.XPATH,
                                    f"//ng-dropdown-panel//div[contains(@class,'ng-option') and contains(.,'{self.payment_group_id}')]"
                                ))
                            )
                            summit_option.click()
                            print(f"[UnitedDH PreAuth] step1: Selected Billing Entity: {self.payment_group_id}")
                            billing_selected = True
                        except TimeoutException:
                            try:
                                first_option = self.driver.find_element(By.XPATH,
                                    "//ng-dropdown-panel//div[contains(@class,'ng-option')]"
                                )
                                option_text = first_option.text.strip()
                                first_option.click()
                                print(f"[UnitedDH PreAuth] step1: Selected Billing Entity: {option_text}")
                                billing_selected = True
                            except Exception:
                                print("[UnitedDH PreAuth] step1: No options available in Billing Entity dropdown")
                        ActionChains(self.driver).send_keys(Keys.ESCAPE).perform()
                        time.sleep(1)
                except Exception as e:
                    print(f"[UnitedDH PreAuth] step1: Billing Entity selection failed: {e}")

                if not billing_selected:
                    print("[UnitedDH PreAuth] step1: WARNING - Could not select Billing Entity")

                continue_btn2 = WebDriverWait(self.driver, 10).until(
                    EC.element_to_be_clickable((By.XPATH, "(//button[contains(normalize-space(.),'Continue')])[last()]"))
                )
                self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", continue_btn2)
                self.driver.execute_script("arguments[0].click();", continue_btn2)
                print("[UnitedDH PreAuth] step1: Clicked Continue (Provider & Location) → Selected Patient page")
                time.sleep(5)

            except TimeoutException:
                try:
                    results_elem = self.driver.find_element(By.XPATH,
                        "//*[contains(text(),'Selected Patient') or contains(@id,'patient-name') or contains(@id,'eligibility')]"
                    )
                    if results_elem.is_displayed():
                        print("[UnitedDH PreAuth] step1: Already on Selected Patient page")
                        return "OK"
                except Exception:
                    pass
                print("[UnitedDH PreAuth] step1: Continue not found on Provider & Location page — proceeding")
            except Exception as e:
                print(f"[UnitedDH PreAuth] step1: Error clicking Continue on Provider & Location page: {e}")
                error_result = self._check_for_error_dialog()
                if error_result:
                    return error_result

            error_result = self._check_for_error_dialog()
            if error_result:
                return error_result

            print("[UnitedDH PreAuth] step1: Patient search complete — on Selected Patient page")
            return "OK"

        except Exception as e:
            return f"ERROR: step1_search_patient - {e}"

    def step2_click_preauth_button(self):
        """
        On the Selected Patient results page, click the Submit Pre-Authorization button.
        Tries pre-auth-specific IDs first, then falls back to text matching.
        """
        try:
            print("[UnitedDH PreAuth] step2: Looking for Submit Pre-Authorization button...")
            time.sleep(2)

            # Try pre-auth specific button IDs first
            preauth_selectors = [
                (By.ID, "btnSubmitAuthorization"),
                (By.ID, "btnSubmitPreAuth"),
                (By.ID, "btnPreAuth"),
                (By.ID, "btnPreAuthorization"),
                (By.XPATH,
                    "//button[contains(normalize-space(.),'Submit Authorization') or "
                    "contains(normalize-space(.),'Pre-Auth') or "
                    "contains(normalize-space(.),'Pre Authorization') or "
                    "contains(normalize-space(.),'PreAuth') or "
                    "contains(normalize-space(.),'Prior Auth')]"
                ),
            ]

            for by, selector in preauth_selectors:
                try:
                    btn = WebDriverWait(self.driver, 15).until(
                        EC.element_to_be_clickable((by, selector))
                    )
                    self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", btn)
                    time.sleep(0.5)
                    btn.click()
                    print(f"[UnitedDH PreAuth] step2: Clicked pre-auth button ({selector})")
                    time.sleep(4)
                    print(f"[UnitedDH PreAuth] step2 URL: {self.driver.current_url}")
                    return "OK"
                except (TimeoutException, Exception):
                    continue

            return "ERROR: step2 - Could not find Submit Pre-Authorization button on Selected Patient page"

        except Exception as e:
            return f"ERROR: step2_click_preauth_button - {e}"

    def step3_continue_prefilled(self):
        """
        Pre-auth page: member ID and DOB are pre-filled.
        Select Payer by typing "UnitedHealthcare Massachusetts" + Enter, then click Continue.
        """
        try:
            print("[UnitedDH PreAuth] step3: Pre-auth page — selecting Payer then clicking Continue...")

            WebDriverWait(self.driver, 10).until(
                EC.presence_of_element_located((By.XPATH,
                    "//label[contains(text(),'Payer')] | //ng-select"
                ))
            )

            # Fill procedure date from tentative service date.
            # If tentative date == today, user did not change it — use today + 3 days instead.
            if self.serviceDate:
                try:
                    from datetime import date, timedelta
                    service_date = date.fromisoformat(self.serviceDate[:10])
                    if service_date == date.today():
                        service_date = service_date + timedelta(days=3)
                        print("[UnitedDH PreAuth] step3: Tentative date is today — using today + 3 days")
                    proc_date_str = service_date.strftime("%m/%d/%Y")
                    proc_input = self.driver.find_element(By.ID, "procedureDate_Back")
                    proc_input.click()
                    proc_input.send_keys(Keys.CONTROL, "a")
                    proc_input.send_keys(proc_date_str)
                    print(f"[UnitedDH PreAuth] step3: Procedure date entered: {proc_date_str}")
                except Exception as e:
                    print(f"[UnitedDH PreAuth] step3: WARNING - Could not fill procedure date: {e}")

            payer_selected = False
            try:
                payer_selectors = [
                    "//label[contains(text(),'Payer')]/following-sibling::ng-select",
                    "//label[contains(text(),'Payer')]/..//ng-select",
                    "//ng-select[contains(@placeholder,'Payer') or contains(@placeholder,'payer')]",
                    "//ng-select[.//input[contains(@placeholder,'Search by Payers')]]",
                ]
                payer_ng_select = None
                for sel in payer_selectors:
                    try:
                        elem = self.driver.find_element(By.XPATH, sel)
                        if elem.is_displayed():
                            payer_ng_select = elem
                            break
                    except Exception:
                        continue

                if payer_ng_select:
                    self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", payer_ng_select)
                    time.sleep(0.5)
                    payer_ng_select.click()
                    time.sleep(1)
                    search_input = payer_ng_select.find_element(By.XPATH,
                        ".//input[contains(@type,'text') or contains(@role,'combobox')]")
                    search_input.clear()
                    search_input.send_keys("UnitedHealthcare Massachusetts")
                    print("[UnitedDH PreAuth] step3: Typed payer search text")
                    time.sleep(2)
                    search_input.send_keys(Keys.ENTER)
                    print("[UnitedDH PreAuth] step3: Pressed Enter to select Payer")
                    time.sleep(0.5)
                    payer_selected = True
                else:
                    print("[UnitedDH PreAuth] step3: Could not find Payer ng-select element")
            except Exception as e:
                print(f"[UnitedDH PreAuth] step3: Payer selection error: {e}")

            if not payer_selected:
                print("[UnitedDH PreAuth] step3: WARNING - Could not select Payer")

            time.sleep(1)

            continue_btn = WebDriverWait(self.driver, 15).until(
                EC.element_to_be_clickable((By.XPATH, "//button[contains(text(),'Continue')]"))
            )
            self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", continue_btn)
            continue_btn.click()
            print("[UnitedDH PreAuth] step3: Clicked Continue")
            time.sleep(4)

            error_result = self._check_for_error_dialog()
            if error_result:
                return error_result

            print(f"[UnitedDH PreAuth] step3 URL: {self.driver.current_url}")
            return "OK"

        except Exception as e:
            return f"ERROR: step3_continue_prefilled - {e}"

    def step4_select_insurance_ok(self):
        """Click Ok on the Select Insurance popup."""
        try:
            print("[UnitedDH PreAuth] step4: Waiting for Select Insurance popup...")

            try:
                ok_btn = WebDriverWait(self.driver, 10).until(
                    EC.element_to_be_clickable((By.XPATH,
                        "//button[@type='button' and contains(@class,'btn-primary') and "
                        "(normalize-space(text())='Ok' or normalize-space(text())='OK')] | "
                        "//modal-container//button[normalize-space(.)='Ok' or normalize-space(.)='OK'] | "
                        "//div[contains(@class,'modal')]//button[normalize-space(.)='Ok' or normalize-space(.)='OK']"
                    ))
                )
                ActionChains(self.driver).move_to_element(ok_btn).pause(0.5).click().perform()
                print("[UnitedDH PreAuth] step4: Clicked Ok on Select Insurance popup")
                try:
                    WebDriverWait(self.driver, 8).until(EC.staleness_of(ok_btn))
                    print("[UnitedDH PreAuth] step4: Select Insurance modal closed")
                except TimeoutException:
                    print("[UnitedDH PreAuth] step4: Modal staleness timeout — continuing anyway")
            except TimeoutException:
                print("[UnitedDH PreAuth] step4: Select Insurance popup not found — proceeding")

            print(f"[UnitedDH PreAuth] step4 URL: {self.driver.current_url}")
            return "OK"

        except Exception as e:
            return f"ERROR: step4_select_insurance_ok - {e}"

    def step5_practitioner_continue(self):
        """Practitioner & Location page — click Continue only, no dropdown selections."""
        try:
            print("[UnitedDH PreAuth] step5: Waiting for Practitioner & Location page...")

            try:
                WebDriverWait(self.driver, 20).until(
                    EC.visibility_of_element_located((By.XPATH,
                        "//label[@for='treatmentLocation'] | //label[@for='paymentGroupId'] | "
                        "//label[@for='paymentGroup']"
                    ))
                )
                print("[UnitedDH PreAuth] step5: Practitioner & Location page loaded")
            except TimeoutException:
                print("[UnitedDH PreAuth] step5: Practitioner & Location labels not found — trying Continue anyway")

            continue_btn = WebDriverWait(self.driver, 15).until(
                EC.element_to_be_clickable((By.XPATH,
                    "//button[contains(@class,'btn-primary') and contains(normalize-space(text()),'Continue')] | "
                    "//button[contains(normalize-space(text()),'Continue')]"
                ))
            )
            self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", continue_btn)
            continue_btn.click()
            print("[UnitedDH PreAuth] step5: Clicked Continue — waiting for Code Entry page")
            time.sleep(3)

            try:
                WebDriverWait(self.driver, 10).until(
                    EC.presence_of_element_located((By.ID, "procedureCode"))
                )
                print("[UnitedDH PreAuth] step5: Code Entry page loaded (procedureCode found)")
            except TimeoutException:
                print("[UnitedDH PreAuth] step5: procedureCode input not found — proceeding anyway")

            print(f"[UnitedDH PreAuth] step5 URL: {self.driver.current_url}")
            return "OK"

        except Exception as e:
            return f"ERROR: step5_practitioner_continue - {e}"

    def step6_fill_preauth_form(self):
        """
        Fill CDT codes, tooth, billed amount for each service line.
        Same structure as the claim form step.
        """
        try:
            active_lines = [
                ln for ln in self.serviceLines
                if str(ln.get("procedureCode") or "").strip()
            ]
            print(f"[UnitedDH PreAuth] step6: {len(active_lines)} service line(s)")

            if not active_lines:
                print("[UnitedDH PreAuth] step6: No service lines — skipping")
                return "OK"

            for idx, line in enumerate(active_lines):
                code = str(line.get("procedureCode") or "").strip().upper()
                billed = str(
                    line.get("totalBilled") or
                    line.get("billedAmount") or
                    line.get("fee") or ""
                ).strip()
                tooth = str(line.get("toothNumber") or line.get("tooth_number") or "").strip()
                surface = str(line.get("toothSurface") or line.get("tooth_surface") or "").strip().upper()
                print(f"[UnitedDH PreAuth] step6: line {idx}: code={code}, billed={billed}, tooth={tooth}, surface={surface}")

                # Click btnAddItem to open/activate the procedure row
                try:
                    add_btn = WebDriverWait(self.driver, 10).until(
                        EC.element_to_be_clickable((By.ID, "btnAddItem"))
                    )
                    self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", add_btn)
                    add_btn.click()
                    print(f"[UnitedDH PreAuth] step6: clicked btnAddItem to open row {idx}")
                    time.sleep(1)
                except Exception as e:
                    print(f"[UnitedDH PreAuth] step6: could not click btnAddItem to open row {idx}: {e}")

                # Type CDT code
                try:
                    proc_input = WebDriverWait(self.driver, 10).until(
                        EC.element_to_be_clickable((By.ID, "procedureCode"))
                    )
                    self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", proc_input)
                    self.driver.execute_script("arguments[0].click();", proc_input)
                    proc_input.send_keys(Keys.CONTROL + "a")
                    proc_input.send_keys(Keys.DELETE)
                    proc_input.send_keys(code)
                    print(f"[UnitedDH PreAuth] step6: typed procedure code: {code}")
                    time.sleep(0.5)
                except Exception as e:
                    print(f"[UnitedDH PreAuth] step6: could not type procedure code for row {idx}: {e}")
                    continue

                # Click btnAddItem to confirm code and reveal billed amount input
                try:
                    add_btn = WebDriverWait(self.driver, 8).until(
                        EC.element_to_be_clickable((By.ID, "btnAddItem"))
                    )
                    self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", add_btn)
                    add_btn.click()
                    print(f"[UnitedDH PreAuth] step6: clicked btnAddItem to reveal billedAmount for row {idx}")
                    time.sleep(1.5)
                except Exception as e:
                    print(f"[UnitedDH PreAuth] step6: could not click btnAddItem for billed amount row {idx}: {e}")
                    continue

                # Fill tooth number
                if tooth:
                    try:
                        tooth_input = WebDriverWait(self.driver, 5).until(
                            EC.element_to_be_clickable((By.ID, "tooth"))
                        )
                        self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", tooth_input)
                        tooth_input.click()
                        tooth_input.send_keys(Keys.CONTROL + "a")
                        tooth_input.send_keys(Keys.DELETE)
                        tooth_input.send_keys(tooth)
                        print(f"[UnitedDH PreAuth] step6: entered tooth number: {tooth}")
                        time.sleep(0.3)
                    except Exception as e:
                        print(f"[UnitedDH PreAuth] step6: could not fill tooth number for row {idx}: {e}")

                # Click surface boxes
                if surface:
                    try:
                        surface_boxes = self.driver.find_elements(By.XPATH,
                            "//div[contains(@class,'claim-add-item-group__box')]")
                        if surface_boxes:
                            for letter in surface:
                                if not letter.strip():
                                    continue
                                try:
                                    box = self.driver.find_element(By.XPATH,
                                        f"//div[contains(@class,'claim-add-item-group__box') "
                                        f"and not(contains(@class,'--disabled')) "
                                        f"and @id='{letter}']"
                                    )
                                    self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", box)
                                    box.click()
                                    print(f"[UnitedDH PreAuth] step6: clicked surface '{letter}'")
                                    time.sleep(0.2)
                                except Exception:
                                    print(f"[UnitedDH PreAuth] step6: surface '{letter}' not found or disabled")
                        else:
                            print(f"[UnitedDH PreAuth] step6: no surface boxes on page — skipping")
                    except Exception as e:
                        print(f"[UnitedDH PreAuth] step6: surface click error for row {idx}: {e}")

                # Fill billed amount
                if billed:
                    try:
                        billed_input = WebDriverWait(self.driver, 8).until(
                            EC.element_to_be_clickable((By.ID, "billedAmount"))
                        )
                        self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", billed_input)
                        billed_input.click()
                        billed_input.send_keys(Keys.CONTROL + "a")
                        billed_input.send_keys(Keys.DELETE)
                        billed_input.send_keys(billed)
                        print(f"[UnitedDH PreAuth] step6: entered billed amount: {billed}")
                        time.sleep(0.5)
                    except Exception as e:
                        print(f"[UnitedDH PreAuth] step6: could not fill billed amount for row {idx}: {e}")

                # Click the span "Add" button to confirm the row
                try:
                    span_add = WebDriverWait(self.driver, 8).until(
                        EC.element_to_be_clickable((By.XPATH,
                            "//span[contains(@class,'ng-star-inserted') and normalize-space(text())='Add'] | "
                            "//button[normalize-space(text())='Add' and not(@id='btnAddItem')] | "
                            "//span[normalize-space(text())='Add']"
                        ))
                    )
                    self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", span_add)
                    span_add.click()
                    print(f"[UnitedDH PreAuth] step6: clicked span Add — row {idx} confirmed")
                    time.sleep(1)
                except Exception as e:
                    print(f"[UnitedDH PreAuth] step6: could not click span Add for row {idx}: {e}")

            print("[UnitedDH PreAuth] step6: Done filling pre-auth form")
            return "OK"

        except Exception as e:
            return f"ERROR: step6_fill_preauth_form - {e}"

    def step7_attach_files(self):
        """
        If there are claim files:
          1. Click the fa-caret-up dropdown icon to reveal the Add Document button
          2. Click id="upload-document"
          3. Send the absolute file path to the file input
        """
        try:
            if not self.claimFiles:
                print("[UnitedDH PreAuth] step7: No files to attach")
                return "OK"

            try:
                caret = WebDriverWait(self.driver, 8).until(
                    EC.element_to_be_clickable((By.XPATH,
                        "//em[contains(@class,'fa-caret-up')] | "
                        "//i[contains(@class,'fa-caret-up')] | "
                        "//*[contains(@class,'fa') and contains(@class,'fa-caret-up')]"
                    ))
                )
                self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", caret)
                caret.click()
                print("[UnitedDH PreAuth] step7: Clicked caret-up to expand Attached Documents")
                time.sleep(1)
            except Exception as e:
                print(f"[UnitedDH PreAuth] step7: Could not click caret (section may already be open): {e}")

            attached = 0
            for cf in self.claimFiles:
                relative_path = cf.get("filePath") or ""
                if not relative_path:
                    print(f"[UnitedDH PreAuth] step7: Skipping file with no filePath: {cf}")
                    continue

                abs_path = os.path.normpath(os.path.join(_BACKEND_CWD, relative_path.lstrip("/")))
                if not os.path.isfile(abs_path):
                    print(f"[UnitedDH PreAuth] step7: File not found on disk: {abs_path}")
                    continue

                print(f"[UnitedDH PreAuth] step7: Attaching: {abs_path}")
                try:
                    file_input = WebDriverWait(self.driver, 8).until(
                        EC.presence_of_element_located((By.XPATH, "//input[@type='file']"))
                    )
                    self.driver.execute_script("arguments[0].removeAttribute('class');", file_input)
                    file_input.send_keys(abs_path)
                    WebDriverWait(self.driver, 60).until(
                        EC.element_to_be_clickable((By.XPATH,
                            "//button[contains(normalize-space(.),'Submit Authorization') or "
                            "contains(normalize-space(.),'Submit Pre-Auth') or "
                            "contains(normalize-space(.),'Submit Pre Authorization') or "
                            "contains(normalize-space(.),'Submit Pre-Authorization') or "
                            "contains(normalize-space(.),'Submit Claim')]"
                        ))
                    )
                    print(f"[UnitedDH PreAuth] step7: Attached: {os.path.basename(abs_path)}")
                    attached += 1
                except Exception as e:
                    print(f"[UnitedDH PreAuth] step7: Could not attach {abs_path}: {e}")

            print(f"[UnitedDH PreAuth] step7: Attached {attached}/{len(self.claimFiles)} file(s)")
            return "OK"

        except Exception as e:
            return f"ERROR: step7_attach_files - {e}"

    def step8_submit_preauth(self):
        """
        Click Submit Pre-Authorization (or Submit Claim) on the form page, then click
        "View Status and History" on the post-submit popup.
        """
        try:
            print(f"[UnitedDH PreAuth] step8: submitting pre-auth — URL: {self.driver.current_url}")

            submit_btn = WebDriverWait(self.driver, 15).until(
                EC.element_to_be_clickable((By.XPATH,
                    "//button[contains(normalize-space(.),'Submit Authorization') or "
                    "contains(normalize-space(.),'Submit Pre-Auth') or "
                    "contains(normalize-space(.),'Submit Pre Authorization') or "
                    "contains(normalize-space(.),'Submit Pre-Authorization') or "
                    "contains(normalize-space(.),'Submit Claim')]"
                ))
            )
            self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", submit_btn)
            time.sleep(0.5)
            self.driver.execute_script("arguments[0].click();", submit_btn)
            print("[UnitedDH PreAuth] step8: Clicked Submit — waiting for post-submit popup")
            time.sleep(3)

            try:
                view_btn = WebDriverWait(self.driver, 15).until(
                    EC.element_to_be_clickable((By.XPATH,
                        "//button[contains(normalize-space(.),'View Status and History')] | "
                        "//a[contains(normalize-space(.),'View Status and History')]"
                    ))
                )
                view_btn.click()
                print("[UnitedDH PreAuth] step8: Clicked 'View Status and History'")
                time.sleep(3)
            except TimeoutException:
                print("[UnitedDH PreAuth] step8: Post-submit popup not found — proceeding to step9")

            print(f"[UnitedDH PreAuth] step8: URL after popup: {self.driver.current_url}")
            return "OK"

        except Exception as e:
            return f"ERROR: step8_submit_preauth - {e}"

    def step9_save_confirmation_pdf(self):
        """
        On the Status & History page, read the pre-auth/reference number from the
        first row, then save the page as PDF.
        """
        try:
            print("[UnitedDH PreAuth] step9: waiting for Status & History page")

            WebDriverWait(self.driver, 40).until(
                lambda d: "status" in d.current_url.lower() or "history" in d.current_url.lower()
                          or d.find_elements(By.XPATH, "//td | //th[contains(text(),'Reference')]")
            )
            time.sleep(3)
            print(f"[UnitedDH PreAuth] step9: Status & History URL: {self.driver.current_url}")

            # Wait for table rows with actual data (not just headers)
            try:
                WebDriverWait(self.driver, 20).until(
                    EC.presence_of_element_located((By.XPATH, "//table//tr[td]"))
                )
                time.sleep(2)
            except Exception:
                print("[UnitedDH PreAuth] step9: Table rows did not appear — proceeding with body scan")

            preauth_number = None

            # 1. Try URL — pre-auth ID often appears in the URL path
            current_url = self.driver.current_url
            url_match = re.search(r'/(\d{6,})', current_url)
            if url_match:
                preauth_number = url_match.group(1)
                print(f"[UnitedDH PreAuth] step9: Pre-auth number from URL: {preauth_number!r}")

            # 2. Scan all table cells and log them for debugging
            if not preauth_number:
                try:
                    all_cells = self.driver.find_elements(By.XPATH, "//table//tr[td]/td")
                    cell_texts = [c.text.strip() for c in all_cells if c.text.strip()]
                    print(f"[UnitedDH PreAuth] step9: Table cells: {cell_texts[:20]}")

                    # Try each cell — alphanumeric first (e.g. A0260616190876), then pure digits
                    for cell_text in cell_texts:
                        # Alphanumeric like A0260616190876 or PA12345678
                        m = re.search(r'\b([A-Z]{1,4}\d{6,})\b', cell_text)
                        if m:
                            preauth_number = m.group(1)
                            print(f"[UnitedDH PreAuth] step9: Pre-auth number (alphanumeric): {preauth_number!r}")
                            break
                        # 14-digit pure numeric ref
                        m = re.search(r'\b(\d{14})\b', cell_text)
                        if m:
                            preauth_number = m.group(1)
                            print(f"[UnitedDH PreAuth] step9: Pre-auth number (14-digit): {preauth_number!r}")
                            break
                        # 8-13 digit fallback
                        m = re.search(r'\b(\d{8,13})\b', cell_text)
                        if m:
                            preauth_number = m.group(1)
                            print(f"[UnitedDH PreAuth] step9: Pre-auth number (8-13 digit): {preauth_number!r}")
                            break
                except Exception as e:
                    print(f"[UnitedDH PreAuth] step9: Table cell scan failed: {e}")

            # 3. Full body scan fallback
            if not preauth_number:
                try:
                    body_text = self.driver.find_element(By.TAG_NAME, "body").text
                    print(f"[UnitedDH PreAuth] step9: Body text (first 500 chars): {body_text[:500]!r}")
                    m = re.search(r'\b([A-Z]{1,4}\d{6,})\b', body_text)
                    if m:
                        preauth_number = m.group(1)
                    else:
                        m = re.search(r'\b(\d{14})\b', body_text)
                        if m:
                            preauth_number = m.group(1)
                        else:
                            m = re.search(r'\b(\d{8,13})\b', body_text)
                            if m:
                                preauth_number = m.group(1)
                    if preauth_number:
                        print(f"[UnitedDH PreAuth] step9: Pre-auth number (body scan): {preauth_number!r}")
                    else:
                        print("[UnitedDH PreAuth] step9: No pre-auth number found in body")
                except Exception as e:
                    print(f"[UnitedDH PreAuth] step9: Body scan failed: {e}")

            print(f"[UnitedDH PreAuth] step9: Final pre-auth number: {preauth_number!r}")

            shared_downloads = os.path.join(_SERVICE_DIR, "downloads")
            os.makedirs(shared_downloads, exist_ok=True)
            safe_member = "".join(c for c in str(self.memberId) if c.isalnum() or c in "-_.")
            safe_preauth = ("_" + preauth_number[:20]) if preauth_number else ""
            timestamp = time.strftime("%Y%m%d_%H%M%S")
            pdf_filename = f"uniteddh_preauth_confirmation_{safe_member}{safe_preauth}_{timestamp}.pdf"
            pdf_path = os.path.join(shared_downloads, pdf_filename)

            try:
                pdf_data = self.driver.execute_cdp_cmd("Page.printToPDF", {
                    "printBackground": True,
                    "paperWidth": 8.5,
                    "paperHeight": 11,
                    "marginTop": 0.4,
                    "marginBottom": 0.4,
                    "marginLeft": 0.4,
                    "marginRight": 0.4,
                })
                pdf_bytes = base64.b64decode(pdf_data["data"])
                with open(pdf_path, "wb") as f:
                    f.write(pdf_bytes)
                print(f"[UnitedDH PreAuth] step9: PDF saved: {pdf_path}")
            except Exception as e:
                print(f"[UnitedDH PreAuth] step9: PDF capture failed: {e}")
                return f"ERROR: step9 PDF failed: {e}"

            self._hide_browser()

            return {
                "status": "success",
                "pdf_path": pdf_path,
                "preAuthNumber": preauth_number,
            }

        except Exception as e:
            return f"ERROR: step9_save_confirmation_pdf - {e}"

    # ── Main workflow ──────────────────────────────────────────────────────────

    def main_workflow(self, url):
        try:
            self.config_driver()

            login_result = self.login(url)
            print(f"[main_workflow] Login result: {login_result}")

            if login_result == "OTP_REQUIRED":
                return {"status": "otp_required", "message": "OTP required after login"}

            if isinstance(login_result, str) and login_result.startswith("ERROR"):
                return {"status": "error", "message": login_result}

            step1_result = self.step1_search_patient()
            print(f"[main_workflow] step1 result: {step1_result}")
            if isinstance(step1_result, str) and step1_result.startswith("ERROR"):
                return {"status": "error", "message": step1_result}

            step2_result = self.step2_click_preauth_button()
            print(f"[main_workflow] step2 result: {step2_result}")
            if isinstance(step2_result, str) and step2_result.startswith("ERROR"):
                return {"status": "error", "message": step2_result}

            step3_result = self.step3_continue_prefilled()
            print(f"[main_workflow] step3 result: {step3_result}")
            if isinstance(step3_result, str) and step3_result.startswith("ERROR"):
                return {"status": "error", "message": step3_result}

            step4_result = self.step4_select_insurance_ok()
            print(f"[main_workflow] step4 result: {step4_result}")
            if isinstance(step4_result, str) and step4_result.startswith("ERROR"):
                return {"status": "error", "message": step4_result}

            step5_result = self.step5_practitioner_continue()
            print(f"[main_workflow] step5 result: {step5_result}")
            if isinstance(step5_result, str) and step5_result.startswith("ERROR"):
                return {"status": "error", "message": step5_result}

            step6_result = self.step6_fill_preauth_form()
            print(f"[main_workflow] step6 result: {step6_result}")
            if isinstance(step6_result, str) and step6_result.startswith("ERROR"):
                return {"status": "error", "message": step6_result}

            step7_result = self.step7_attach_files()
            print(f"[main_workflow] step7 result: {step7_result}")
            if isinstance(step7_result, str) and step7_result.startswith("ERROR"):
                return {"status": "error", "message": step7_result}

            step8_result = self.step8_submit_preauth()
            print(f"[main_workflow] step8 result: {step8_result}")
            if isinstance(step8_result, str) and step8_result.startswith("ERROR"):
                return {"status": "error", "message": step8_result}

            step9_result = self.step9_save_confirmation_pdf()
            print(f"[main_workflow] step9 result: {step9_result}")
            return step9_result

        except Exception as e:
            return {"status": "error", "message": str(e)}
