#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Damco (APM) Tracking for Incentive - Maersk Portal Automation (Electron WebContents)
Author: Izaz Ahamed
------------------------------------------------------------
✅ Electron WebContents-driven automation
✅ PDF export per FCR
✅ Compatible with Commercial RPA architecture
"""

import os, sys, time, logging, base64, tempfile, json
import pandas as pd
from datetime import datetime

# Use Electron browser instead of Selenium
try:
    from shared.electron_browser import ElectronBrowser, By, ElectronWait, EC
except ImportError:
    # Fallback for direct execution
    import sys
    import os
    sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
    from automation_scripts.shared.electron_browser import ElectronBrowser, By, ElectronWait, EC

# Compatibility exceptions
class TimeoutException(Exception):
    pass

try:
    import requests
    from urllib.parse import urlparse, parse_qs
except ImportError:
    requests = None
    print("⚠️ Warning: 'requests' library not found. Server communication will be disabled.")
    print("   Install with: pip install requests")


# -------------------------------------------------------------
# Damco / Maersk helper utilities
# -------------------------------------------------------------

def resolve_damco_fcr_id(fcr_number: str, logger: logging.Logger = None) -> str:
    """
    Resolve the dynamic Damco FCR detail ID for a given FCR/booking number.
    
    Logic (based on observed behaviour of:
      - `https://reporting.damco.com/Reporting/spot?ref=/search&search=<FCR>`
      - `https://reporting.damco.com/Reporting/spot?ref=/fcr&id=<ID>&txt=<FCR>`
    
    1. Call the /search URL without following redirects.
       - If we get a 3xx, extract Location header and parse the `id` query param.
    2. If we get 200 OK (search result page), parse the HTML and look for a link
       whose href contains `ref=/fcr` and the given FCR number in the `txt` param,
       then extract the `id` from that href.
    
    Returns:
        The `id` string (e.g. "53072691") or None if it cannot be resolved.
    """
    if requests is None:
        if logger:
            logger.warning("⚠️ 'requests' library not available. Cannot resolve Damco FCR id.")
        return None
    
    base_url = "https://reporting.damco.com/Reporting/spot"
    search_url = f"{base_url}?ref=/search&search={fcr_number}"
    
    try:
        if logger:
            logger.info(f"🌐 Resolving Damco FCR id for {fcr_number} via {search_url}")
        
        # Step 1: do not follow redirects so we can inspect Location header
        resp = requests.get(search_url, allow_redirects=False, timeout=30)
        
        # Case A: redirect directly to /fcr
        if resp.status_code in (301, 302, 303, 307, 308):
            location = resp.headers.get("Location", "")
            if logger:
                logger.info(f"➡️ Damco /search redirect Location: {location}")
            if location:
                parsed = urlparse(location)
                qs = parse_qs(parsed.query)
                fcr_id = qs.get("id", [None])[0]
                if fcr_id:
                    if logger:
                        logger.info(f"✅ Resolved Damco FCR id from redirect: {fcr_id}")
                    return fcr_id
        
        # Case B: 200 OK – we are on a search result page, need to parse HTML for /fcr link
        if resp.status_code == 200:
            html = resp.text or ""
            # Look for `/Reporting/spot?ref=/fcr&id=<digits>&txt=<FCR>` OR relative `spot?ref=/fcr&id=...`
            # NOTE: Damco sometimes HTML-encodes '&' as '&amp;' and sometimes uses td/@href instead of a tags
            import re
            patterns = [
                # Absolute URL, plain '&'
                rf"Reporting/spot\?ref=/fcr&id=(\d+)&txt={re.escape(fcr_number)}",
                # Relative URL, plain '&'
                rf"spot\?ref=/fcr&id=(\d+)&txt={re.escape(fcr_number)}",
                # Relative URL, HTML-encoded '&amp;'
                rf"spot\?ref=/fcr&amp;id=(\d+)&amp;txt={re.escape(fcr_number)}",
                # Very loose fallbacks (any txt)
                r"spot\?ref=/fcr&id=(\d+)&txt=",
                r"spot\?ref=/fcr&amp;id=(\d+)&amp;txt=",
            ]
            for pattern in patterns:
                m = re.search(pattern, html, flags=re.IGNORECASE)
                if m:
                    fcr_id = m.group(1)
                    if logger:
                        logger.info(f"✅ Resolved Damco FCR id from HTML search result: {fcr_id} (pattern: {pattern})")
                    return fcr_id
            
            if logger:
                logger.warning(f"⚠️ No /fcr link found in Damco /search HTML for {fcr_number}")
        else:
            if logger:
                logger.warning(f"⚠️ Unexpected status from Damco /search for {fcr_number}: {resp.status_code}")
    except Exception as e:
        if logger:
            logger.warning(f"⚠️ Failed to resolve Damco FCR id for {fcr_number}: {e}")
    
    return None


def fetch_damco_fcr_html(fcr_number: str, logger: logging.Logger = None) -> str:
    """
    Fetch the Damco FCR HTML for a given FCR number using the dynamic id.
    
    Flow:
      1. Resolve the dynamic `id` via resolve_damco_fcr_id().
      2. If found, GET:
         https://reporting.damco.com/Reporting/spot?ref=/fcr&id=<ID>&txt=<FCR>
      3. If id cannot be resolved, fall back to the /search HTML (may be a search list).
    """
    if requests is None:
        if logger:
            logger.warning("⚠️ 'requests' library not available. Cannot fetch Damco FCR HTML.")
        return ""
    
    try:
        fcr_id = resolve_damco_fcr_id(fcr_number, logger)
        base_url = "https://reporting.damco.com/Reporting/spot"
        
        if fcr_id:
            detail_url = f"{base_url}?ref=/fcr&id={fcr_id}&txt={fcr_number}"
            if logger:
                logger.info(f"🌐 Fetching Damco FCR detail HTML: {detail_url}")
            resp = requests.get(detail_url, timeout=30)
            if resp.status_code == 200 and resp.text:
                if logger:
                    logger.info(f"✅ Received Damco FCR detail HTML for {fcr_number} (id={fcr_id})")
                return resp.text
            else:
                if logger:
                    logger.warning(f"⚠️ Unexpected status for Damco FCR detail ({detail_url}): {resp.status_code}")
        
        # Fallback: raw /search HTML
        search_url = f"{base_url}?ref=/search&search={fcr_number}"
        if logger:
            logger.info(f"🌐 Falling back to Damco /search HTML: {search_url}")
        resp = requests.get(search_url, timeout=30)
        if resp.status_code == 200 and resp.text:
            if logger:
                logger.info(f"✅ Received Damco /search HTML for {fcr_number}")
            return resp.text
        else:
            if logger:
                logger.warning(f"⚠️ Failed to fetch Damco /search HTML for {fcr_number}: {resp.status_code}")
    except Exception as e:
        if logger:
            logger.warning(f"⚠️ Error fetching Damco FCR HTML for {fcr_number}: {e}")
    
    return ""


# -------------------------------------------------------------
# Server Client
# -------------------------------------------------------------

class ServerClient:
    """Handles all server communication for the automation script"""
    
    def __init__(self, api_base_url: str, user_id: str, job_id: str, logger=None):
        self.api_base_url = api_base_url.rstrip('/')
        self.user_id = user_id
        self.job_id = job_id
        self.logger = logger or logging.getLogger("ServerClient")
        
        if requests is None:
            self.logger.warning("⚠️ 'requests' library not available. Server communication disabled.")
            self.enabled = False
        else:
            self.enabled = True
    
    def upload_log_file(self, log_file_path: str, job_data: dict) -> str:
        """Upload log file to server via POST /api/local-exe/jobs/:jobId/upload-log"""
        if not self.enabled:
            self.logger.warning("⚠️ Server communication disabled. Skipping log file upload.")
            return None
        
        if not os.path.exists(log_file_path):
            self.logger.warning(f"⚠️ Log file not found: {log_file_path}")
            return None
        
        try:
            url = f"{self.api_base_url}/api/local-exe/jobs/{self.job_id}/upload-log"
            
            with open(log_file_path, 'rb') as f:
                files = {'logFile': (os.path.basename(log_file_path), f, 'text/plain')}
                data = {
                    'userId': job_data.get('userId', self.user_id),
                    'serviceId': job_data.get('serviceId', 'damco-tracking-maersk'),
                    'serviceName': job_data.get('serviceName', 'Damco (APM) Tracking'),
                    'inputFileName': job_data.get('inputFileName', 'input.csv'),
                    'inputFilePath': job_data.get('inputFilePath', ''),
                    'creditsUsed': str(job_data.get('creditsUsed', 0))
                }
                
                response = requests.post(url, files=files, data=data, timeout=30)
                response.raise_for_status()
                result = response.json()
                
                if result.get('success') and result.get('logFilePath'):
                    self.logger.info(f"✅ Log file uploaded: {result['logFilePath']}")
                    return result['logFilePath']
                else:
                    self.logger.warning(f"⚠️ Log upload failed: {result.get('error', 'Unknown error')}")
                    return None
        except requests.exceptions.RequestException as e:
            self.logger.warning(f"⚠️ Failed to upload log file: {e}")
            return None
        except Exception as e:
            self.logger.warning(f"⚠️ Unexpected error uploading log file: {e}")
            return None
    
    def upload_log_buffer(self, log_text: str, file_name: str, job_data: dict) -> str:
        if not self.enabled:
            self.logger.warning("⚠️ Server communication disabled. Skipping log buffer upload.")
            return None
        try:
            import io
            url = f"{self.api_base_url}/api/local-exe/jobs/{self.job_id}/upload-log"
            files = { 'logFile': (file_name, io.BytesIO(log_text.encode('utf-8')), 'text/plain') }
            data = {
                'userId': job_data.get('userId', self.user_id),
                'serviceId': job_data.get('serviceId', 'damco-tracking-maersk'),
                'serviceName': job_data.get('serviceName', 'Damco (APM) Tracking'),
                'inputFileName': job_data.get('inputFileName', 'input.csv'),
                'inputFilePath': job_data.get('inputFilePath', ''),
                'creditsUsed': str(job_data.get('creditsUsed', 0))
            }
            response = requests.post(url, files=files, data=data, timeout=30)
            response.raise_for_status()
            result = response.json()
            return result.get('logFilePath') if result.get('success') else None
        except Exception as e:
            self.logger.error(f"❌ Failed to upload log buffer: {e}")
            return None
    
    def upload_results_csv(self, csv_file_path: str, job_data: dict) -> str:
        """Upload results CSV file to server via POST /api/local-exe/jobs/:jobId/upload-log"""
        if not self.enabled:
            self.logger.warning("⚠️ Server communication disabled. Skipping CSV upload.")
            return None
        
        if not os.path.exists(csv_file_path):
            self.logger.warning(f"⚠️ CSV file not found: {csv_file_path}")
            return None
        
        try:
            url = f"{self.api_base_url}/api/local-exe/jobs/{self.job_id}/upload-log"
            
            with open(csv_file_path, 'rb') as f:
                files = {'logFile': (os.path.basename(csv_file_path), f, 'text/csv')}
                data = {
                    'userId': job_data.get('userId', self.user_id),
                    'serviceId': job_data.get('serviceId', 'damco-tracking-maersk'),
                    'serviceName': job_data.get('serviceName', 'Damco (APM) Tracking'),
                    'inputFileName': job_data.get('inputFileName', 'input.csv'),
                    'inputFilePath': job_data.get('inputFilePath', ''),
                    'creditsUsed': str(job_data.get('creditsUsed', 0))
                }
                
                response = requests.post(url, files=files, data=data, timeout=30)
                response.raise_for_status()
                result = response.json()
                
                if result.get('success') and result.get('logFilePath'):
                    self.logger.info(f"✅ Results CSV uploaded: {result['logFilePath']}")
                    return result['logFilePath']
                else:
                    self.logger.warning(f"⚠️ CSV upload failed: {result.get('error', 'Unknown error')}")
                    return None
        except requests.exceptions.RequestException as e:
            self.logger.warning(f"⚠️ Failed to upload CSV file: {e}")
            return None
        except Exception as e:
            self.logger.warning(f"⚠️ Unexpected error uploading CSV file: {e}")
            return None
    
    def report_job_result(self, result_files: list, output_directory: str, successful_count: int = None) -> bool:
        """Report job completion to server via POST /api/local-exe/jobs/:jobId/complete"""
        if not self.enabled:
            self.logger.warning("⚠️ Server communication disabled. Skipping job result reporting.")
            return False
        
        try:
            url = f"{self.api_base_url}/api/local-exe/jobs/{self.job_id}/complete"
            
            payload = {
                'success': True,
                'resultFiles': result_files,
                'outputFiles': [],
                'outputDirectory': output_directory
            }
            if successful_count is not None:
                payload['successfulCount'] = successful_count
            
            response = requests.post(url, json=payload, timeout=30)
            response.raise_for_status()
            result = response.json()
            
            if result.get('success'):
                self.logger.info("✅ Job result reported successfully to server")
                return True
            else:
                self.logger.warning(f"⚠️ Job result reporting failed: {result.get('error', 'Unknown error')}")
                return False
        except requests.exceptions.RequestException as e:
            self.logger.warning(f"⚠️ Failed to report job result: {e}")
            return False
        except Exception as e:
            self.logger.warning(f"⚠️ Unexpected error reporting job result: {e}")
            return False

class DamcoTrackingAutomation:
    def __init__(self, headless=True, output_dir='results', job_id=None, server_client=None):
        self.headless = headless
        # Use isolated service-specific output directory provided by the server
        self.output_dir = os.path.normpath(os.path.abspath(output_dir))
        os.makedirs(self.output_dir, exist_ok=True)
        
        self.job_id = job_id
        self.driver = None
        self.wait = None
        self.results = []
        self.result_files = []
        self.temp_profile = None
        self.server_client = server_client
        self.input_file_path = None
        self.credits_used = 0.0
        self.setup_logging(job_id)

    # -------------------------------------------------------------
    # Logging setup
    # -------------------------------------------------------------
    def setup_logging(self, job_id=None):
        from io import StringIO
        self.log_buffer = StringIO()
        buffer_handler = logging.StreamHandler(self.log_buffer)
        buffer_handler.setLevel(logging.INFO)
        buffer_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
        
        console_handler = logging.StreamHandler(sys.stdout)
        console_handler.setLevel(logging.INFO)
        console_formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
        console_handler.setFormatter(console_formatter)
        
        logging.basicConfig(
            level=logging.INFO,
            format="%(asctime)s - %(levelname)s - %(message)s",
            handlers=[console_handler, buffer_handler],
            force=True
        )
        self.logger = logging.getLogger(f"DamcoTracking-{job_id}")
        self.log_file_path = ''

    # -------------------------------------------------------------
    # Driver setup (UC)
    # -------------------------------------------------------------
    def setup_driver(self):
        self.logger.info("🔧 Launching Electron browser (headless mode)...")

        try:
            # Get job_id from args (should be 3rd argument)
            job_id = self.job_id if hasattr(self, 'job_id') else 'unknown'
            self.driver = ElectronBrowser.create(job_id=job_id, headless=self.headless, logger=self.logger)
            self.wait = ElectronWait(self.driver, 20)
            self.logger.info("✅ Electron browser launched successfully")
            os.makedirs(self.output_dir, exist_ok=True)
            # Use output_dir directly, no pdfs subfolder needed
            return True
        except Exception as e:
            self.logger.error(f"❌ Failed to start Electron browser: {e}")
            return False

    # -------------------------------------------------------------
    # Navigation
    # -------------------------------------------------------------
    def navigate_to_maersk(self):
        try:
            self.logger.info("🌐 Opening Maersk tracking portal...")
            self.driver.get("https://www.maersk.com/mymaersk-scm-track/")
            self.wait.until(EC.presence_of_element_located((By.TAG_NAME, "body")))
            self.logger.info("📍 Page loaded successfully.")
            
            # Handle cookie consent dialog on first visit
            self.handle_cookie_consent()
            
            return True
        except Exception as e:
            self.logger.error(f"❌ Navigation failed: {e}")
            return False

    def close_coach_popup(self):
        try:
            got_it_btn = self.wait.until(
                EC.element_to_be_clickable((By.CSS_SELECTOR, "button[data-test='finishButton']"))
            )
            got_it_btn.click()
            self.logger.info("✅ Closed coach popup.")
            time.sleep(2)
        except TimeoutException:
            self.logger.info("⚠️ No coach popup found.")
        except Exception as e:
            self.logger.warning(f"Coach popup error: {e}")

    def handle_cookie_consent(self):
        """
        Handle cookie consent dialog that appears on first visit to Maersk website.
        Clicks "Allow all" button if found, otherwise skips.
        """
        try:
            self.logger.info("🍪 Checking for cookie consent dialog...")
            
            # Multiple possible selectors for the "Allow all" button
            allow_all_selectors = [
                "button[data-test='coi-allow-all-button']",  # Desktop button
                "button[data-test='coi-allow-all-button-mobile']",  # Mobile button
                "#coi-banner-wrapper button.coi-banner__accept",  # Generic accept button
                "button.coi-banner__accept",  # Generic class-based selector
            ]
            
            button_clicked = False
            for selector in allow_all_selectors:
                try:
                    # Use ElectronWait with short timeout since this is optional
                    temp_wait = ElectronWait(self.driver, 3)
                    
                    allow_btn = temp_wait.until(
                        EC.element_to_be_clickable((By.CSS_SELECTOR, selector))
                    )
                    if allow_btn:
                        allow_btn.click()
                        self.logger.info(f"✅ Clicked 'Allow all' on cookie consent dialog (selector: {selector})")
                        button_clicked = True
                        time.sleep(2)
                        break
                except TimeoutException:
                    continue
                except Exception as e:
                    self.logger.debug(f"Selector {selector} failed: {e}")
                    continue
            
            if not button_clicked:
                self.logger.info("ℹ️ No cookie consent dialog found - continuing...")
                
        except Exception as e:
            # Don't fail the whole process if cookie handling fails
            self.logger.info(f"ℹ️ Cookie consent handling skipped: {e}")

    def read_iframe_html_for_booking(self, booking_number: str, save_path: str = None, timeout: int = 30) -> str:
        try:
            url = f"https://www.maersk.com/mymaersk-scm-track/{booking_number}"
            self.driver.get(url)
            self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "#damco-track")))
            time.sleep(2)
            html = self.driver.execute_script("return document.documentElement.outerHTML", frame_selector="#damco-track")
            if save_path:
                os.makedirs(os.path.dirname(save_path), exist_ok=True)
                with open(save_path, "w", encoding="utf-8") as f:
                    f.write(html)
            self.logger.info(f"✅ Iframe HTML read for {booking_number}: {len(html)} bytes")
            return html
        except Exception as e:
            self.logger.error(f"❌ Failed to read iframe HTML for {booking_number}: {e}")
            return ""

    # -------------------------------------------------------------
    # Core Processing
    # -------------------------------------------------------------
    def process_booking(self, booking_number, invoice_number, index):
        try:
            self.logger.info(f"🔍 Processing FCR {index}: {booking_number} (Invoice: {invoice_number})")
            
            # -----------------------------------------------------
            # 1) Navigate to live Maersk tracking page
            # -----------------------------------------------------
            maersk_url = f"https://www.maersk.com/mymaersk-scm-track/{booking_number}"
            self.logger.info(f"🌐 Navigating to Maersk tracking page: {maersk_url}")
            self.driver.get(maersk_url)
            # Ensure main page body is loaded
            self.wait.until(EC.presence_of_element_located((By.TAG_NAME, "body")))
            self.logger.info("📄 Maersk tracking page body loaded.")
            
            # Cookie consent is only handled on first visit in navigate_to_maersk()
            # No need to handle it again for each booking
            
            # -----------------------------------------------------
            # 2) Wait for iframe to be present and load
            # -----------------------------------------------------
            iframe_selector = "#damco-track"
            self.logger.info("⏳ Waiting for Damco iframe to load...")
            
            try:
                # Wait for iframe element to be present in DOM
                self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, iframe_selector)))
                self.logger.info("✅ Iframe element found in page")
                
                # Reduced wait time - iframe loads quickly
                time.sleep(2)  # Reduced from 5s to 2s
                
            except TimeoutException:
                self.logger.error(f"❌ Timeout waiting for iframe to load for {booking_number}")
                raise Exception("Iframe not found on page")
            
            # -----------------------------------------------------
            # 3) OPTIMIZED: Skip URL check, directly resolve and navigate
            # -----------------------------------------------------
            # Instead of checking iframe URL (which can be unreliable),
            # always resolve the FCR ID and navigate directly - faster and more reliable
            
            self.logger.info("🔍 Resolving FCR ID and navigating to detail page...")
            
            try:
                # Resolve the FCR ID
                fcr_id = resolve_damco_fcr_id(booking_number, self.logger)
                
                if fcr_id:
                    # Build the detail URL
                    detail_url = f"https://reporting.damco.com/Reporting/spot?ref=/fcr&id={fcr_id}&txt={booking_number}"
                    self.logger.info(f"✅ Resolved FCR detail URL: {detail_url}")
                    
                    # Use JavaScript to change the iframe src directly
                    self.logger.info("🔄 Navigating iframe to detail page...")
                    self.driver.execute_script(
                        f"document.getElementById('damco-track').src = '{detail_url}';",
                        frame_selector=None  # Execute in main page context
                    )
                    
                    # Wait for the detail page to load (optimized timing)
                    self.logger.info("⏳ Waiting for detail page to load...")
                    time.sleep(4)  # Reduced from 8s to 4s - detail page loads quickly
                    
                    self.logger.info("✅ Successfully navigated to Shipment Details page")
                else:
                    self.logger.warning(f"⚠️ Could not resolve FCR ID for {booking_number}")
                    self.logger.warning("⚠️ Will print current search results page")
                    # Still wait a bit for search page to be ready
                    time.sleep(2)
                    
            except Exception as e:
                self.logger.error(f"❌ Error navigating to detail page: {e}")
                import traceback
                traceback.print_exc()
                self.logger.warning("⚠️ Will print current page as-is")
            
            # -----------------------------------------------------
            # 5) Print page to PDF
            # -----------------------------------------------------
            self.logger.info("📄 Printing page to PDF...")
            pdf_filename = f"{index:03d} {invoice_number} {booking_number}.pdf"
            pdf_path = os.path.join(self.output_dir, pdf_filename)
            
            try:
                pdf_data = self.driver.execute_cdp_cmd("Page.printToPDF", {
                    "format": "A4",
                    "printBackground": True,
                    "preferCSSPageSize": False
                })
                
                with open(pdf_path, "wb") as f:
                    f.write(base64.b64decode(pdf_data["data"]))
                self.logger.info(f"✅ PDF saved: {pdf_filename}")
                
            except Exception as e:
                self.logger.error(f"❌ Error generating PDF: {e}")
                raise
            
            # -----------------------------------------------------
            # 6) Navigate back to Maersk tracking page
            # -----------------------------------------------------
            self.logger.info("🔙 Navigating back to Maersk tracking page...")
            self.driver.get("https://www.maersk.com/mymaersk-scm-track/")
            time.sleep(1)  # Reduced from 2s to 1s - page loads quickly
            
            # Record success
            self.results.append({
                "fcr_number": booking_number,
                "invoice_number": invoice_number,
                "status": "success",
                "pdf_file": pdf_filename,
                "timestamp": datetime.now().isoformat()
            })
            
            return pdf_filename

        except Exception as e:
            self.logger.error(f"❌ Error processing {booking_number}: {e}")
            import traceback
            traceback.print_exc()
            
            self.results.append({
                "fcr_number": booking_number,
                "invoice_number": invoice_number,
                "status": "error",
                "error": str(e),
                "timestamp": datetime.now().isoformat()
            })
            return None

    def _wait_for_iframe_ready(self, iframe_selector: str, timeout: int = 20):
        """Wait until the iframe element is present in the main page."""
        try:
            self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, iframe_selector)))
            return True
        except Exception:
            return False

    def _ensure_shipment_details_loaded(self, iframe_selector: str, booking_number: str, timeout: int = 30):
        """
        Ensures the iframe content is on the shipment details page.
        Uses ElectronBrowser's frame_selector capability to interact with cross-origin iframe.
        """
        
        def _check_and_handle(driver):
            # Check for Type 1: Shipment Details already loaded
            try:
                # Using ID check inside iframe
                driver.find_element(By.ID, "shipment_details", frame_selector=iframe_selector)
                return True
            except Exception:
                pass
            
            # Check for Type 2: Search results list with FCR link
            try:
                # Use XPath to find the specific link containing the booking number
                xpath = f"//div[@id='fcr_by_fcr_number']//a[contains(text(), '{booking_number}')]"
                link = driver.find_element(By.XPATH, xpath, frame_selector=iframe_selector)
                if link:
                    self.logger.info(f"🖱️ Clicking FCR link for {booking_number}")
                    link.click()
                    # After clicking, we return False to continue waiting for shipment_details
                    return False
            except Exception:
                pass
            
            return False

        # Custom wait logic
        end_time = time.time() + timeout
        while time.time() < end_time:
            if _check_and_handle(self.driver):
                self.logger.info("✅ Shipment details loaded in iframe.")
                return True
            time.sleep(1)
            
        self.logger.warning(f"⚠️ Timed out waiting for shipment details for {booking_number}")
        return False

    # -------------------------------------------------------------
    # File Reading
    # -------------------------------------------------------------
    def read_booking_numbers_from_file(self, file_path):
        try:
            ext = os.path.splitext(file_path)[1].lower()
            if ext != ".csv":
                error_msg = (
                    f"⚠️  WARNING: Invalid File Type\n"
                    f"📁 File provided: {file_path}\n"
                    f"📄 File extension: {ext}\n\n"
                    f"ℹ️  This automation only supports CSV (Comma-Separated Values) files.\n"
                    f"   Please ensure your input file has a .csv extension.\n\n"
                )
                raise ValueError(error_msg)
            df = pd.read_csv(file_path)
            
            # Normalize column names (case-insensitive)
            df.columns = df.columns.str.strip()
            column_map = {col.lower(): col for col in df.columns}
            
            # Find FCR Number and INVOICE_NO columns (case-insensitive)
            fcr_col = None
            invoice_col = None
            
            for key in column_map:
                if 'fcr' in key and 'number' in key:
                    fcr_col = column_map[key]
                elif 'invoice' in key and 'no' in key:
                    invoice_col = column_map[key]
            
            if fcr_col is None:
                raise ValueError("CSV file must contain 'FCR Number' column")
            if invoice_col is None:
                raise ValueError("CSV file must contain 'INVOICE_NO' column")
            
            # Read both columns and create list of dictionaries
            bookings = []
            for _, row in df.iterrows():
                fcr_number = str(row[fcr_col]).strip() if pd.notna(row[fcr_col]) else ""
                invoice_number = str(row[invoice_col]).strip() if pd.notna(row[invoice_col]) else ""
                if fcr_number and invoice_number:
                    bookings.append({
                        "fcr_number": fcr_number,
                        "invoice_number": invoice_number
                    })
            
            return bookings
        except Exception as e:
            self.logger.error(f"❌ Failed to read {file_path}: {e}")
            return []

    # -------------------------------------------------------------
    # Generate Results CSV
    # -------------------------------------------------------------
    def generate_results_csv(self):
        """Generate CSV file with processing results (success and failure)"""
        if not self.results:
            self.logger.warning("No results to generate CSV file")
            return None
        
        csv_filename = f"damco_tracking_results_{self.job_id}.csv"
        csv_path = os.path.join(self.output_dir, csv_filename)
        
        self.logger.info("ℹ️ Results CSV generation disabled")
        return None
        
        # try:
        #     # Prepare data for CSV
        #     csv_data = []
        #     for result in self.results:
        #         csv_data.append({
        #             "fcr_number": result.get("fcr_number", ""),
        #             "invoice_number": result.get("invoice_number", ""),
        #             "status": result.get("status", "error"),
        #             "error": result.get("error", ""),
        #             "timestamp": result.get("timestamp", "")
        #         })
            
        #     # Create DataFrame and save to CSV
        #     df = pd.DataFrame(csv_data)
        
        #     # self.logger.info("ℹ️ Results CSV generation disabled")
        #     # return None
        # except Exception as e:
        #     self.logger.error(f"❌ Failed to generate results CSV: {e}")
        #     return None

    # -------------------------------------------------------------
    # Process All Bookings
    # -------------------------------------------------------------
    def process_all_bookings(self, bookings):
        pdfs, fails = [], []
        total_bookings = len(bookings)
        success_count = 0
        failed_count = 0
        
        self.logger.info(f"\n{'='*60}")
        self.logger.info(f"Found {total_bookings} bookings to process")
        self.logger.info(f"{'='*60}\n")
        
        for i, booking_data in enumerate(bookings, 1):
            # Check for stop signal
            stop_file = os.path.join(self.output_dir, f"stop_{self.job_id}.txt")
            if os.path.exists(stop_file):
                self.logger.info("Stop signal received, stopping automation...")
                self.cleanup()
                sys.exit(130)
            
            # Log progress: items remaining
            items_remaining = total_bookings - i + 1
            self.logger.info(f"📊 Progress: {i}/{total_bookings} (Remaining: {items_remaining})")
            
            fcr_number = booking_data["fcr_number"]
            invoice_number = booking_data["invoice_number"]
            
            pdf = self.process_booking(fcr_number, invoice_number, i)
            if pdf:
                pdfs.append(pdf)
                success_count += 1
                self.logger.info(f"✓ Successfully processed booking {i}/{total_bookings}: {fcr_number} (Invoice: {invoice_number})")
            else:
                fails.append(booking_data)
                failed_count += 1
                self.logger.error(f"✗ Failed to process booking {i}/{total_bookings}: {fcr_number} (Invoice: {invoice_number})")
            
            self.logger.info(f"📥 Downloads complete: {success_count} successful, {failed_count} failed")
            # Removed time.sleep(2) - unnecessary delay between bookings
        
        # Generate results CSV file
        csv_filename = self.generate_results_csv()
        if csv_filename:
            self.logger.info(f"📊 Results CSV file: {csv_filename}")
        
        # Upload files to server if server_client is available
        uploaded_log_path = None
        uploaded_csv_path = None
        
        if self.server_client:
            try:
                # Prepare job data for uploads
                job_data = {
                    'userId': self.server_client.user_id,
                    'serviceId': 'damco-tracking-maersk',
                    'serviceName': 'Damco (APM) Tracking',
                    'inputFileName': os.path.basename(self.input_file_path) if self.input_file_path else 'input.csv',
                    'inputFilePath': self.input_file_path if self.input_file_path else '',
                    'creditsUsed': self.credits_used
                }
                
                uploaded_log_path = None
                if hasattr(self, 'log_buffer'):
                    log_text = self.log_buffer.getvalue()
                    if log_text:
                        uploaded_log_path = self.server_client.upload_log_buffer(log_text, f'damco_tracking_{self.job_id}.log', job_data)
                
                # Upload results CSV
                if csv_filename:
                    csv_path = os.path.join(self.output_dir, csv_filename)
                    if os.path.exists(csv_path):
                        uploaded_csv_path = self.server_client.upload_results_csv(csv_path, job_data)
                
                # Report job result
                result_files = []
                if uploaded_csv_path:
                    result_files.append(uploaded_csv_path)
                elif uploaded_log_path:
                    result_files.append(uploaded_log_path)
                
                if result_files:
                    self.server_client.report_job_result(
                        result_files,
                        self.output_dir,
                        successful_count=success_count
                    )
            except Exception as e:
                self.logger.warning(f"⚠️ Error uploading files to server: {e}")
                # Continue even if upload fails
        
        # Print summary
        self.logger.info(f"\n{'='*60}")
        self.logger.info("PROCESSING SUMMARY")
        self.logger.info(f"{'='*60}")
        self.logger.info(f"Total bookings: {total_bookings}")
        self.logger.info(f"✓ Successful downloads: {success_count}")
        self.logger.info(f"✗ Failed downloads: {failed_count}")
        self.logger.info(f"📥 Total downloads completed: {success_count + failed_count}/{total_bookings}")
        
        self.logger.info(f"{'='*60}\n")
        
        return pdfs, fails

    # -------------------------------------------------------------
    # Cleanup
    # -------------------------------------------------------------
    def cleanup(self):
        try:
            if self.driver:
                self.driver.quit()
                self.logger.info("🔒 Browser closed.")
        except Exception as e:
            self.logger.warning(f"Error closing browser: {e}")

    # -------------------------------------------------------------
    # Main runner
    # -------------------------------------------------------------
    def run(self, file_path):
        try:
            if not self.setup_driver():
                return False
            
            # Navigate to Maersk portal first to handle cookie consent on first visit
            if not self.navigate_to_maersk():
                self.logger.error("❌ Failed to navigate to Maersk portal")
                return False

            bookings = self.read_booking_numbers_from_file(file_path)
            pdfs, fails = self.process_all_bookings(bookings)

            self.logger.info(f"🎉 Done. Success={len(pdfs)}, Fail={len(fails)}")
            return True
        finally:
            self.cleanup()
    
    # Backward compatibility method
    def run_automation(self, file_path):
        return self.run(file_path)


# -------------------------------------------------------------
# Entry Point
# -------------------------------------------------------------
def main():
    # Early execution logging
    print("🔵 [DAMCO_TRACKING] Script execution started", flush=True)
    print(f"🔵 [DAMCO_TRACKING] sys.argv: {sys.argv}", flush=True)
    print(f"🔵 [DAMCO_TRACKING] __name__: {__name__}", flush=True)
    print(f"🔵 [DAMCO_TRACKING] Python version: {sys.version}", flush=True)
    
    # Check for visible/headed mode flag
    headless_mode = True
    if "--visible" in sys.argv:
        headless_mode = False
        sys.argv.remove("--visible")
        print("🔵 [DAMCO_TRACKING] Running in VISIBLE mode (not headless)", flush=True)
    
    if len(sys.argv) < 4:
        error_msg = "Usage: python damco_tracking_maersk.py <input_file> <output_dir> <job_id> [api_base_url] [user_id] [credits_used] [--visible]"
        print(f"❌ [DAMCO_TRACKING] {error_msg}", file=sys.stderr, flush=True)
        print(error_msg, file=sys.stderr)
        sys.exit(1)

    file_path, output_dir, job_id = sys.argv[1], sys.argv[2], sys.argv[3]
    print(f"🔵 [DAMCO_TRACKING] Parsed arguments: file_path={file_path}, output_dir={output_dir}, job_id={job_id}", flush=True)
    
    # Optional server communication parameters
    api_base_url = sys.argv[4] if len(sys.argv) > 4 else None
    user_id = sys.argv[5] if len(sys.argv) > 5 else None
    credits_used = float(sys.argv[6]) if len(sys.argv) > 6 and sys.argv[6] else 0.0
    print(f"🔵 [DAMCO_TRACKING] Server params: api_base_url={api_base_url}, user_id={user_id}, credits_used={credits_used}", flush=True)
    
    # File path check
    file_exists = os.path.exists(file_path)
    print(f"🔵 [DAMCO_TRACKING] File path check: {file_path} exists: {file_exists}", flush=True)
    if not file_exists:
        error_msg = f"❌ File not found: {file_path}"
        print(f"❌ [DAMCO_TRACKING] {error_msg}", file=sys.stderr, flush=True)
        print(error_msg, file=sys.stderr)
        sys.exit(1)

    # Create server client if parameters provided
    print("🔵 [DAMCO_TRACKING] Importing modules...", flush=True)
    server_client = None
    if api_base_url and user_id:
        try:
            server_client = ServerClient(api_base_url, user_id, job_id)
            if server_client.enabled:
                print(f"✅ [DAMCO_TRACKING] Server communication enabled: {api_base_url}", flush=True)
                print(f"✅ Server communication enabled: {api_base_url}")
            else:
                print(f"⚠️ [DAMCO_TRACKING] Server communication disabled (requests library not available)", flush=True)
                print(f"⚠️ Server communication disabled (requests library not available)")
        except Exception as e:
            print(f"❌ [DAMCO_TRACKING] Failed to create ServerClient: {e}", file=sys.stderr, flush=True)
            import traceback
            traceback.print_exc(file=sys.stderr)

    print("🔵 [DAMCO_TRACKING] Creating DamcoTrackingAutomation instance...", flush=True)
    try:
        automation = DamcoTrackingAutomation(
            headless=headless_mode, 
            output_dir=output_dir, 
            job_id=job_id,
            server_client=server_client
        )
        
        # Store input file path and credits for server uploads
        automation.input_file_path = file_path
        automation.credits_used = credits_used
        
        print("🔵 [DAMCO_TRACKING] Starting automation.run()...", flush=True)
        ok = automation.run(file_path)
        print(f"🔵 [DAMCO_TRACKING] Automation completed: success={ok}", flush=True)
        sys.exit(0 if ok else 1)
    except Exception as e:
        print(f"❌ [DAMCO_TRACKING] Error in main execution: {e}", file=sys.stderr, flush=True)
        import traceback
        traceback.print_exc(file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    try:
        main()
    except Exception as e:
        print(f"❌ [DAMCO_TRACKING] Fatal error: {e}", file=sys.stderr, flush=True)
        import traceback
        traceback.print_exc(file=sys.stderr)
        sys.exit(1)