from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
import os
import re
import json
import base64
import tempfile
from datetime import date

from cca_browser_manager import get_browser_manager

LANDING_URL = "https://pwp.sciondental.com/PWP/Landing"
AUTH_ENTRY_URL = "https://pwp.sciondental.com/PWP/Dental/AuthEntry"


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

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

        patient_name = (claim.get("patientName") or "").strip()
        parts = patient_name.split()
        first_name = parts[0] if parts else ""
        last_name = " ".join(parts[1:]) if len(parts) > 1 else ""

        self.cca_username = claim.get("cca_username", "") or raw.get("cca_username", "")
        self.cca_password = claim.get("cca_password", "") or raw.get("cca_password", "")
        self.memberId = claim.get("memberId", "")
        self.dateOfBirth = claim.get("dateOfBirth", "")
        # PreAuth uses tentative service date; falls back to serviceDate for compatibility
        self.serviceDate = (
            claim.get("tentativeServiceDate")
            or claim.get("serviceDate", "")
        )
        self.firstName = claim.get("firstName", "") or first_name
        self.lastName = claim.get("lastName", "") or last_name
        self.serviceLines = claim.get("serviceLines", []) or []
        files_raw = raw.get("files", []) or []
        self.uploadFiles = [f for f in files_raw if f.get("bufferBase64")]

        print(f"[CCA PreAuth] Init — member={self.memberId}, "
              f"patient={self.firstName} {self.lastName}, "
              f"lines={len(self.serviceLines)}, files={len(self.uploadFiles)}")

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

    # ------------------------------------------------------------------ #
    #  Login  (identical to CCA Claim)                                     #
    # ------------------------------------------------------------------ #
    def _page_has_logged_in_content(self):
        try:
            # Also accept AuthEntry page content as proof of being logged in
            current_url = self.driver.current_url
            if "AuthEntry" in current_url or "ClaimEntry" in current_url:
                return True
            body = self.driver.find_element(By.TAG_NAME, "body").text
            return any(x in body for x in [
                "Verify Patient Eligibility", "Patient Management",
                "Submit a Claim", "Claim Inquiry", "Submit Claims",
                "Authorization Entry", "Subscriber ID", "Authorization",
            ])
        except Exception:
            return False

    def login(self, url):
        browser_manager = get_browser_manager()
        try:
            if self.cca_username and browser_manager.credentials_changed(self.cca_username):
                try:
                    self.driver.delete_all_cookies()
                except Exception:
                    pass
                browser_manager.clear_credentials_hash()
                self.driver.get(url)
                time.sleep(2)

            try:
                current_url = self.driver.current_url
                if ("sciondental.com" in current_url
                        and "login" not in current_url.lower()
                        and self._page_has_logged_in_content()):
                    print("[CCA PreAuth login] Already logged in")
                    return "ALREADY_LOGGED_IN"
            except Exception:
                pass

            print("[CCA PreAuth login] Checking session at landing page...")
            self.driver.get(LANDING_URL)
            try:
                WebDriverWait(self.driver, 10).until(
                    lambda d: "sciondental.com" in d.current_url
                    and d.execute_script("return document.readyState") == "complete"
                )
            except TimeoutException:
                pass

            if self._page_has_logged_in_content():
                print("[CCA PreAuth login] Session still valid")
                return "ALREADY_LOGGED_IN"

            print("[CCA PreAuth login] Session expired — logging in...")
            self.driver.get(url)
            try:
                WebDriverWait(self.driver, 15).until(
                    EC.presence_of_element_located((By.XPATH, "//input")))
            except TimeoutException:
                pass

            # Username
            username_field = None
            for sel in [
                (By.ID, "Username"),
                (By.NAME, "Username"),
                (By.XPATH, "//input[@type='text']"),
                (By.XPATH, "//input[@type='email']"),
            ]:
                try:
                    f = WebDriverWait(self.driver, 6).until(EC.element_to_be_clickable(sel))
                    username_field = f
                    break
                except Exception:
                    continue

            if not username_field:
                if self._page_has_logged_in_content():
                    return "ALREADY_LOGGED_IN"
                return "ERROR: Could not find username field"

            username_field.click()
            username_field.send_keys(Keys.CONTROL + "a")
            username_field.send_keys(Keys.DELETE)
            username_field.send_keys(self.cca_username)
            time.sleep(0.3)

            # Password
            pw_field = None
            for sel in [
                (By.ID, "Password"),
                (By.NAME, "Password"),
                (By.XPATH, "//input[@type='password']"),
            ]:
                try:
                    f = WebDriverWait(self.driver, 6).until(EC.element_to_be_clickable(sel))
                    pw_field = f
                    break
                except Exception:
                    continue

            if not pw_field:
                return "ERROR: Password field not found"

            pw_field.click()
            pw_field.send_keys(Keys.CONTROL + "a")
            pw_field.send_keys(Keys.DELETE)
            pw_field.send_keys(self.cca_password)
            time.sleep(0.3)

            # Submit
            submitted = False
            for sel in [
                (By.XPATH, "//button[@type='submit']"),
                (By.XPATH, "//input[@type='submit']"),
                (By.XPATH, "//button[contains(text(),'Sign In') or contains(text(),'Log In') or contains(text(),'Login')]"),
            ]:
                try:
                    btn = self.driver.find_element(*sel)
                    if btn.is_displayed():
                        btn.click()
                        submitted = True
                        break
                except Exception:
                    continue

            if not submitted:
                pw_field.send_keys(Keys.RETURN)

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

            try:
                WebDriverWait(self.driver, 20).until(
                    lambda d: any(x in d.find_element(By.TAG_NAME, "body").text
                                  for x in ["Verify Patient Eligibility", "Patient Management",
                                            "Submit a Claim", "Claim Inquiry", "Submit Claims"]))
                print("[CCA PreAuth login] Login succeeded")
                return "SUCCESS"
            except TimeoutException:
                pass

            body_text = self.driver.find_element(By.TAG_NAME, "body").text
            if "invalid" in body_text.lower():
                return "ERROR: Invalid username or password"
            return "ERROR: Login did not succeed — portal content not found after submit"

        except Exception as e:
            print(f"[CCA PreAuth login] Exception: {e}")
            return f"ERROR:LOGIN FAILED: {e}"

    # ------------------------------------------------------------------ #
    #  Step 1 — Navigate via menu: Authorizations → Submit Authorizations  #
    # ------------------------------------------------------------------ #
    def step1_navigate_to_auth_entry(self):
        try:
            # Click the "Authorizations" dropdown in the navbar
            print("[CCA PreAuth step1] Clicking Authorizations menu...")
            auth_link = WebDriverWait(self.driver, 15).until(
                EC.element_to_be_clickable((By.ID, "linkAuths"))
            )
            auth_link.click()
            time.sleep(1.0)
            print("[CCA PreAuth step1] Authorizations dropdown opened")

            # Click "Submit Authorizations" — first item in the dropdown
            print("[CCA PreAuth step1] Clicking Submit Authorizations...")
            for sel in [
                (By.XPATH, "//span[contains(text(),'Submit Authorizations')]"),
                (By.XPATH, "//a[contains(text(),'Submit Authorizations')]"),
                (By.XPATH, "//ul[contains(@class,'dropdown-menu')]//a[contains(.,'Submit Authorizations')]"),
            ]:
                try:
                    item = WebDriverWait(self.driver, 8).until(EC.element_to_be_clickable(sel))
                    item.click()
                    print(f"[CCA PreAuth step1] Clicked Submit Authorizations via {sel}")
                    break
                except Exception:
                    continue

            # Wait for the Authorization Entry page to load
            try:
                WebDriverWait(self.driver, 20).until(
                    EC.presence_of_element_located((By.ID, "tbSubscriberId"))
                )
                print(f"[CCA PreAuth step1] Authorization Entry page loaded — URL: {self.driver.current_url}")
            except TimeoutException:
                current_url = self.driver.current_url
                print(f"[CCA PreAuth step1] tbSubscriberId not found, URL: {current_url}")
                if "AuthEntry" in current_url:
                    print("[CCA PreAuth step1] URL confirms AuthEntry — continuing")
                elif "login" in current_url.lower():
                    return "ERROR: Session expired — redirected to login"
                else:
                    print("[CCA PreAuth step1] Continuing despite timeout")

            time.sleep(1)
            return "SUCCESS"

        except Exception as e:
            print(f"[CCA PreAuth step1] Exception: {e}")
            return f"ERROR: step1 failed: {e}"

    def _set_ng_value(self, field, value: str):
        try:
            self.driver.execute_script(
                """
                var el = arguments[0], val = arguments[1];
                var setter = Object.getOwnPropertyDescriptor(
                    window.HTMLInputElement.prototype, 'value').set;
                setter.call(el, val);
                el.dispatchEvent(new Event('input',  {bubbles:true}));
                el.dispatchEvent(new Event('change', {bubbles:true}));
                """,
                field, value
            )
        except Exception:
            field.click()
            field.send_keys(Keys.CONTROL + "a")
            field.send_keys(Keys.DELETE)
            field.send_keys(value)

    # ------------------------------------------------------------------ #
    #  Step 2 — Fill Patient/Provider info, Verify Eligibility             #
    # ------------------------------------------------------------------ #
    def _js_set(self, element_id: str, value: str) -> bool:
        try:
            el = self.driver.find_element(By.ID, element_id)
            self.driver.execute_script(
                "var el=arguments[0], v=arguments[1];"
                "var s=Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set;"
                "s.call(el,v);"
                "el.dispatchEvent(new Event('input',{bubbles:true}));"
                "el.dispatchEvent(new Event('change',{bubbles:true}));",
                el, value
            )
            actual = el.get_attribute("value")
            print(f"[CCA PreAuth step2] JS set {element_id!r} = {actual!r}")
            return True
        except Exception as e:
            print(f"[CCA PreAuth step2] JS set {element_id!r} failed: {e}")
            return False

    def _keys_set(self, element_id: str, value: str) -> bool:
        try:
            el = self.driver.find_element(By.ID, element_id)
            el.click()
            el.send_keys(Keys.CONTROL + "a")
            el.send_keys(Keys.DELETE)
            el.send_keys(value)
            time.sleep(0.3)
            actual = el.get_attribute("value")
            print(f"[CCA PreAuth step2] keys set {element_id!r} = {actual!r}")
            return True
        except Exception as e:
            print(f"[CCA PreAuth step2] keys set {element_id!r} failed: {e}")
            return False

    def step2_fill_patient_eligibility(self):
        """
        Fill Subscriber ID, Date of Birth, Tentative Service Date then click Verify Eligibility.
        Returns 'SUCCESS' or 'ERROR:...'.
        """
        try:
            formatted_dob = self._format_dob(self.dateOfBirth)
            formatted_dos = self._format_dob(self.serviceDate) if self.serviceDate else ""
            print(f"[CCA PreAuth step2] memberId={self.memberId!r}, DOB={formatted_dob!r}, "
                  f"TentativeDOS={formatted_dos!r}")

            print("[CCA PreAuth step2] Waiting for tbSubscriberId in DOM...")
            try:
                WebDriverWait(self.driver, 20).until(
                    EC.presence_of_element_located((By.ID, "tbSubscriberId"))
                )
                print("[CCA PreAuth step2] tbSubscriberId present in DOM")
            except Exception:
                print("[CCA PreAuth step2] tbSubscriberId not in DOM after 20s")
                return "ERROR: Subscriber ID field not found in DOM"

            time.sleep(1)

            if not self._js_set("tbSubscriberId", self.memberId):
                return "ERROR: Could not set Subscriber ID"

            if not self._keys_set("tbDateOfBirth", formatted_dob):
                return "ERROR: Could not set Date of Birth"

            # Tentative Service Date — same field as DOS on the portal
            if formatted_dos:
                self._keys_set("tbDateOfService", formatted_dos)

            print("[CCA PreAuth step2] Clicking 'Verify Eligibility'...")
            verified = False
            for sel in [
                (By.ID, "ButtonVerifyMemberEligibility"),
                (By.XPATH, "//button[@id='ButtonVerifyMemberEligibility']"),
                (By.XPATH, "//button[contains(text(),'Verify Eligibility')]"),
            ]:
                try:
                    btn = WebDriverWait(self.driver, 8).until(EC.element_to_be_clickable(sel))
                    btn.click()
                    verified = True
                    print(f"[CCA PreAuth step2] Clicked 'Verify Eligibility' via {sel}")
                    break
                except Exception:
                    continue

            if not verified:
                return "ERROR: 'Verify Eligibility' button not found"

            print("[CCA PreAuth step2] Waiting for eligibility result...")
            try:
                WebDriverWait(self.driver, 25).until(
                    lambda d: any(x in d.find_element(By.TAG_NAME, "body").text.lower() for x in [
                        "eligible", "not eligible", "ineligible",
                        "patient name", "member name", "verified",
                    ])
                )
                print("[CCA PreAuth step2] Eligibility result appeared")
            except TimeoutException:
                print(f"[CCA PreAuth step2] Timed out waiting for eligibility result — "
                      f"URL: {self.driver.current_url}")

            time.sleep(1)
            return "SUCCESS"

        except Exception as e:
            print(f"[CCA PreAuth step2] Exception: {e}")
            return f"ERROR: step2 failed: {e}"

    # ------------------------------------------------------------------ #
    #  Step 4 — Extract auth number + capture confirmation PDF             #
    # ------------------------------------------------------------------ #
    def extract_auth_number(self):
        try:
            WebDriverWait(self.driver, 15).until(
                lambda d: d.execute_script("return document.readyState") == "complete"
            )
            time.sleep(1)

            body_text = self.driver.find_element(By.TAG_NAME, "body").text
            print(f"[CCA PreAuth step4] Confirmation page text (first 600): {body_text[:600]}")

            for sel in [
                (By.XPATH, "//*[contains(text(),'Auth Number') or contains(text(),'Authorization Number')]/following-sibling::*[1]"),
                (By.XPATH, "//*[contains(text(),'Auth Number') or contains(text(),'Authorization Number')]/following::*[1]"),
                (By.XPATH, "//*[contains(text(),'Claim Number') or contains(text(),'claim number')]/following-sibling::*[1]"),
                (By.XPATH, "//*[contains(text(),'Reference Number') or contains(text(),'Confirmation Number')]/following-sibling::*[1]"),
                (By.XPATH, "//*[contains(text(),'assigned the number')]/following::*[1]"),
                (By.XPATH, "//*[@id and (contains(@id,'AuthNumber') or contains(@id,'ClaimNumber') or contains(@id,'ConfirmationNumber'))]"),
            ]:
                try:
                    el = self.driver.find_element(*sel)
                    text = el.text.strip()
                    if text and re.search(r'\d{6,}', text):
                        print(f"[CCA PreAuth step4] Auth number via DOM: {text}")
                        return re.search(r'[\w\-]{6,}', text).group(0)
                except Exception:
                    continue

            for pattern in [
                r'assigned\s+the\s+number\s+([\w\-]{6,30})',
                r'[Aa]uth(?:orization)?\s+[Nn]umber[:\s]+([A-Z0-9\-]{6,30})',
                r'[Cc]laim\s+[Nn]umber[:\s]+([A-Z0-9\-]{6,30})',
                r'[Cc]onfirmation\s+[Nn]umber[:\s]+([A-Z0-9\-]{6,30})',
                r'[Rr]eference\s+[Nn]umber[:\s]+([A-Z0-9\-]{6,30})',
                r'(\d{15})',
                r'(\d{9,14})',
            ]:
                m = re.search(pattern, body_text)
                if m:
                    print(f"[CCA PreAuth step4] Auth number via regex ({pattern}): {m.group(1)}")
                    return m.group(1)

            auth_number = self.driver.execute_script(r"""
                var els = document.querySelectorAll('body, p, div, span, td, label, h1, h2, h3, strong, b');
                for (var i = 0; i < els.length; i++) {
                    var t = (els[i].textContent || '').trim();
                    var m = t.match(/assigned\s+the\s+number\s+([\w\-]{6,30})/i)
                          || t.match(/[Aa]uth(?:orization)?\s+[Nn]umber[:\s]+([\w\-]{6,30})/)
                          || t.match(/[Cc]laim\s+[Nn]umber[:\s]+([\w\-]{6,30})/)
                          || t.match(/(\d{15})/)
                          || t.match(/(\d{9,14})/);
                    if (m) return m[1];
                }
                return null;
            """)
            if auth_number:
                print(f"[CCA PreAuth step4] Auth number via JS: {auth_number}")
                return auth_number

            print("[CCA PreAuth step4] Could not extract auth number")
            return None
        except Exception as e:
            print(f"[CCA PreAuth step4] extract_auth_number exception: {e}")
            return None

    def step4_capture_confirmation(self):
        """
        Return the auth number and screenshot captured from the success popup in step3.
        Returns dict: { authNumber, pdfBase64, pdfFilename }.
        """
        auth_number = getattr(self, "_popup_auth_number", None)
        pdf_base64 = getattr(self, "_popup_pdf_base64", "")
        pdf_filename = getattr(self, "_popup_pdf_filename", "")

        if auth_number:
            print(f"[CCA PreAuth step4] Using popup data — authNumber={auth_number}, "
                  f"screenshotLen={len(pdf_base64)}")
        else:
            print("[CCA PreAuth step4] No popup data — attempting page screenshot fallback")
            try:
                safe_member = "".join(c for c in str(self.memberId) if c.isalnum() or c in "-_.")
                timestamp = time.strftime("%Y%m%d_%H%M%S")
                pdf_filename = f"cca_preauth_{safe_member}_{timestamp}.png"
                total_height = self.driver.execute_script("return document.body.scrollHeight")
                self.driver.set_window_size(1280, max(total_height, 900))
                time.sleep(0.5)
                pdf_base64 = self.driver.get_screenshot_as_base64()
                auth_number = self.extract_auth_number()
                print(f"[CCA PreAuth step4] Fallback screenshot captured, authNumber={auth_number}")
            except Exception as e:
                print(f"[CCA PreAuth step4] Fallback screenshot failed: {e}")

        return {
            "authNumber": auth_number,
            "pdfBase64": pdf_base64,
            "pdfFilename": pdf_filename,
        }

    def _format_dob(self, dob_str):
        if not dob_str:
            return dob_str
        s = str(dob_str).strip()
        if re.match(r'^\d{1,2}/\d{1,2}/\d{4}$', s):
            return s
        if "-" in s:
            parts = s.split("-")
            if len(parts) == 3:
                if len(parts[0]) == 4:
                    return f"{parts[1]}/{parts[2]}/{parts[0]}"
                if len(parts[2]) == 4:
                    return f"{parts[0]}/{parts[1]}/{parts[2]}"
        return s

    # ------------------------------------------------------------------ #
    #  Fee schedule helpers (same as CCA Claim)                            #
    # ------------------------------------------------------------------ #
    def _load_cca_fee_schedule(self):
        base = os.path.dirname(os.path.abspath(__file__))
        json_path = os.path.join(
            base, "..", "Frontend", "src", "assets", "data", "procedureCodesCCA.json"
        )
        try:
            with open(json_path) as f:
                rows = json.load(f)
            fee_map = {}
            for row in rows:
                code = str(row.get("Procedure Code", "")).strip().upper()
                if code:
                    fee_map[code] = row
            print(f"[CCA PreAuth] Loaded {len(fee_map)} CCA fee codes")
            return fee_map
        except Exception as e:
            print(f"[CCA PreAuth] Could not load CCA fee schedule: {e}")
            return {}

    def _get_patient_age(self):
        if not self.dateOfBirth:
            return None
        try:
            parts = self.dateOfBirth.split("-")
            dob = date(int(parts[0]), int(parts[1]), int(parts[2]))
            today = date.today()
            return today.year - dob.year - ((today.month, today.day) < (dob.month, dob.day))
        except Exception:
            return None

    def _get_fee(self, code, fee_map, age):
        row = fee_map.get(str(code).strip().upper(), {})
        if not row:
            return ""
        if "Price" in row:
            val = str(row["Price"])
        elif age is not None and age <= 21:
            val = str(row.get("PriceLTEQ21") or row.get("PriceGT21") or "")
        else:
            val = str(row.get("PriceGT21") or row.get("PriceLTEQ21") or "")
        return "" if val in ("NC", "IC", "None", "") else val

    # ------------------------------------------------------------------ #
    #  EJ Grid helpers (same as CCA Claim)                                 #
    # ------------------------------------------------------------------ #
    def _build_col_map(self):
        # AuthEntry column layout (from portal PDF):
        # 1=RowNum, 2=Code, 3=Desc, 4=Tooth,
        # 5-9=Surf1-5, 10=OralCavity(1 col dropdown), 11-14=DiagPtr1-4,
        # 15=DurationVal, 16=DurationUnit, 17=FreqVal, 18=FreqUnit,
        # 19=Qty, 20=POS, 21=TentativeFrom, 22=TentativeTo, 23=BilledAmt
        col_map = {
            "CODE": 2,
            "TOOTH": 4,
            "SURF1": 5, "S1": 5,
            "SURF2": 6, "S2": 6,
            "SURF3": 7, "S3": 7,
            "SURF4": 8, "S4": 8,
            "SURF5": 9, "S5": 9,
            "QTY": 19,
            "POS": 20,
            "TENTATIVE_FROM": 21,
            "TENTATIVE_TO": 22,
            "BILLED_AMOUNT": 23,
        }
        return col_map

    def _fill_active_input(self, input_id, value):
        try:
            field = WebDriverWait(self.driver, 5).until(
                EC.presence_of_element_located((By.ID, input_id))
            )
            self.driver.execute_script(
                "arguments[0].scrollIntoView({block:'nearest',inline:'nearest'});", field)
            field.click()
            field.send_keys(Keys.CONTROL + "a")
            field.send_keys(Keys.DELETE)
            if value:
                field.send_keys(str(value))
            time.sleep(0.2)
            print(f"[CCA PreAuth grid] Filled {input_id}={value!r}")
            return True
        except Exception as e:
            print(f"[CCA PreAuth grid] Could not fill {input_id}: {e}")
            return False

    def _click_cell_and_keyboard_type(self, row_num, col_expr, value):
        """
        For cells that do not accept paste (Tentative From, Billed Amount):
        click the cell to activate it, then type each character individually
        into whatever element is focused — no ID lookup, no CTRL+A paste.
        """
        from selenium.webdriver.common.action_chains import ActionChains
        cell = None
        for xpath in [
            f"(//div[contains(@class,'e-gridcontent')]//tr)[{row_num}]/td[{col_expr}]",
            f"(//div[contains(@class,'e-content')]//tr)[{row_num}]/td[{col_expr}]",
            f"(//div[@id='Services']//div[contains(@class,'e-content')]//tr)[{row_num}]/td[{col_expr}]",
            f"(//tr[@aria-rowindex='{row_num-1}'])/td[{col_expr}]",
        ]:
            try:
                cell = self.driver.find_element(By.XPATH, xpath)
                break
            except Exception:
                continue

        if not cell:
            print(f"[CCA PreAuth grid] keyboard_type: cell not found row={row_num} col={col_expr}")
            return False

        try:
            self.driver.execute_script(
                "arguments[0].scrollIntoView({block:'nearest',inline:'nearest'});", cell)
            cell.click()
            time.sleep(0.4)
            # Clear any existing content then type character by character
            active = self.driver.switch_to.active_element
            active.send_keys(Keys.CONTROL + "a")
            active.send_keys(Keys.DELETE)
            time.sleep(0.1)
            for ch in str(value):
                active.send_keys(ch)
                time.sleep(0.05)
            print(f"[CCA PreAuth grid] keyboard_type row={row_num} col={col_expr} value={value!r}")
            return True
        except Exception as e:
            print(f"[CCA PreAuth grid] keyboard_type failed row={row_num} col={col_expr}: {e}")
            return False

    # ------------------------------------------------------------------ #
    #  Step 3 — Fill Services grid, attach docs, submit authorization      #
    # ------------------------------------------------------------------ #
    def step3_fill_services_and_submit(self):
        """
        Fill the services grid and submit the authorization request.
        Same structure as CCA Claim; submit button selectors cover both
        'Submit Claim' and 'Submit Authorization' variants.
        """
        try:
            fee_map = self._load_cca_fee_schedule()
            age = self._get_patient_age()
            formatted_dos = self._format_dob(self.serviceDate) if self.serviceDate else ""

            active_lines = [
                ln for ln in self.serviceLines
                if str(ln.get("procedureCode") or "").strip()
            ]
            print(f"[CCA PreAuth step3] {len(active_lines)} active service line(s)")

            # Date as MMDDYYYY (no slashes) — required format for DOS_FROM input
            date_no_slash = formatted_dos.replace("/", "") if formatted_dos else ""

            for idx, line in enumerate(active_lines):
                code = str(line.get("procedureCode") or "").strip()
                tooth = str(line.get("toothNumber") or "").strip()
                total_billed = line.get("totalBilled") or line.get("fee") or line.get("billedAmount")
                billed_str = str(total_billed) if total_billed else self._get_fee(code, fee_map, age)
                row_idx = idx  # 0-based aria-rowindex

                print(f"[CCA PreAuth step3] Line {idx+1}: code={code}, tooth={tooth}, billed={billed_str}")

                # Find the row element scoped to Services grid
                row_el = None
                for xpath in [
                    f"//div[@id='Services']//tr[@aria-rowindex='{row_idx}']",
                    f"//div[@id='Services']//div[contains(@class,'e-gridcontent')]//tbody/tr[{idx+1}]",
                    f"//div[@id='Services']//div[contains(@class,'e-content')]//tr[{idx+1}]",
                ]:
                    try:
                        row_el = self.driver.find_element(By.XPATH, xpath)
                        break
                    except Exception:
                        continue

                if not row_el:
                    print(f"[CCA PreAuth step3] WARNING: row {idx+1} not found — skipping")
                    continue

                # ── Code: click td[2] to activate → find ServicesCODE by ID globally ──
                code_filled = False
                for xpath in [
                    f"//div[@id='Services']//tr[@aria-rowindex='{row_idx}']/td[2]",
                    f"//div[@id='Services']//div[contains(@class,'e-gridcontent')]//tbody/tr[{idx+1}]/td[2]",
                    f"//div[@id='Services']//div[contains(@class,'e-content')]//tr[{idx+1}]/td[2]",
                ]:
                    try:
                        code_cell = self.driver.find_element(By.XPATH, xpath)
                        self.driver.execute_script(
                            "arguments[0].scrollIntoView({block:'nearest',inline:'nearest'});", code_cell)
                        code_cell.click()
                        time.sleep(0.5)
                        code_input = WebDriverWait(self.driver, 5).until(
                            EC.presence_of_element_located((By.ID, "ServicesCODE")))
                        code_input.send_keys(code)
                        time.sleep(1.5)
                        print(f"[CCA PreAuth step3] Row {idx+1}: code={code!r} entered")
                        code_filled = True
                        break
                    except Exception:
                        continue

                if not code_filled:
                    print(f"[CCA PreAuth step3] Row {idx+1}: Code fill failed — skipping row")
                    continue

                # ── Tooth: click td[4] in Services grid → wait for input → clear → type ──
                if tooth:
                    try:
                        from selenium.webdriver.common.action_chains import ActionChains
                        tooth_cell = None
                        tooth_cell_xpaths = [
                            f"//div[@id='Services']//tr[@aria-rowindex='{row_idx}']/td[4]",
                            f"//div[@id='Services']//div[contains(@class,'e-gridcontent')]//tbody/tr[{idx+1}]/td[4]",
                            f"//div[@id='Services']//div[contains(@class,'e-content')]//tr[{idx+1}]/td[4]",
                        ]
                        # Re-find cell fresh each attempt to avoid StaleElement
                        for xpath in tooth_cell_xpaths:
                            try:
                                tooth_cell = self.driver.find_element(By.XPATH, xpath)
                                break
                            except Exception:
                                continue

                        if tooth_cell:
                            self.driver.execute_script(
                                "arguments[0].scrollIntoView({block:'nearest',inline:'nearest'});", tooth_cell)
                            tooth_cell.click()
                            time.sleep(0.3)

                            # Wait until an input becomes active/focused inside the cell
                            tooth_input = None
                            for _ in range(10):
                                active = self.driver.switch_to.active_element
                                tag = active.tag_name.lower() if active else ""
                                if tag == "input":
                                    tooth_input = active
                                    break
                                # Try clicking again to trigger edit mode
                                try:
                                    tooth_cell = self.driver.find_element(By.XPATH, tooth_cell_xpaths[0])
                                except Exception:
                                    pass
                                try:
                                    tooth_cell.click()
                                except Exception:
                                    pass
                                time.sleep(0.2)

                            if tooth_input:
                                # Clear → Type → Verify (with JS fallback)
                                tooth_input.click()
                                tooth_input.send_keys(Keys.CONTROL + "a")
                                tooth_input.send_keys(Keys.DELETE)
                                time.sleep(0.1)
                                for ch in str(tooth):
                                    tooth_input.send_keys(ch)
                                    time.sleep(0.05)
                                time.sleep(0.2)
                                # Verify value was entered; fall back to JS inject if empty
                                entered = tooth_input.get_attribute("value") or ""
                                if entered.strip() == "":
                                    self.driver.execute_script(
                                        "arguments[0].value = arguments[1];"
                                        "arguments[0].dispatchEvent(new Event('input',{bubbles:true}));"
                                        "arguments[0].dispatchEvent(new Event('change',{bubbles:true}));",
                                        tooth_input, str(tooth))
                                    time.sleep(0.2)
                                print(f"[CCA PreAuth step3] Row {idx+1}: tooth={tooth!r} entered")
                            else:
                                # Fallback: JS inject directly into active element
                                active = self.driver.switch_to.active_element
                                self.driver.execute_script(
                                    "arguments[0].value = arguments[1];"
                                    "arguments[0].dispatchEvent(new Event('input',{bubbles:true}));"
                                    "arguments[0].dispatchEvent(new Event('change',{bubbles:true}));",
                                    active, str(tooth))
                                time.sleep(0.2)
                                print(f"[CCA PreAuth step3] Row {idx+1}: tooth={tooth!r} entered via JS fallback")
                        else:
                            print(f"[CCA PreAuth step3] Row {idx+1}: Tooth cell not found — skipping")
                    except Exception as e:
                        print(f"[CCA PreAuth step3] Row {idx+1}: Tooth fill failed: {e} — skipping tooth")

                # ── Billed Amount: click TD cell to activate edit mode (turn yellow),
                #    wait for input, delete default 0.00, type amount ──
                if billed_str:
                    try:
                        billed_cell_xpaths = [
                            f"//div[@id='Services']//tr[@aria-rowindex='{row_idx}']/td[23]",
                            f"//div[@id='Services']//div[contains(@class,'e-gridcontent')]//tbody/tr[{idx+1}]/td[23]",
                            f"//div[@id='Services']//div[contains(@class,'e-content')]//tr[{idx+1}]/td[23]",
                        ]
                        billed_cell = None
                        for xpath in billed_cell_xpaths:
                            try:
                                billed_cell = self.driver.find_element(By.XPATH, xpath)
                                break
                            except Exception:
                                continue

                        if billed_cell:
                            self.driver.execute_script(
                                "arguments[0].scrollIntoView({block:'nearest',inline:'nearest'});", billed_cell)
                            billed_cell.click()
                            time.sleep(0.3)

                            # Wait until the input activates (cell turns yellow = edit mode)
                            billed_input = None
                            for _ in range(10):
                                try:
                                    billed_input = WebDriverWait(self.driver, 1).until(
                                        EC.element_to_be_clickable((By.ID, "ServicesBILLED_AMOUNT")))
                                    break
                                except Exception:
                                    pass
                                try:
                                    billed_cell = self.driver.find_element(By.XPATH, billed_cell_xpaths[0])
                                    billed_cell.click()
                                except Exception:
                                    pass
                                time.sleep(0.2)

                            if billed_input:
                                billed_input.send_keys(Keys.DELETE)
                                time.sleep(0.3)
                                for ch in str(billed_str):
                                    billed_input.send_keys(ch)
                                    time.sleep(0.05)
                                time.sleep(0.2)
                                # Verify; fallback to JS if empty
                                entered = billed_input.get_attribute("value") or ""
                                if entered.strip() == "":
                                    self.driver.execute_script(
                                        "arguments[0].value = arguments[1];"
                                        "arguments[0].dispatchEvent(new Event('input',{bubbles:true}));"
                                        "arguments[0].dispatchEvent(new Event('change',{bubbles:true}));",
                                        billed_input, str(billed_str))
                                    time.sleep(0.2)
                                print(f"[CCA PreAuth step3] Row {idx+1}: billed={billed_str!r} entered")
                            else:
                                print(f"[CCA PreAuth step3] Row {idx+1}: Billed Amount input not activated — skipping")
                        else:
                            print(f"[CCA PreAuth step3] Row {idx+1}: Billed Amount cell not found — skipping")
                    except Exception as e:
                        print(f"[CCA PreAuth step3] Row {idx+1}: Billed Amount fill failed: {e}")

                # ── Commit row before next row or next section ────────────────────
                time.sleep(1.0)
                try:
                    self.driver.find_element(By.TAG_NAME, "body").click()
                    time.sleep(1.0)
                except Exception:
                    pass

            # ---- Attached Documents ----
            if self.uploadFiles:
                print(f"[CCA PreAuth step3] Attaching {len(self.uploadFiles)} file(s)...")

                for sel in [
                    (By.XPATH, "//h4[contains(@class,'panel-title') and contains(.,'Attached Documents')]"),
                    (By.XPATH, "//*[contains(@class,'panel-title') and contains(.,'Attached Documents')]"),
                ]:
                    try:
                        el = WebDriverWait(self.driver, 6).until(EC.element_to_be_clickable(sel))
                        try:
                            chevron = el.find_element(By.XPATH, ".//*[contains(@class,'fa-chevron-down')]")
                            if chevron.is_displayed():
                                el.click()
                                time.sleep(0.8)
                        except Exception:
                            pass
                        break
                    except Exception:
                        continue

                tmp_dir = tempfile.mkdtemp()
                temp_paths = []
                for f in self.uploadFiles:
                    try:
                        original_name = f.get("originalname", "attachment.pdf")
                        safe_name = re.sub(r'[^\w.\-]', '_', original_name)
                        tmp_path = os.path.join(tmp_dir, safe_name)
                        with open(tmp_path, "wb") as fh:
                            fh.write(base64.b64decode(f["bufferBase64"]))
                        temp_paths.append(tmp_path)
                    except Exception as fe:
                        print(f"[CCA PreAuth step3] Failed to write attachment: {fe}")

                if temp_paths:
                    file_input_found = False
                    for sel in [
                        (By.XPATH, "//input[@type='file']"),
                        (By.XPATH, "//button[@id='AttachDocumentButton']/preceding::input[@type='file'][1]"),
                        (By.XPATH, "//button[@id='AttachDocumentButton']/following::input[@type='file'][1]"),
                    ]:
                        try:
                            file_input = self.driver.find_element(*sel)
                            self.driver.execute_script(
                                "arguments[0].style.display='block'; "
                                "arguments[0].style.visibility='visible'; "
                                "arguments[0].style.opacity='1';",
                                file_input
                            )
                            file_input.send_keys("\n".join(temp_paths))
                            file_input_found = True
                            break
                        except Exception:
                            continue

                    if not file_input_found:
                        try:
                            btn = WebDriverWait(self.driver, 6).until(
                                EC.element_to_be_clickable((By.ID, "AttachDocumentButton")))
                            btn.click()
                        except Exception:
                            print("[CCA PreAuth step3] AttachDocumentButton not found")

                    try:
                        WebDriverWait(self.driver, 30).until(
                            lambda d: any(
                                re.sub(r'[^\w.\-]', '_', f.get("originalname", ""))[:10].lower()
                                in d.find_element(By.TAG_NAME, "body").text.lower()
                                for f in self.uploadFiles
                            ) or "Attached Documents (1)" in d.find_element(By.TAG_NAME, "body").text
                        )
                        print("[CCA PreAuth step3] Attachment confirmed on page")
                    except TimeoutException:
                        print("[CCA PreAuth step3] Attachment upload wait timed out — proceeding")

                    try:
                        import shutil
                        shutil.rmtree(tmp_dir, ignore_errors=True)
                    except Exception:
                        pass

            # Give the page a moment to settle after grid edits / attachments
            time.sleep(2.0)

            # ---- Submit Authorization ----
            print("[CCA PreAuth step3] Clicking submit button...")
            submitted = False
            for sel in [
                (By.ID, "ContinueToGuidelineButton"),
                (By.XPATH, "//button[@id='ContinueToGuidelineButton']"),
                (By.XPATH, "//button[contains(text(),'Review Requirements and Submit')]"),
                (By.XPATH, "//button[@ng-click and contains(@ng-click,'ContinueToGuidelineOnClick')]"),
                (By.ID, "SaveAuthButton"),
                (By.ID, "SaveClaimButton"),
                (By.XPATH, "//button[@ng-click and (contains(@ng-click,'SubmitAuth') or contains(@ng-click,'SubmitClaim'))]"),
                (By.XPATH, "//button[contains(text(),'Submit Authorization') or contains(text(),'Submit Auth')]"),
                (By.XPATH, "//button[contains(text(),'Submit Claim')]"),
            ]:
                try:
                    btn = WebDriverWait(self.driver, 8).until(EC.element_to_be_clickable(sel))
                    btn.click()
                    submitted = True
                    print(f"[CCA PreAuth step3] Clicked submit via {sel}")
                    break
                except Exception:
                    continue

            if not submitted:
                return "ERROR: Submit button not found or not clickable"

            # ---- Modal 1: "Review Requirements" → click Submit ----
            print("[CCA PreAuth step3] Waiting for review-requirements modal...")
            time.sleep(1.5)
            for sel in [
                (By.XPATH, "//div[contains(@class,'modal') and contains(@class,'in')]//button[contains(text(),'Submit')]"),
                (By.XPATH, "//div[@class='modal-footer']//button[contains(text(),'Submit')]"),
                (By.XPATH, "//button[@ng-click and contains(@ng-click,'Submit')]"),
                (By.XPATH, "//div[contains(@class,'modal')]//button[contains(text(),'Submit')]"),
            ]:
                try:
                    btn = WebDriverWait(self.driver, 8).until(EC.element_to_be_clickable(sel))
                    btn.click()
                    print(f"[CCA PreAuth step3] Clicked review modal Submit via {sel}")
                    break
                except Exception:
                    continue

            # ---- Modal 2: "Are you sure to submit?" → click OK ----
            print("[CCA PreAuth step3] Waiting for confirmation modal (OK/Cancel)...")
            time.sleep(1.5)
            for sel in [
                (By.XPATH, "//div[contains(@class,'modal') and contains(@class,'in')]//button[contains(text(),'OK') or contains(text(),'Ok')]"),
                (By.XPATH, "//div[@class='modal-footer']//button[contains(text(),'OK') or contains(text(),'Ok')]"),
                (By.XPATH, "//div[contains(@class,'modal')]//button[contains(text(),'OK') or contains(text(),'Ok')]"),
                (By.XPATH, "//button[contains(text(),'OK') or contains(text(),'Ok')][not(contains(text(),'Cancel'))]"),
            ]:
                try:
                    btn = WebDriverWait(self.driver, 8).until(EC.element_to_be_clickable(sel))
                    btn.click()
                    print(f"[CCA PreAuth step3] Clicked confirmation OK via {sel}")
                    break
                except Exception:
                    continue

            # ---- Modal 3: "Authorization AXXXXXX has been successfully submitted" ----
            print("[CCA PreAuth step3] Waiting for success popup with auth number...")
            self._popup_auth_number = None
            self._popup_pdf_base64 = ""
            self._popup_pdf_filename = ""

            try:
                # Wait up to 30s for the success message popup
                WebDriverWait(self.driver, 30).until(
                    lambda d: any(x in d.find_element(By.TAG_NAME, "body").text.lower() for x in [
                        "successfully submitted", "has been submitted",
                        "authorization a", "auth number",
                    ])
                )
                print("[CCA PreAuth step3] Success popup detected")
            except TimeoutException:
                print("[CCA PreAuth step3] Success popup not detected within 30s — proceeding")

            time.sleep(0.5)

            # Extract auth number from popup text
            try:
                body_text = self.driver.find_element(By.TAG_NAME, "body").text
                # Match "Authorization A026XXXX" or any auth number pattern
                for pattern in [
                    r'[Aa]uthorization\s+([A-Z0-9]{6,20})\s+has been',
                    r'[Aa]uthorization\s+([A-Z0-9]{6,20})',
                    r'[Aa]uth(?:orization)?\s+[Nn]umber[:\s]+([A-Z0-9\-]{6,20})',
                    r'\b([A-Z]\d{6,12})\b',
                ]:
                    m = re.search(pattern, body_text)
                    if m:
                        self._popup_auth_number = m.group(1)
                        print(f"[CCA PreAuth step3] Auth number extracted: {self._popup_auth_number}")
                        break
            except Exception as e:
                print(f"[CCA PreAuth step3] Auth number extraction failed: {e}")

            # Screenshot the whole page with popup visible — save as PNG (base64)
            try:
                safe_member = "".join(c for c in str(self.memberId) if c.isalnum() or c in "-_.")
                safe_auth = ("_" + re.sub(r'[^\w\-]', '', str(self._popup_auth_number))[:20]) if self._popup_auth_number else ""
                timestamp = time.strftime("%Y%m%d_%H%M%S")
                self._popup_pdf_filename = f"cca_preauth_{safe_member}{safe_auth}_{timestamp}.png"

                # Expand window to capture full page + popup
                total_height = self.driver.execute_script("return document.body.scrollHeight")
                self.driver.set_window_size(1280, max(total_height, 900))
                time.sleep(0.3)
                self._popup_pdf_base64 = self.driver.get_screenshot_as_base64()
                print(f"[CCA PreAuth step3] Success popup screenshot captured ({len(self._popup_pdf_base64)} b64 chars)")
            except Exception as e:
                print(f"[CCA PreAuth step3] Screenshot failed: {e}")

            # Click OK to dismiss the success popup
            for sel in [
                (By.XPATH, "//div[contains(@class,'modal') and contains(@class,'in')]//button[contains(text(),'OK') or contains(text(),'Ok')]"),
                (By.XPATH, "//div[@class='modal-footer']//button[contains(text(),'OK') or contains(text(),'Ok')]"),
                (By.XPATH, "//div[contains(@class,'modal')]//button[contains(text(),'OK') or contains(text(),'Ok')]"),
                (By.XPATH, "//button[contains(text(),'OK') or contains(text(),'Ok')]"),
            ]:
                try:
                    btn = WebDriverWait(self.driver, 5).until(EC.element_to_be_clickable(sel))
                    btn.click()
                    print("[CCA PreAuth step3] Dismissed success popup (OK)")
                    break
                except Exception:
                    continue

            time.sleep(1)
            return "SUCCESS"

        except Exception as e:
            print(f"[CCA PreAuth step3] Exception: {e}")
            return f"ERROR: step3 failed: {e}"
