#!/usr/bin/env python3
"""
Standalone API Server for DCS Credit Application Extension
This version doesn't depend on the corrupted main.py file
"""

import os
import json
import logging
import shutil
from datetime import datetime
from pathlib import Path
from typing import Dict, Any, Optional
import dateutil.parser

from fastapi import FastAPI, HTTPException, Request, Query, Response
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import uvicorn
import xmlrpc.client
import base64
import re

# Configuration (inline to avoid importing config.py)
TEMPLATE_PATH = "./template/autoking_template.pdf"
CUSTOMER_DOCS_BASE = r"D:\Customer Docs"
ODOO_CONFIG_FILE = "./odoo_config.json"

# Set up logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)


def calculate_best_income(data: Dict, prefix: str) -> float:
    """
    Calculate the best monthly income from available data
    Ranking: 1=YTD calculation, 2=hourly calculation, 3=stated income

    Args:
        data: Dictionary containing extracted data
        prefix: Either 'applicant' or 'co_applicant'

    Returns:
        Best monthly income value
    """
    # Build field names based on prefix
    if prefix == 'applicant':
        ytd_income_field = 'applicant_ytd_income'
        ytd_periods_field = 'applicant_ytd_pay_periods'
        hours_field = 'applicant_hours_per_week'
        hourly_pay_field = 'applicant_hourly_pay'
        stated_income_field = 'applicant_monthly_income'
    else:  # co_applicant
        ytd_income_field = 'co_applicant_ytd_income'
        ytd_periods_field = 'co_applicant_ytd_pay_periods'
        hours_field = 'co_applicant_hours_per_week'
        hourly_pay_field = 'co_applicant_hourly_pay'
        stated_income_field = 'co_applicant_monthly_income'

    # 1. Try YTD calculation (highest priority)
    ytd_income = data.get(ytd_income_field, 0)
    ytd_periods = data.get(ytd_periods_field, 0)
    if ytd_income and ytd_periods:
        try:
            ytd_monthly = float(ytd_income) / float(ytd_periods)
            if ytd_monthly > 0:
                logger.info(f"Using YTD income for {prefix}: ${ytd_monthly:.2f} (YTD: ${ytd_income}, Periods: {ytd_periods})")
                return ytd_monthly
        except (ValueError, ZeroDivisionError):
            pass

    # 2. Try hourly calculation (second priority)
    hours_per_week = data.get(hours_field, 0)
    hourly_pay = data.get(hourly_pay_field, 0)
    if hours_per_week and hourly_pay:
        try:
            hours = float(hours_per_week)
            pay = float(hourly_pay)
            if hours > 0 and pay > 0:
                # Regular hours (max 40) * 4.3 weeks per month * hourly rate
                regular_hours = min(hours, 40)
                regular_income = regular_hours * 4.3 * pay

                # Overtime hours (anything over 40) * 4.3 weeks per month * hourly rate * 1.5
                overtime_hours = max(hours - 40, 0)
                overtime_income = overtime_hours * 4.3 * pay * 1.5

                calculated_monthly = regular_income + overtime_income
                if calculated_monthly > 0:
                    logger.info(f"Using hourly calculation for {prefix}: ${calculated_monthly:.2f} ({hours}h/week @ ${pay}/hr)")
                    return calculated_monthly
        except ValueError:
            pass

    # 3. Fall back to stated income (lowest priority)
    stated_income = data.get(stated_income_field, 0)
    if stated_income:
        try:
            # Clean currency formatting
            if isinstance(stated_income, str):
                stated_value = float(stated_income.replace('$', '').replace(',', ''))
            else:
                stated_value = float(stated_income)

            if stated_value > 0:
                logger.info(f"Using stated income for {prefix}: ${stated_value:.2f}")
                return stated_value
        except ValueError:
            pass

    logger.warning(f"No valid income found for {prefix}")
    return 0.0


class OdooClient:
    """Client for connecting to Odoo via XML-RPC API"""
    
    def __init__(self, url: str, database: str, username: str, password: str):
        self.url = url.rstrip('/')
        self.database = database
        self.username = username
        self.password = password
        self.uid = None
        self.common = None
        self.models = None
        
    def connect(self) -> bool:
        """Test connection and authenticate with Odoo"""
        try:
            # Connect to common endpoint for authentication
            self.common = xmlrpc.client.ServerProxy(f'{self.url}/xmlrpc/2/common')
            
            # Test connection by getting version info
            version_info = self.common.version()
            logger.info(f"Connected to Odoo {version_info.get('server_version', 'unknown')}")
            
            # Authenticate user
            self.uid = self.common.authenticate(self.database, self.username, self.password, {})
            if not self.uid:
                logger.error("Authentication failed - invalid credentials")
                return False
            
            # Connect to models endpoint
            self.models = xmlrpc.client.ServerProxy(f'{self.url}/xmlrpc/2/object')
            logger.info(f"Successfully authenticated as user ID: {self.uid}")
            return True
            
        except Exception as e:
            logger.error(f"Failed to connect to Odoo: {str(e)}")
            return False
    
    def normalize_phone(self, phone: str) -> str:
        """Normalize phone number for comparison"""
        if not phone:
            return ""
        # Remove all non-digit characters
        digits_only = re.sub(r'\D', '', phone)
        # Return last 10 digits for US numbers
        return digits_only[-10:] if len(digits_only) >= 10 else digits_only
    
    def search_leads(self, name: str, phone: str) -> list:
        """Search for leads by name and phone"""
        if not self.models or not self.uid:
            raise Exception("Not connected to Odoo")
        
        try:
            normalized_phone = self.normalize_phone(phone)
            logger.info(f"Searching for leads: name='{name}', phone='{phone}', normalized_phone='{normalized_phone}'")
            
            # Build search domain - try multiple search strategies
            # PRIORITY: Phone number matching since Odoo uses vehicle names, not customer names
            domains_to_try = []
            
            # Strategy 1: PRIORITY - Phone number search (most reliable)
            if normalized_phone:
                domains_to_try.append(['|', ('phone', 'like', normalized_phone), ('mobile', 'like', normalized_phone)])
                logger.info(f"Strategy 1 (PRIORITY): Phone search for '{normalized_phone}'")
            
            # Strategy 2: Phone with dashes
            if phone:
                domains_to_try.append(['|', ('phone', 'ilike', phone), ('mobile', 'ilike', phone)])
                logger.info(f"Strategy 2: Phone with formatting search for '{phone}'")
            
            # Strategy 3: Exact name match (case-insensitive) 
            if name:
                domains_to_try.append([('name', 'ilike', name)])
                logger.info(f"Strategy 3: Exact name search for '{name}'")
            
            # Strategy 4: Name contains just the customer part (extract from phone-name format)
            if name and ' - ' in name:
                # Extract customer name from "715-497-8572 - John Test" format
                customer_part = name.split(' - ', 1)[1] if len(name.split(' - ', 1)) > 1 else name
                domains_to_try.append([('name', 'ilike', customer_part)])
                logger.info(f"Strategy 4: Customer name part search for '{customer_part}'")
            
            # Strategy 5: Name contains phone number part (extract from phone-name format)
            if name and ' - ' in name:
                # Extract phone part from "715-497-8572 - John Test" format
                phone_part = name.split(' - ')[0]
                domains_to_try.append([('name', 'ilike', phone_part)])
                logger.info(f"Strategy 5: Phone part search for '{phone_part}'")
            
            
            # Try each search strategy
            all_leads = []
            for i, domain in enumerate(domains_to_try):
                if not domain:
                    continue
                    
                logger.info(f"Executing search {i+1}/{len(domains_to_try)} with domain: {domain}")
                
                try:
                    # Search for leads with this domain
                    lead_ids = self.models.execute_kw(
                        self.database, self.uid, self.password,
                        'crm.lead', 'search',
                        [domain],
                        {'limit': 20, 'order': 'create_date desc'}  # Most recent first
                    )
                    
                    logger.info(f"Search {i+1} returned {len(lead_ids) if lead_ids else 0} lead IDs")
                    
                    if lead_ids:
                        # Get lead details
                        leads = self.models.execute_kw(
                            self.database, self.uid, self.password,
                            'crm.lead', 'read',
                            [lead_ids],
                            {'fields': ['id', 'name', 'phone', 'mobile', 'email_from', 'create_date', 'stage_id']}
                        )
                        logger.info(f"Retrieved details for {len(leads)} leads from search {i+1}")
                        
                        # Log each found lead for debugging
                        for lead in leads:
                            logger.info(f"Found lead: ID={lead['id']}, name='{lead.get('name', '')}'")
                        
                        # Add to results if not already there
                        for lead in leads:
                            if not any(l['id'] == lead['id'] for l in all_leads):
                                all_leads.append(lead)
                                
                except Exception as search_error:
                    logger.error(f"Search {i+1} failed with error: {search_error}")
                    # Continue with next search strategy
                    continue
            
            if not all_leads:
                logger.info("No matching leads found with any search strategy")
                # Debug: Try to get a few leads to verify connection works
                try:
                    logger.info("Attempting to verify Odoo connection by retrieving recent leads...")
                    test_lead_ids = self.models.execute_kw(
                        self.database, self.uid, self.password,
                        'crm.lead', 'search',
                        [[]],
                        {'limit': 5, 'order': 'create_date desc'}
                    )
                    if test_lead_ids:
                        test_leads = self.models.execute_kw(
                            self.database, self.uid, self.password,
                            'crm.lead', 'read',
                            [test_lead_ids],
                            {'fields': ['id', 'name']}
                        )
                        logger.info(f"Connection verified: found {len(test_leads)} recent leads")
                        for lead in test_leads:
                            logger.info(f"Sample lead: ID={lead['id']}, name='{lead.get('name', '')}'")
                    else:
                        logger.warning("No leads found in database - might be empty or access restricted")
                except Exception as test_error:
                    logger.error(f"Connection test failed: {test_error}")
            else:
                logger.info(f"Found {len(all_leads)} total matching leads")
            
            return all_leads
            
        except Exception as e:
            logger.error(f"Error searching leads: {str(e)}")
            raise
    
    def debug_search(self, search_term: str, field: str = 'name', operator: str = 'ilike') -> dict:
        """Debug method to test individual search operations and show recent leads"""
        if not self.models or not self.uid:
            raise Exception("Not connected to Odoo")
        
        try:
            logger.info(f"DEBUG SEARCH: field='{field}', operator='{operator}', term='{search_term}'")
            
            domain = [(field, operator, search_term)]
            logger.info(f"Search domain: {domain}")
            
            # Search for leads
            lead_ids = self.models.execute_kw(
                self.database, self.uid, self.password,
                'crm.lead', 'search',
                [domain],
                {'limit': 10, 'order': 'create_date desc'}
            )
            
            logger.info(f"Found {len(lead_ids) if lead_ids else 0} lead IDs")
            
            # Get recent leads for comparison (last 50)
            recent_lead_ids = self.models.execute_kw(
                self.database, self.uid, self.password,
                'crm.lead', 'search',
                [[]],  # Empty domain = all leads
                {'limit': 50, 'order': 'create_date desc'}
            )
            
            recent_leads = []
            if recent_lead_ids:
                recent_leads = self.models.execute_kw(
                    self.database, self.uid, self.password,
                    'crm.lead', 'read',
                    [recent_lead_ids],
                    {'fields': ['name', 'phone', 'mobile', 'create_date', 'stage_id', 'user_id']}
                )
            
            # Get found leads details
            found_leads = []
            if lead_ids:
                found_leads = self.models.execute_kw(
                    self.database, self.uid, self.password,
                    'crm.lead', 'read',
                    [lead_ids],
                    {'fields': ['name', 'phone', 'mobile', 'create_date', 'stage_id', 'user_id']}
                )
            
            return {
                'search_performed': {
                    'field': field,
                    'operator': operator, 
                    'term': search_term,
                    'domain': domain
                },
                'found_leads': found_leads,
                'recent_leads': recent_leads[:10]  # Show first 10 for debugging
            }
                
        except Exception as e:
            logger.error(f"Debug search failed: {str(e)}")
            raise

    def attach_file(self, lead_id: int, filename: str, file_content: bytes) -> bool:
        """Attach a file to a lead"""
        if not self.models or not self.uid:
            raise Exception("Not connected to Odoo")
        
        try:
            # Encode file content to base64
            file_data = base64.b64encode(file_content).decode('utf-8')
            
            # Create attachment
            attachment_id = self.models.execute_kw(
                self.database, self.uid, self.password,
                'ir.attachment', 'create',
                [{
                    'name': filename,
                    'datas': file_data,
                    'res_model': 'crm.lead',
                    'res_id': lead_id,
                    'type': 'binary'
                }]
            )
            
            logger.info(f"Successfully attached file '{filename}' to lead {lead_id}")
            return True
            
        except Exception as e:
            logger.error(f"Error attaching file: {str(e)}")
            return False
    
    def add_tag(self, lead_id: int, tag_name: str) -> bool:
        """Add a tag to a lead"""
        if not self.models or not self.uid:
            raise Exception("Not connected to Odoo")
        
        try:
            # First, find or create the tag
            tag_ids = self.models.execute_kw(
                self.database, self.uid, self.password,
                'crm.tag', 'search',
                [[('name', '=', tag_name)]]
            )
            
            if not tag_ids:
                # Create the tag if it doesn't exist
                tag_id = self.models.execute_kw(
                    self.database, self.uid, self.password,
                    'crm.tag', 'create',
                    [{'name': tag_name}]
                )
                logger.info(f"Created new tag '{tag_name}' with ID: {tag_id}")
            else:
                tag_id = tag_ids[0]
            
            # Add tag to lead (using special write operation for many2many)
            self.models.execute_kw(
                self.database, self.uid, self.password,
                'crm.lead', 'write',
                [[lead_id], {'tag_ids': [(4, tag_id, 0)]}]  # (4, id, 0) means link existing record
            )
            
            logger.info(f"Successfully added tag '{tag_name}' to lead {lead_id}")
            return True
            
        except Exception as e:
            logger.error(f"Error adding tag: {str(e)}")
            return False
    
    def create_lead(self, extracted_data: dict) -> Optional[int]:
        """Create a new lead in Odoo with all extracted credit application data"""
        if not self.models or not self.uid:
            return None
        
        try:
            # Build customer name
            first_name = extracted_data.get('first_name', '')
            last_name = extracted_data.get('last_name', '')
            customer_name = f"{first_name} {last_name}".strip()
            
            if not customer_name:
                customer_name = extracted_data.get('contact_name', 'Unknown Customer')
            
            # Build opportunity name from vehicle info if available
            vehicle_year = extracted_data.get('vehicle_year', '')
            vehicle_make = extracted_data.get('vehicle_make', '')
            vehicle_model = extracted_data.get('vehicle_model', '')
            
            if vehicle_year and vehicle_make and vehicle_model:
                opportunity_name = f"{vehicle_year} {vehicle_make} {vehicle_model}".strip()
            elif vehicle_make and vehicle_model:
                opportunity_name = f"{vehicle_make} {vehicle_model}".strip()
            else:
                opportunity_name = customer_name
            
            # Build lead values
            lead_vals = {
                'name': opportunity_name,
                'contact_name': customer_name,
                # Don't set partner_name to avoid duplicate contacts
                'phone': extracted_data.get('mobile_phone') or extracted_data.get('home_phone') or extracted_data.get('phone', ''),
                'mobile': extracted_data.get('mobile_phone', ''),
                'email_from': extracted_data.get('email', ''),
                'street': extracted_data.get('address', ''),
                'city': extracted_data.get('city', ''),
                'zip': extracted_data.get('zip', ''),
                'type': 'lead',
                'x_lead_type': 'credit_app',
                'x_lead_source': 'chrome_extension',  # Mark as coming from extension
                'description': f"Credit application processed via Chrome extension on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
            }
            
            # Remove empty values
            lead_vals = {k: v for k, v in lead_vals.items() if v}
            
            # Create the lead
            lead_id = self.models.execute_kw(
                self.database, self.uid, self.password,
                'crm.lead', 'create', [lead_vals]
            )
            
            logger.info(f"Created new lead ID: {lead_id} with {len(lead_vals)} fields")
            
            # Update with full credit application data
            if lead_id:
                self.update_lead_data(lead_id, extracted_data)
            
            return lead_id
            
        except Exception as e:
            logger.error(f"Error creating lead: {e}")
            return None

    def update_lead_data(self, lead_id: int, extracted_data: dict) -> bool:
        """Update lead with extracted credit application data using structured ADF fields"""
        if not self.models or not self.uid:
            raise Exception("Not connected to Odoo")
            
        try:
            # Map extracted data to ADF module structured fields
            lead_updates = {}
            
            # Set lead type and processing flags with current timestamp
            lead_updates['x_lead_type'] = 'credit_app'
            lead_updates['x_credit_app_processed'] = True
            lead_updates['x_credit_app_date'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            
            # Basic contact info (use existing standard fields)
            if extracted_data.get('first_name') and extracted_data.get('last_name'):
                full_name = f"{extracted_data['first_name']} {extracted_data['last_name']}"
                lead_updates['contact_name'] = full_name
                lead_updates['x_customer_name'] = full_name
            
            if extracted_data.get('email'):
                lead_updates['email_from'] = extracted_data['email']
            
            # Phone prioritization: mobile > home > work (standard fields)
            phone = (extracted_data.get('mobile_phone') or 
                    extracted_data.get('phone') or 
                    extracted_data.get('applicant_work_phone'))
            if phone:
                lead_updates['phone'] = phone
                
            if extracted_data.get('mobile_phone'):
                lead_updates['mobile'] = extracted_data['mobile_phone']
            
            # Standard address fields
            if extracted_data.get('address'):
                lead_updates['street'] = extracted_data['address']
            if extracted_data.get('city'):
                lead_updates['city'] = extracted_data['city']
            if extracted_data.get('zip'):
                lead_updates['zip'] = extracted_data['zip']
            
            # ===== APPLICANT STRUCTURED FIELDS =====
            # Personal Information
            if extracted_data.get('first_name'):
                lead_updates['x_first_name'] = extracted_data['first_name']
            if extracted_data.get('middle_name'):
                lead_updates['x_middle_name'] = extracted_data['middle_name']
            if extracted_data.get('last_name'):
                lead_updates['x_last_name'] = extracted_data['last_name']
            if extracted_data.get('ssn'):
                lead_updates['x_ssn'] = extracted_data['ssn']
            if extracted_data.get('date_of_birth'):
                # Convert date string to date object
                try:
                    lead_updates['x_date_of_birth'] = dateutil.parser.parse(extracted_data['date_of_birth']).date().strftime('%Y-%m-%d')
                except:
                    pass
            if extracted_data.get('dl_number'):
                lead_updates['x_dl_number'] = extracted_data['dl_number']
            if extracted_data.get('dl_state'):
                lead_updates['x_dl_state'] = extracted_data['dl_state']
            if extracted_data.get('dl_exp_date'):
                try:
                    lead_updates['x_dl_exp_date'] = dateutil.parser.parse(extracted_data['dl_exp_date']).date().strftime('%Y-%m-%d')
                except:
                    pass
                    
            # Address & Housing
            if extracted_data.get('address'):
                lead_updates['x_address'] = extracted_data['address']
            if extracted_data.get('city'):
                lead_updates['x_city'] = extracted_data['city']
            if extracted_data.get('state'):
                lead_updates['x_state'] = extracted_data['state']
            if extracted_data.get('zip'):
                lead_updates['x_zip'] = extracted_data['zip']
            if extracted_data.get('residence_type'):
                res_type = extracted_data['residence_type'].lower()
                # Map residence types to Odoo's allowed values
                if 'own' in res_type:
                    lead_updates['x_residence_type'] = 'own'
                elif 'rent' in res_type:
                    lead_updates['x_residence_type'] = 'rent'
                else:  # "Living with Family" or any other value
                    lead_updates['x_residence_type'] = 'other'
            if extracted_data.get('time_at_residence'):
                lead_updates['x_time_at_residence'] = extracted_data['time_at_residence']
            if extracted_data.get('rent_mortgage'):
                try:
                    # Clean currency and convert to float
                    rent_amount = str(extracted_data['rent_mortgage']).replace('$', '').replace(',', '')
                    lead_updates['x_rent_mortgage'] = float(rent_amount)
                except:
                    pass
                    
            # Contact Information
            if extracted_data.get('phone'):
                lead_updates['x_home_phone'] = extracted_data['phone']
            if extracted_data.get('mobile_phone'):
                lead_updates['x_mobile_phone'] = extracted_data['mobile_phone']
            if extracted_data.get('email'):
                lead_updates['x_email_address'] = extracted_data['email']
                
            # Employment Information
            if extracted_data.get('applicant_employer'):
                lead_updates['x_employer'] = extracted_data['applicant_employer']
            if extracted_data.get('applicant_employer_address'):
                lead_updates['x_employer_address'] = extracted_data['applicant_employer_address']
            if extracted_data.get('applicant_employer_city'):
                lead_updates['x_employer_city'] = extracted_data['applicant_employer_city']
            # Income handling - store all types and calculate best
            if extracted_data.get('applicant_monthly_income'):
                try:
                    # Clean currency and convert to float
                    income = str(extracted_data['applicant_monthly_income']).replace('$', '').replace(',', '')
                    lead_updates['x_monthly_income'] = float(income)
                except:
                    pass

            # YTD Income fields
            if extracted_data.get('applicant_ytd_income'):
                try:
                    ytd = str(extracted_data['applicant_ytd_income']).replace('$', '').replace(',', '')
                    lead_updates['x_ytd_income'] = float(ytd)
                except:
                    pass
            if extracted_data.get('applicant_ytd_pay_periods'):
                try:
                    lead_updates['x_ytd_pay_periods'] = float(extracted_data['applicant_ytd_pay_periods'])
                except:
                    pass

            # Hourly wage fields (already exist but ensuring they're captured)
            if extracted_data.get('applicant_hours_per_week'):
                try:
                    lead_updates['x_hours_per_week'] = float(extracted_data['applicant_hours_per_week'])
                except:
                    pass
            if extracted_data.get('applicant_hourly_pay'):
                try:
                    hourly = str(extracted_data['applicant_hourly_pay']).replace('$', '').replace(',', '')
                    lead_updates['x_hourly_pay'] = float(hourly)
                except:
                    pass
            if extracted_data.get('applicant_occupation'):
                lead_updates['x_occupation'] = extracted_data['applicant_occupation']
            if extracted_data.get('applicant_work_phone'):
                lead_updates['x_work_phone'] = extracted_data['applicant_work_phone']
            if extracted_data.get('applicant_time_on_job'):
                lead_updates['x_time_on_job'] = extracted_data['applicant_time_on_job']
            
            # ===== CO-APPLICANT STRUCTURED FIELDS =====
            # Personal Information
            if extracted_data.get('co_applicant_first_name'):
                lead_updates['x_co_applicant_first_name'] = extracted_data['co_applicant_first_name']
            if extracted_data.get('co_applicant_middle_name'):
                lead_updates['x_co_applicant_middle_name'] = extracted_data['co_applicant_middle_name']
            if extracted_data.get('co_applicant_last_name'):
                lead_updates['x_co_applicant_last_name'] = extracted_data['co_applicant_last_name']
            if extracted_data.get('co_applicant_ssn'):
                lead_updates['x_co_applicant_ssn'] = extracted_data['co_applicant_ssn']
            if extracted_data.get('co_applicant_dob'):
                try:
                    lead_updates['x_co_applicant_dob'] = dateutil.parser.parse(extracted_data['co_applicant_dob']).date().strftime('%Y-%m-%d')
                except:
                    pass
            if extracted_data.get('co_applicant_dl_number'):
                lead_updates['x_co_applicant_dl_number'] = extracted_data['co_applicant_dl_number']
            if extracted_data.get('co_applicant_dl_state'):
                lead_updates['x_co_applicant_dl_state'] = extracted_data['co_applicant_dl_state']
            if extracted_data.get('co_applicant_dl_exp_date'):
                try:
                    lead_updates['x_co_applicant_dl_exp_date'] = dateutil.parser.parse(extracted_data['co_applicant_dl_exp_date']).date().strftime('%Y-%m-%d')
                except:
                    pass
                    
            # Address & Housing
            if extracted_data.get('co_applicant_address'):
                lead_updates['x_co_applicant_address'] = extracted_data['co_applicant_address']
            if extracted_data.get('co_applicant_city'):
                lead_updates['x_co_applicant_city'] = extracted_data['co_applicant_city']
            if extracted_data.get('co_applicant_state'):
                lead_updates['x_co_applicant_state'] = extracted_data['co_applicant_state']
            if extracted_data.get('co_applicant_zip'):
                lead_updates['x_co_applicant_zip'] = extracted_data['co_applicant_zip']
            if extracted_data.get('co_applicant_residence_type'):
                res_type = extracted_data['co_applicant_residence_type'].lower()
                # Map residence types to Odoo's allowed values
                if 'own' in res_type:
                    lead_updates['x_co_applicant_residence_type'] = 'own'
                elif 'rent' in res_type:
                    lead_updates['x_co_applicant_residence_type'] = 'rent'
                else:  # "Living with Family" or any other value
                    lead_updates['x_co_applicant_residence_type'] = 'other'
            if extracted_data.get('co_applicant_time_at_residence'):
                lead_updates['x_co_applicant_time_at_residence'] = extracted_data['co_applicant_time_at_residence']
            if extracted_data.get('co_applicant_rent_mortgage'):
                try:
                    rent_amount = str(extracted_data['co_applicant_rent_mortgage']).replace('$', '').replace(',', '')
                    lead_updates['x_co_applicant_rent_mortgage'] = float(rent_amount)
                except:
                    pass
                    
            # Contact Information
            if extracted_data.get('co_applicant_home_phone'):
                lead_updates['x_co_applicant_home_phone'] = extracted_data['co_applicant_home_phone']
            if extracted_data.get('co_applicant_mobile_phone'):
                lead_updates['x_co_applicant_mobile_phone'] = extracted_data['co_applicant_mobile_phone']
            if extracted_data.get('co_applicant_email'):
                lead_updates['x_co_applicant_email'] = extracted_data['co_applicant_email']
                
            # Employment Information
            if extracted_data.get('co_applicant_employer'):
                lead_updates['x_co_applicant_employer'] = extracted_data['co_applicant_employer']
            if extracted_data.get('co_applicant_employer_address'):
                lead_updates['x_co_applicant_employer_address'] = extracted_data['co_applicant_employer_address']
            if extracted_data.get('co_applicant_employer_city'):
                lead_updates['x_co_applicant_employer_city'] = extracted_data['co_applicant_employer_city']
            # Co-applicant income handling - store all types
            if extracted_data.get('co_applicant_monthly_income'):
                try:
                    income = str(extracted_data['co_applicant_monthly_income']).replace('$', '').replace(',', '')
                    lead_updates['x_co_applicant_monthly_income'] = float(income)
                except:
                    pass

            # Co-applicant YTD Income fields
            if extracted_data.get('co_applicant_ytd_income'):
                try:
                    ytd = str(extracted_data['co_applicant_ytd_income']).replace('$', '').replace(',', '')
                    lead_updates['x_co_applicant_ytd_income'] = float(ytd)
                except:
                    pass
            if extracted_data.get('co_applicant_ytd_pay_periods'):
                try:
                    lead_updates['x_co_applicant_ytd_pay_periods'] = float(extracted_data['co_applicant_ytd_pay_periods'])
                except:
                    pass

            # Co-applicant hourly wage fields (already exist but ensuring they're captured)
            if extracted_data.get('co_applicant_hours_per_week'):
                try:
                    lead_updates['x_co_applicant_hours_per_week'] = float(extracted_data['co_applicant_hours_per_week'])
                except:
                    pass
            if extracted_data.get('co_applicant_hourly_pay'):
                try:
                    hourly = str(extracted_data['co_applicant_hourly_pay']).replace('$', '').replace(',', '')
                    lead_updates['x_co_applicant_hourly_pay'] = float(hourly)
                except:
                    pass
            if extracted_data.get('co_applicant_occupation'):
                lead_updates['x_co_applicant_occupation'] = extracted_data['co_applicant_occupation']
            if extracted_data.get('co_applicant_work_phone'):
                lead_updates['x_co_applicant_work_phone'] = extracted_data['co_applicant_work_phone']
            if extracted_data.get('co_applicant_time_on_job'):
                lead_updates['x_co_applicant_time_on_job'] = extracted_data['co_applicant_time_on_job']
            
            # ===== VEHICLE STRUCTURED FIELDS (existing + enhanced) =====
            if extracted_data.get('vehicle_year'):
                lead_updates['x_vehicle_year'] = str(extracted_data['vehicle_year'])
            if extracted_data.get('vehicle_make'):
                lead_updates['x_vehicle_make'] = extracted_data['vehicle_make']
            if extracted_data.get('vehicle_model'):
                lead_updates['x_vehicle_model'] = extracted_data['vehicle_model']
            if extracted_data.get('vehicle_vin'):
                lead_updates['x_vehicle_vin'] = extracted_data['vehicle_vin']
            if extracted_data.get('vehicle_mileage'):
                lead_updates['x_vehicle_mileage'] = str(extracted_data['vehicle_mileage'])
            if extracted_data.get('sale_price'):
                try:
                    # Use enhanced sale price field
                    price = str(extracted_data['sale_price']).replace('$', '').replace(',', '')
                    lead_updates['x_vehicle_sale_price'] = float(price)
                except:
                    pass
            
            # Preserve original customer notes/comments if they exist, otherwise create summary
            original_description = ""
            if 'original_description' in extracted_data and extracted_data['original_description']:
                original_description = extracted_data['original_description']
            elif 'customer_comments' in extracted_data and extracted_data['customer_comments']:
                original_description = extracted_data['customer_comments']
            elif 'comments' in extracted_data and extracted_data['comments']:
                original_description = extracted_data['comments']
            
            # If no original description, create a basic summary
            if not original_description:
                description_parts = ["Credit Application Processed"]
                if extracted_data.get('vehicle_year') and extracted_data.get('vehicle_make') and extracted_data.get('vehicle_model'):
                    vehicle_summary = f"{extracted_data['vehicle_year']} {extracted_data['vehicle_make']} {extracted_data['vehicle_model']}"
                    description_parts.append(f"Vehicle: {vehicle_summary}")
                original_description = '\n'.join(description_parts)
                
            # Update lead name to vehicle info for credit apps
            if extracted_data.get('vehicle_year') and extracted_data.get('vehicle_make') and extracted_data.get('vehicle_model'):
                lead_updates['name'] = f"{extracted_data['vehicle_year']} {extracted_data['vehicle_make']} {extracted_data['vehicle_model']}"
            
            lead_updates['description'] = original_description
            
            # Update the lead
            if lead_updates:
                self.models.execute_kw(
                    self.database, self.uid, self.password,
                    'crm.lead', 'write',
                    [[lead_id], lead_updates]
                )
                
                logger.info(f"Updated lead {lead_id} with {len(lead_updates)} structured fields: {list(lead_updates.keys())}")
                return True
            else:
                logger.info(f"No data to update for lead {lead_id}")
                return True
                
        except Exception as e:
            logger.error(f"Error updating lead data: {str(e)}")
            return False


class OdooConfig:
    """Manage Odoo configuration settings"""
    
    def __init__(self, config_file: str = ODOO_CONFIG_FILE):
        self.config_file = config_file
        self.settings = self.load_config()
    
    def load_config(self) -> dict:
        """Load configuration from file"""
        try:
            if os.path.exists(self.config_file):
                with open(self.config_file, 'r') as f:
                    return json.load(f)
            return {}
        except Exception as e:
            logger.error(f"Error loading Odoo config: {str(e)}")
            return {}
    
    def save_config(self) -> bool:
        """Save current configuration to file"""
        try:
            with open(self.config_file, 'w') as f:
                json.dump(self.settings, f, indent=2)
            logger.info("Odoo configuration saved successfully")
            return True
        except Exception as e:
            logger.error(f"Error saving Odoo config: {str(e)}")
            return False
    
    def update_settings(self, **kwargs) -> bool:
        """Update configuration settings"""
        self.settings.update(kwargs)
        return self.save_config()
    
    def get_client(self) -> Optional[OdooClient]:
        """Get connected Odoo client if configuration is valid"""
        if not self.is_configured():
            return None
        
        try:
            client = OdooClient(
                url=self.settings['url'],
                database=self.settings['database'],
                username=self.settings['username'],
                password=self.settings['password']
            )
            
            if client.connect():
                return client
            return None
            
        except Exception as e:
            logger.error(f"Error creating Odoo client: {str(e)}")
            return None
    
    def is_configured(self) -> bool:
        """Check if all required settings are present"""
        required = ['url', 'database', 'username', 'password']
        return all(key in self.settings and self.settings[key] for key in required)
    
    def test_connection(self) -> dict:
        """Test Odoo connection and return status"""
        if not self.is_configured():
            return {
                'success': False,
                'message': 'Odoo not configured - missing required settings'
            }
        
        try:
            client = OdooClient(
                url=self.settings['url'],
                database=self.settings['database'],
                username=self.settings['username'],
                password=self.settings['password']
            )
            
            if client.connect():
                return {
                    'success': True,
                    'message': 'Successfully connected to Odoo'
                }
            else:
                return {
                    'success': False,
                    'message': 'Failed to connect - check credentials and URL'
                }
                
        except Exception as e:
            return {
                'success': False,
                'message': f'Connection error: {str(e)}'
            }


# Global configuration instance
odoo_config = OdooConfig()

# Lifespan event handler for FastAPI
from contextlib import asynccontextmanager

@asynccontextmanager
async def lifespan(app):
    """Initialize processor on startup"""
    success = initialize_processor()
    if not success:
        logger.error("Failed to start API server - processor initialization failed")
    else:
        logger.info("DCS Credit Application API Server started successfully")
    yield
    # Cleanup code would go here if needed

# FastAPI app setup
app = FastAPI(
    title="DCS Credit Application API",
    description="Standalone API server for processing credit applications from Chrome extension",
    version="1.0.0",
    lifespan=lifespan
)

# Enable CORS for Chrome extension
app.add_middleware(
    CORSMiddleware,
    allow_origins=["chrome-extension://*", "http://localhost:*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Pydantic models for request/response
class ApplicationData(BaseModel):
    """Data model for credit application information"""
    # Accept any fields dynamically from the extension
    class Config:
        extra = "allow"

class ProcessRequest(BaseModel):
    """Request model for processing credit applications"""
    data: Dict[str, Any]  # Accept any data structure
    pageHtml: Optional[Dict[str, Any]] = None  # HTML content for PDF generation
    timestamp: str
    source_url: Optional[str] = None
    pdf_filename: Optional[str] = None  # Original DCS PDF filename for reference
    auto_print: bool = True  # Whether to auto-print the DCS PDF
    auto_open: bool = True  # Whether to auto-open in Adobe Acrobat
    customer_docs_path: Optional[str] = None  # Override customer docs path
    odoo_sync: bool = True  # Whether to create Odoo lead

class ProcessResponse(BaseModel):
    """Response model for processing results"""
    success: bool
    message: str
    filename: Optional[str] = None
    customer_folder: Optional[str] = None
    customer_name: Optional[str] = None
    fields_processed: int
    json_exported: bool = False
    odoo_lead_id: Optional[int] = None  # Odoo lead ID if created

# Odoo-related models
class OdooConfigRequest(BaseModel):
    """Request model for Odoo configuration"""
    url: str
    database: str
    username: str
    password: Optional[str] = None
    enable_sync: bool = True
    tag_name: str = "synced"

class LeadSearchRequest(BaseModel):
    """Request model for lead search"""
    name: str
    phone: str = ""

class SyncToOdooRequest(BaseModel):
    """Request model for syncing data to Odoo"""
    lead_id: int
    customer_name: str
    pdf_path: str
    dcs_pdf_path: Optional[str] = None
    json_path: str
    extracted_data: Optional[Dict[str, Any]] = None

class StandaloneProcessor:
    """Simplified processor for API use"""
    
    def __init__(self, export_folder, customer_docs_base=None):
        self.export_folder = Path(export_folder)
        self.customer_docs_base = Path(customer_docs_base) if customer_docs_base else None
        
        # Create directories
        self.export_folder.mkdir(parents=True, exist_ok=True)
        if self.customer_docs_base:
            self.customer_docs_base.mkdir(parents=True, exist_ok=True)
    
    def generate_customer_name(self, data):
        """Generate customer name from extracted data"""
        first_name = (data.get('first_name') or '').strip()
        last_name = (data.get('last_name') or '').strip()
        
        # Clean up any invalid characters for file/folder names
        def clean_name(name):
            if not name:
                return ""
            # Remove invalid Windows filename characters
            invalid_chars = '<>:"/\\|?*'
            for char in invalid_chars:
                name = name.replace(char, '')
            return name.strip()
        
        first_name = clean_name(first_name)
        last_name = clean_name(last_name)
        
        if first_name and last_name:
            return f"{first_name} {last_name}"
        elif first_name:
            return first_name
        elif last_name:
            return last_name
        else:
            return "Unknown Customer"
    
    def fill_template_pdf(self, data, output_path):
        """Fill the AutoKing template PDF with extracted data"""
        try:
            # Try PyMuPDF first (same as working legacy system)
            logger.info("Attempting to fill AutoKing template with PyMuPDF...")
            success = self._fill_with_pymupdf(data, output_path)
            if success:
                return True
            
            # Fallback to PyPDF2 with enhanced error handling
            logger.info("PyMuPDF failed, trying PyPDF2...")
            return self._fill_with_pypdf2(data, output_path)
            
        except Exception as e:
            logger.error(f"Error filling template PDF: {e}")
            return False
    
    def _fill_with_pymupdf(self, data, output_path):
        """Fill PDF using PyMuPDF (primary method from legacy system)"""
        try:
            import fitz  # PyMuPDF
            logger.info("PyMuPDF imported successfully")
            
            template_path = Path(TEMPLATE_PATH)
            if not template_path.exists():
                logger.error(f"Template not found: {template_path}")
                return False
            
            # Open the PDF with PyMuPDF
            doc = fitz.open(str(template_path))
            page = doc[0]  # First page
            
            # Create field mapping
            field_mapping = self.map_data_to_fields(data)
            
            # Get form fields and fill them
            widgets = page.widgets()
            filled_count = 0
            
            for widget in widgets:
                field_name = widget.field_name
                
                # Debug: Log all field types
                logger.info(f"DEBUG: Field '{field_name}' has type {widget.field_type} (checkbox=2)")
                
                if field_name in field_mapping:
                    field_value = field_mapping[field_name]
                    
                    # Handle checkbox fields specially - they should only get "Yes", "On", or True
                    if widget.field_type == 2:  # PDF_WIDGET_TYPE_CHECKBOX = 2
                        # For checkboxes, set to True/checked if value contains "Yes" or similar
                        if isinstance(field_value, str) and ('yes' in field_value.lower() or 'on' in field_value.lower()):
                            widget.field_value = True
                        else:
                            widget.field_value = False
                        logger.info(f"*** CHECKBOX FIELD: {field_name} = {widget.field_value} (was: '{field_value}')")
                    elif widget.field_type == 7:  # Type 7 fields need special handling
                        # Type 7 fields are problematic - treat them as text but with size limits
                        text_value = str(field_value)
                        if len(text_value) > 50:  # Limit long values that might cause rendering issues
                            text_value = text_value[:50]
                            logger.info(f"Truncated long value for type 7 field: {field_name}")
                        widget.field_value = text_value
                        logger.info(f"Type 7 field: {field_name} = {text_value}")
                    else:
                        # For normal text fields, use the full value
                        widget.field_value = str(field_value)
                        logger.info(f"Text field (type {widget.field_type}): {field_name} = {field_value}")
                    
                    widget.update()
                    filled_count += 1
            
            logger.info(f"Filled {filled_count} fields using PyMuPDF")
            
            # Save the filled PDF
            pdf_path = output_path.with_suffix('.pdf')
            doc.save(str(pdf_path))
            doc.close()
            
            # Verify the file was created and has content
            if pdf_path.exists() and pdf_path.stat().st_size > 1000:
                logger.info(f"PDF created successfully with PyMuPDF: {pdf_path.stat().st_size} bytes")
                return True
            else:
                logger.error(f"PyMuPDF PDF creation failed or file too small")
                return False
            
        except ImportError:
            logger.warning("PyMuPDF not available, will try PyPDF2")
            return False
        except Exception as e:
            logger.error(f"PyMuPDF method failed: {e}")
            return False
    
    def _fill_with_pypdf2(self, data, output_path):
        """Fill PDF using PyPDF2 (fallback method with enhanced error handling)"""
        try:
            from PyPDF2 import PdfReader, PdfWriter
            logger.info("PyPDF2 imported successfully")
            
            template_path = Path(TEMPLATE_PATH)
            if not template_path.exists():
                logger.error(f"Template not found: {template_path}")
                return False
            
            # Read the template
            reader = PdfReader(template_path)
            writer = PdfWriter()
            
            logger.info(f"Template loaded successfully, pages: {len(reader.pages)}")
            
            # Create field mapping from source data to target form fields
            field_mapping = self.map_data_to_fields(data)
            logger.info(f"Field mapping created with {len(field_mapping)} fields")
            
            # Check if PDF has form fields
            if '/AcroForm' in reader.trailer['/Root']:
                logger.info("PDF has AcroForm - attempting form filling")
                
                # Try the modern PyPDF2 approach (same as legacy)
                try:
                    for page in reader.pages:
                        writer.add_page(page)
                    
                    # Update form field values
                    writer.update_page_form_field_values(writer.pages[0], field_mapping)
                    
                    # CRITICAL: Flatten the form to make fields visible in all PDF viewers
                    if hasattr(writer, 'flatten'):
                        writer.flatten()
                        logger.info("Form flattened successfully")
                    else:
                        logger.warning("PyPDF2 version doesn't support flatten() - installing newer PyPDF2 recommended")
                    
                    logger.info("Form fields updated successfully with PyPDF2 method")
                    
                except Exception as modern_error:
                    logger.warning(f"PyPDF2 method failed: {modern_error}")
                    return False
            else:
                logger.error("Template PDF has no form fields")
                return False
            
            # Write the filled PDF
            pdf_path = output_path.with_suffix('.pdf')
            with open(pdf_path, 'wb') as output_file:
                writer.write(output_file)
            
            logger.info(f"PyPDF2 filled template PDF: {pdf_path}")
            logger.info(f"Mapped {len(field_mapping)} fields to template")
            
            # Verify the file was created and has content
            if pdf_path.exists() and pdf_path.stat().st_size > 1000:
                logger.info(f"PDF created successfully with PyPDF2: {pdf_path.stat().st_size} bytes")
                return True
            else:
                logger.error(f"PyPDF2 PDF creation failed or file too small")
                return False
                
        except ImportError:
            logger.error("PyPDF2 not available for PDF form filling")
            return False
        except Exception as e:
            logger.error(f"PyPDF2 error filling template PDF: {e}")
            return False
    
    def map_data_to_fields(self, data):
        """Map extracted data to AutoKing template field names"""
        # Normalize the data first - convert extension field names to legacy field names
        normalized_data = self.normalize_extension_data(data)
        
        # Use the proven field mappings from the working legacy system
        mapping = {}
        
        # APPLICANT INFORMATION
        if 'applicant_first_name' in normalized_data:
            mapping['Buyer First Name'] = normalized_data['applicant_first_name']
            mapping['First Name'] = normalized_data['applicant_first_name']
        if 'applicant_middle_name' in normalized_data:
            mapping['Buyer Middle Initial'] = normalized_data['applicant_middle_name']
            mapping['Middle Initial'] = normalized_data['applicant_middle_name']
        if 'applicant_last_name' in normalized_data:
            mapping['Buyer Last Name'] = normalized_data['applicant_last_name']
            mapping['Last Name'] = normalized_data['applicant_last_name']
        if 'applicant_dob' in normalized_data:
            mapping['Date of birth'] = normalized_data['applicant_dob']
        if 'applicant_ssn' in normalized_data:
            # Format SSN with dashes: 556322546 -> 556-32-2546
            ssn = normalized_data['applicant_ssn']
            if len(str(ssn)) == 9:
                formatted_ssn = f"{ssn[:3]}-{ssn[3:5]}-{ssn[5:]}"
                mapping['SSN'] = formatted_ssn
            else:
                mapping['SSN'] = ssn
        
        # Home phone mapping
        if 'applicant_home_phone' in normalized_data:
            mapping['Home Phone'] = normalized_data['applicant_home_phone']
        
        if 'applicant_mobile_phone' in normalized_data:
            # Format phone: 7154978572 -> (715) 497-8572
            phone = str(normalized_data['applicant_mobile_phone'])
            if len(phone) == 10:
                formatted_phone = f"({phone[:3]}) {phone[3:6]}-{phone[6:]}"
                mapping['Cell Phone'] = formatted_phone
            else:
                mapping['Cell Phone'] = phone
        
        # Combine address and address 2 into one field
        address_parts = []
        if 'applicant_address' in normalized_data:
            address_parts.append(normalized_data['applicant_address'])
        if 'applicant_address2' in normalized_data:
            address_parts.append(normalized_data['applicant_address2'])

        if address_parts:
            mapping['Current Address'] = ', '.join(address_parts)
        
        if 'applicant_city' in normalized_data:
            mapping['City'] = normalized_data['applicant_city']
        if 'applicant_state' in normalized_data:
            mapping['State'] = normalized_data['applicant_state']
        if 'applicant_zip' in normalized_data:
            mapping['ZIP Code'] = normalized_data['applicant_zip']
        if 'applicant_email' in normalized_data:
            mapping['Email Address'] = normalized_data['applicant_email']
        if 'applicant_rent_mortgage' in normalized_data:
            # Format currency: remove $ if present
            rent = str(normalized_data['applicant_rent_mortgage']).replace('$', '').replace(',', '')
            mapping['MortgageRent Amount'] = rent
        
        # Handle residence type including "Living with Family"
        if 'applicant_residence_type' in normalized_data:
            res_type = normalized_data['applicant_residence_type'].strip()
            if res_type.lower() == 'own':
                mapping['Own 1'] = 'Yes'
            elif res_type.lower() == 'rent':
                mapping['Rent 1'] = 'Yes'
            elif 'living with family' in res_type.lower():
                mapping['Landlord Name'] = res_type
        
        # Handle time at residence - parse into years and months
        if 'applicant_time_at_residence' in normalized_data:
            years, months = self.parse_time_period(normalized_data['applicant_time_at_residence'])
            if years:
                mapping['undefined'] = years
            if months:
                mapping['undefined_2'] = months

        # EMPLOYMENT INFORMATION
        if 'applicant_employer' in normalized_data:
            mapping['Current employer'] = normalized_data['applicant_employer']
        # Calculate best income (YTD > hourly calc > stated income)
        best_income = calculate_best_income(normalized_data, 'applicant')
        if best_income > 0:
            mapping['Income'] = str(int(round(best_income)))
        if 'applicant_occupation' in normalized_data:
            mapping['Position'] = normalized_data['applicant_occupation']
        # Handle employer address - ALWAYS combine street address with city/state/zip when available
        employer_address_parts = []
        if 'applicant_employer_address' in normalized_data:
            employer_address_parts.append(normalized_data['applicant_employer_address'])
        
        # Add city/state/zip - check for combined city field first, then individual components
        if 'applicant_employer_city' in normalized_data:
            city_data = normalized_data['applicant_employer_city']
            # Check if this already contains state and zip (like "Barron WI, 54812")  
            if any(char.isdigit() for char in city_data) and len(city_data) > 5:
                employer_address_parts.append(city_data)
            else:
                # Just city name, add separate state and zip if available
                city_state_zip_parts = [city_data]
                if 'applicant_employer_state' in normalized_data:
                    city_state_zip_parts.append(normalized_data['applicant_employer_state'])
                if 'applicant_employer_zip' in normalized_data:
                    city_state_zip_parts.append(normalized_data['applicant_employer_zip'])
                employer_address_parts.append(', '.join(city_state_zip_parts))
        else:
            # No combined city field, build from individual components
            city_state_zip_parts = []
            if 'applicant_employer_state' in normalized_data:
                city_state_zip_parts.append(normalized_data['applicant_employer_state'])
            if 'applicant_employer_zip' in normalized_data:
                city_state_zip_parts.append(normalized_data['applicant_employer_zip'])
            if city_state_zip_parts:
                employer_address_parts.append(', '.join(city_state_zip_parts))
        
        if employer_address_parts:
            final_employer_address = ', '.join(employer_address_parts)
            mapping['Employer address incl City State ZIP'] = final_employer_address
            print(f"[DEBUG] Combined employer address: '{final_employer_address}'")
        if 'applicant_work_phone' in normalized_data:
            # Format work phone
            phone = str(normalized_data['applicant_work_phone'])
            if len(phone) == 10:
                formatted_phone = f"({phone[:3]}) {phone[3:6]}-{phone[6:]}"
                mapping['Employer Phone'] = formatted_phone
            else:
                mapping['Employer Phone'] = phone
        
        # Parse applicant time on job into years and months
        if 'applicant_time_on_job' in normalized_data:
            years, months = self.parse_time_period(normalized_data['applicant_time_on_job'])
            if years:
                mapping['YR'] = years
            if months:
                mapping['MO'] = months
        
        # CO-APPLICANT INFORMATION
        if 'co_applicant_first_name' in normalized_data:
            mapping['Co-buyer first name'] = normalized_data['co_applicant_first_name']
            mapping['First Name_2'] = normalized_data['co_applicant_first_name']
        if 'co_applicant_middle_name' in normalized_data:
            mapping['Co-buyer middle initial'] = normalized_data['co_applicant_middle_name']
            mapping['Co-Buyer Middle Initial'] = normalized_data['co_applicant_middle_name']
            mapping['Co-Applicant Middle Initial'] = normalized_data['co_applicant_middle_name']
        if 'co_applicant_last_name' in normalized_data:
            mapping['Co-Buyer Last Name'] = normalized_data['co_applicant_last_name']
            mapping['Last Name_2'] = normalized_data['co_applicant_last_name']
        if 'co_applicant_dob' in normalized_data:
            mapping['Date of birth_2'] = normalized_data['co_applicant_dob']
        if 'co_applicant_ssn' in normalized_data:
            # Format co-applicant SSN
            ssn = str(normalized_data['co_applicant_ssn'])
            if len(ssn) == 9:
                formatted_ssn = f"{ssn[:3]}-{ssn[3:5]}-{ssn[5:]}"
                mapping['SSN_2'] = formatted_ssn
            else:
                mapping['SSN_2'] = ssn
        
        # Co-applicant home phone mapping
        if 'co_applicant_home_phone' in normalized_data:
            mapping['Home Phone_2'] = normalized_data['co_applicant_home_phone']
        
        if 'co_applicant_mobile_phone' in normalized_data:
            # Format co-applicant phone
            phone = str(normalized_data['co_applicant_mobile_phone'])
            if len(phone) == 10:
                formatted_phone = f"({phone[:3]}) {phone[3:6]}-{phone[6:]}"
                mapping['Cell Phone_2'] = formatted_phone
            else:
                mapping['Cell Phone_2'] = phone
        
        # Combine co-applicant address and address 2 into one field
        co_address_parts = []
        if 'co_applicant_address' in normalized_data:
            co_address_parts.append(normalized_data['co_applicant_address'])
        if 'co_applicant_address2' in normalized_data:
            co_address_parts.append(normalized_data['co_applicant_address2'])

        if co_address_parts:
            mapping['Current address'] = ', '.join(co_address_parts)
        
        if 'co_applicant_city' in normalized_data:
            mapping['City_2'] = normalized_data['co_applicant_city']
        if 'co_applicant_state' in normalized_data:
            mapping['State_2'] = normalized_data['co_applicant_state']
        if 'co_applicant_zip' in normalized_data:
            mapping['ZIP Code_2'] = normalized_data['co_applicant_zip']
        if 'co_applicant_email' in normalized_data:
            mapping['Email Address_2'] = normalized_data['co_applicant_email']
        if 'co_applicant_rent_mortgage' in normalized_data:
            rent = str(normalized_data['co_applicant_rent_mortgage']).replace('$', '').replace(',', '')
            mapping['MortgageRent Amount_2'] = rent
        
        # Handle co-applicant residence type including "Living with Family"
        if 'co_applicant_residence_type' in normalized_data:
            res_type = normalized_data['co_applicant_residence_type'].strip()
            if res_type.lower() == 'own':
                mapping['Own 2'] = 'Yes'
            elif res_type.lower() == 'rent':
                mapping['Rent 2'] = 'Yes'
            elif 'living with family' in res_type.lower():
                mapping['Landlord Name_2'] = res_type
        
        # Handle co-applicant time at residence
        if 'co_applicant_time_at_residence' in normalized_data:
            years, months = self.parse_time_period(normalized_data['co_applicant_time_at_residence'])
            if years:
                mapping['undefined_3'] = years
            if months:
                mapping['undefined_4'] = months

        # CO-APPLICANT EMPLOYMENT
        if 'co_applicant_employer' in normalized_data:
            mapping['Current employer_2'] = normalized_data['co_applicant_employer']
        # Calculate best co-applicant income (YTD > hourly calc > stated income)
        co_best_income = calculate_best_income(normalized_data, 'co_applicant')
        if co_best_income > 0:
            mapping['Income 2'] = str(int(round(co_best_income)))
        if 'co_applicant_occupation' in normalized_data:
            mapping['Position_3'] = normalized_data['co_applicant_occupation']
        if 'co_applicant_work_phone' in normalized_data:
            phone = str(normalized_data['co_applicant_work_phone'])
            if len(phone) == 10:
                formatted_phone = f"({phone[:3]}) {phone[3:6]}-{phone[6:]}"
                mapping['Employer phone 2'] = formatted_phone
            else:
                mapping['Employer phone 2'] = phone
        
        # Parse co-applicant time on job
        if 'co_applicant_time_on_job' in normalized_data:
            years, months = self.parse_time_period(normalized_data['co_applicant_time_on_job'])
            if years:
                mapping['YR_2'] = years
            if months:
                mapping['MO_2'] = months
        
        # Handle co-applicant employer address - ALWAYS combine street address with city/state/zip when available
        co_employer_address_parts = []
        if 'co_applicant_employer_address' in normalized_data:
            co_employer_address_parts.append(normalized_data['co_applicant_employer_address'])
        
        # Add city/state/zip - check for combined city field first, then individual components
        if 'co_applicant_employer_city' in normalized_data:
            city_data = normalized_data['co_applicant_employer_city']
            # Check if this already contains state and zip (like "Barron WI, 54812")  
            if any(char.isdigit() for char in city_data) and len(city_data) > 5:
                co_employer_address_parts.append(city_data)
            else:
                # Just city name, add separate state and zip if available
                city_state_zip_parts = [city_data]
                if 'co_applicant_employer_state' in normalized_data:
                    city_state_zip_parts.append(normalized_data['co_applicant_employer_state'])
                if 'co_applicant_employer_zip' in normalized_data:
                    city_state_zip_parts.append(normalized_data['co_applicant_employer_zip'])
                co_employer_address_parts.append(', '.join(city_state_zip_parts))
        else:
            # No combined city field, build from individual components
            city_state_zip_parts = []
            if 'co_applicant_employer_state' in normalized_data:
                city_state_zip_parts.append(normalized_data['co_applicant_employer_state'])
            if 'co_applicant_employer_zip' in normalized_data:
                city_state_zip_parts.append(normalized_data['co_applicant_employer_zip'])
            if city_state_zip_parts:
                co_employer_address_parts.append(', '.join(city_state_zip_parts))
        
        if co_employer_address_parts:
            final_co_employer_address = ', '.join(co_employer_address_parts)
            mapping['Employer address incl City State ZIP_2'] = final_co_employer_address
            print(f"[DEBUG] Combined co-applicant employer address: '{final_co_employer_address}'")

        # VEHICLE INFORMATION
        if 'vehicle_year' in normalized_data:
            mapping['Year'] = normalized_data['vehicle_year']
        if 'vehicle_make' in normalized_data:
            mapping['Make'] = normalized_data['vehicle_make']
        if 'vehicle_model' in normalized_data:
            mapping['Model'] = normalized_data['vehicle_model']
        if 'vehicle_vin' in normalized_data:
            mapping['VIN'] = normalized_data['vehicle_vin']
        if 'vehicle_mileage' in normalized_data:
            # Format odometer with commas
            mileage = self.format_mileage(normalized_data['vehicle_mileage'])
            mapping['Mileage'] = mileage
        if 'vehicle_odometer' in normalized_data:
            # Format odometer with commas  
            mileage = self.format_mileage(normalized_data['vehicle_odometer'])
            mapping['Mileage'] = mileage
        
        # Vehicle sales price to correct field
        if 'vehicle_sales_price' in normalized_data:
            price = str(normalized_data['vehicle_sales_price']).replace(',', '')
            mapping['fill_62'] = price
        if 'sale_price' in normalized_data:
            price = str(normalized_data['sale_price']).replace(',', '')
            mapping['fill_62'] = price
        
        logger.info("=== NORMALIZED DATA DEBUG ===")
        for key, value in normalized_data.items():
            if 'employer' in key.lower():
                logger.info(f"NORMALIZED: '{key}' = '{value}'")
        logger.info("=== FIELD MAPPING DEBUG ===")
        for key, value in mapping.items():
            if 'Employer' in key:
                logger.info(f"MAPPING: '{key}' = '{value}'")
        logger.info("=== END DEBUG ===")
        
        # Clean checkbox field values - remove dates and extra text
        cleaned_mapping = {}
        for field_name, field_value in mapping.items():
            if isinstance(field_value, str):
                # Check if this is a checkbox field (typically contains "Yes" with extra text)
                if 'yes' in field_value.lower() and ('2025' in field_value or '/' in field_value):
                    # This is likely a checkbox with date - just use "Yes"
                    cleaned_mapping[field_name] = 'Yes'
                    logger.info(f"Cleaned checkbox field: {field_name} = 'Yes' (was: '{field_value}')")
                # Also check for acknowledgment/consent fields that might be problematic
                elif any(keyword in field_name.lower() for keyword in ['acknowledgment', 'consent', 'check', 'box']):
                    # If this looks like a checkbox field, clean the value
                    if 'yes' in field_value.lower():
                        cleaned_mapping[field_name] = 'Yes'
                        logger.info(f"Cleaned acknowledgment field: {field_name} = 'Yes' (was: '{field_value}')")
                    else:
                        cleaned_mapping[field_name] = field_value
                else:
                    cleaned_mapping[field_name] = field_value
            else:
                cleaned_mapping[field_name] = field_value
        
        return cleaned_mapping
    
    def normalize_extension_data(self, data):
        """Convert Chrome extension field names to legacy system field names"""
        normalized = {}
        
        # Map extension field names to legacy field names
        field_name_mapping = {
            # Applicant information
            'first_name': 'applicant_first_name',
            'middle_name': 'applicant_middle_name',
            'last_name': 'applicant_last_name', 
            'address': 'applicant_address',
            'address2': 'applicant_address2',
            'city': 'applicant_city',
            'state': 'applicant_state',
            'zip': 'applicant_zip',
            'email': 'applicant_email',
            'mobile_phone': 'applicant_mobile_phone',
            'home_phone': 'applicant_home_phone',
            'phone': 'applicant_mobile_phone',  # fallback
            'ssn': 'applicant_ssn',
            'date_of_birth': 'applicant_dob',
            'drivers_license': 'applicant_drivers_license', 
            'dl_state': 'applicant_dl_state',
            'dl_exp_date': 'applicant_dl_exp_date',
            'time_at_residence': 'applicant_time_at_residence',
            'rent_mortgage': 'applicant_rent_mortgage',
            'residence_type': 'applicant_residence_type',
            
            # Employment
            'employer': 'applicant_employer',
            'monthly_income': 'applicant_monthly_income', 
            'occupation': 'applicant_occupation',
            'work_phone': 'applicant_work_phone',
            'time_on_job': 'applicant_time_on_job',
            'employer_address': 'applicant_employer_address',
            'employer_city': 'applicant_employer_city',
            'employer_state': 'applicant_employer_state',
            'employer_zip': 'applicant_employer_zip',
            
            # Co-applicant information (extension may use co_ prefix)
            'co_first_name': 'co_applicant_first_name',
            'co_middle_name': 'co_applicant_middle_name',
            'co_last_name': 'co_applicant_last_name',
            'co_applicant_middle_name': 'co_applicant_middle_name',
            'co_address': 'co_applicant_address',
            'co_city': 'co_applicant_city',
            'co_state': 'co_applicant_state',
            'co_zip': 'co_applicant_zip',
            'co_email': 'co_applicant_email',
            'co_phone': 'co_applicant_mobile_phone',
            'co_ssn': 'co_applicant_ssn',
            'co_date_of_birth': 'co_applicant_dob',
            
            # Vehicle information
            'vehicle_year': 'vehicle_year',
            'vehicle_make': 'vehicle_make',
            'vehicle_model': 'vehicle_model',
            'vehicle_vin': 'vehicle_vin',
            'vehicle_mileage': 'vehicle_mileage',
            'sale_price': 'vehicle_sales_price',
            'down_payment': 'vehicle_down_payment'
        }
        
        # Apply mappings
        for original_key, value in data.items():
            if value:  # Only include non-empty values
                if original_key in field_name_mapping:
                    normalized[field_name_mapping[original_key]] = value
                else:
                    # Keep original key if no mapping found (might already be in legacy format)
                    normalized[original_key] = value
        
        return normalized
    
    def parse_time_period(self, time_text):
        """Helper function to parse time periods like '3 years, 2 months'"""
        import re
        if not time_text:
            return None, None
        
        years_match = re.search(r'(\d+)\s*years?', str(time_text), re.IGNORECASE)
        # Handle OCR errors like & instead of digits for months
        months_match = re.search(r'([&§\d]+)\s*months?', str(time_text), re.IGNORECASE)
        
        years = years_match.group(1) if years_match else "0"
        months = months_match.group(1) if months_match else "0"
        
        # Clean up OCR errors in months (& -> 8, § -> 5, common OCR mistakes)
        months = re.sub(r'&+', '8', months)
        months = re.sub(r'§+', '5', months)
        
        return years, months
    
    def format_mileage(self, mileage_str):
        """Format mileage with commas: 45678 -> 45,678"""
        import re
        if not mileage_str:
            return ""
        
        # Remove existing commas and any non-digits
        clean_mileage = re.sub(r'[^\d]', '', str(mileage_str))
        
        if clean_mileage and clean_mileage.isdigit():
            # Add commas for thousands
            return f"{int(clean_mileage):,}"
        else:
            return str(mileage_str)
    
    def generate_original_dcs_pdf(self, page_html_data, customer_name, customer_folder):
        """Generate PDF from DCS page HTML content"""
        try:
            if not page_html_data or not page_html_data.get('html'):
                logger.warning("No HTML content provided for DCS PDF generation")
                return None
                
            logger.info("Generating original DCS PDF from HTML content...")
            
            # Try to import weasyprint for HTML to PDF conversion
            try:
                import weasyprint
                logger.info("Using weasyprint for HTML to PDF conversion")
                
                # Create the PDF filename
                dcs_pdf_filename = f"DCS {customer_name} Credit App.pdf"
                dcs_pdf_path = Path(customer_folder) / dcs_pdf_filename
                
                # Convert HTML to PDF
                html_content = page_html_data['html']
                
                # Clean up the HTML for better PDF rendering
                cleaned_html = self.clean_html_for_pdf(html_content)
                
                # Generate PDF using weasyprint
                html_doc = weasyprint.HTML(string=cleaned_html, base_url=page_html_data.get('url', ''))
                html_doc.write_pdf(str(dcs_pdf_path))
                
                logger.info(f"Successfully generated DCS PDF: {dcs_pdf_path}")
                return dcs_pdf_path
                
            except ImportError:
                logger.warning("weasyprint not available, trying alternative method...")
                return self.generate_dcs_pdf_fallback(page_html_data, customer_name, customer_folder)
                
        except Exception as e:
            logger.error(f"Error generating DCS PDF: {e}")
            return None
    
    def clean_html_for_pdf(self, html_content):
        """Clean HTML content extensively for PDF generation to eliminate WeasyPrint warnings"""
        try:
            import re
            
            # Remove script tags and their content
            html_content = re.sub(r'<script[^>]*>.*?</script>', '', html_content, flags=re.DOTALL | re.IGNORECASE)
            
            # Remove ALL style tags and their content to eliminate CSS warnings
            html_content = re.sub(r'<style[^>]*>.*?</style>', '', html_content, flags=re.DOTALL | re.IGNORECASE)
            
            # Remove noscript tags
            html_content = re.sub(r'<noscript[^>]*>.*?</noscript>', '', html_content, flags=re.DOTALL | re.IGNORECASE)
            
            # Remove inline style attributes completely
            html_content = re.sub(r'\sstyle="[^"]*"', '', html_content)
            html_content = re.sub(r"\sstyle='[^']*'", '', html_content)
            
            # Remove CSS class attributes that reference removed stylesheets
            html_content = re.sub(r'\sclass="[^"]*"', '', html_content)
            html_content = re.sub(r"\sclass='[^']*'", '', html_content)
            
            # Remove problematic HTML attributes that cause warnings
            problematic_attrs = [
                'onclick', 'onload', 'onerror', 'onmouseover', 'onmouseout', 'onchange',
                'onfocus', 'onblur', 'onsubmit', 'onkeyup', 'onkeydown', 'onresize',
                'data-[^=]*', 'ng-[^=]*', 'v-[^=]*', 'x-[^=]*', 'role', 'aria-[^=]*'
            ]
            for attr in problematic_attrs:
                html_content = re.sub(f'\\s{attr}="[^"]*"', '', html_content, flags=re.IGNORECASE)
                html_content = re.sub(f"\\s{attr}='[^']*'", '', html_content, flags=re.IGNORECASE)
            
            # Remove problematic HTML elements that often cause CSS issues
            elements_to_remove = ['nav', 'aside', 'footer', 'header']
            for element in elements_to_remove:
                html_content = re.sub(f'<{element}[^>]*>.*?</{element}>', '', html_content, flags=re.DOTALL | re.IGNORECASE)
            
            # NUCLEAR OPTION: Remove ALL potential checkmark containers
            # Log what we're dealing with for debugging
            if 'Acknowledgment and Consent' in html_content:
                logger.info("Found acknowledgment section in HTML - removing all related elements")
            
            # Remove entire acknowledgment sections that contain checkmarks
            html_content = re.sub(r'Acknowledgment and Consent:.*?(?=<div|<p|$)', 'Acknowledgment and Consent: Yes', html_content, flags=re.DOTALL | re.IGNORECASE)
            
            # Remove ALL divs and spans (nuclear option for checkmarks)
            html_content = re.sub(r'<(div|span)[^>]*style[^>]*>.*?</\1>', '', html_content, flags=re.DOTALL | re.IGNORECASE)
            
            # Remove any remaining checkmark symbols
            html_content = re.sub(r'[✓✔☑☒✗✘]', '', html_content)
            
            # Remove any element with font-size in style
            html_content = re.sub(r'<[^>]*style[^>]*font-size[^>]*>.*?</[^>]*>', '', html_content, flags=re.DOTALL | re.IGNORECASE)
            
            # Remove link tags to external stylesheets
            html_content = re.sub(r'<link[^>]*rel=["\']stylesheet["\'][^>]*>', '', html_content, flags=re.IGNORECASE)
            
            # Remove meta tags that might cause issues (except charset)
            html_content = re.sub(r'<meta(?![^>]*charset)[^>]*>', '', html_content, flags=re.IGNORECASE)
            
            # Add comprehensive, clean CSS for PDF rendering
            clean_css = """
            <style>
                * { 
                    margin: 0; 
                    padding: 0; 
                    box-sizing: border-box;
                    font-family: Arial, sans-serif;
                }
                body { 
                    font-family: Arial, sans-serif; 
                    font-size: 12pt; 
                    line-height: 1.4; 
                    margin: 0.5in;
                    color: black;
                    background: white;
                }
                @page { 
                    size: letter; 
                    margin: 0.5in; 
                }
                table { 
                    border-collapse: collapse; 
                    width: 100%; 
                    margin: 10px 0;
                }
                th, td { 
                    border: 1px solid black; 
                    padding: 6px; 
                    text-align: left; 
                    font-size: 11pt;
                }
                th { 
                    background-color: #f0f0f0; 
                    font-weight: bold;
                }
                h1, h2, h3, h4, h5, h6 { 
                    color: black; 
                    margin: 10px 0;
                    font-weight: bold;
                }
                p { 
                    margin: 5px 0; 
                }
                div { 
                    margin: 2px 0; 
                }
                input, select, textarea {
                    border: 1px solid black;
                    padding: 2px;
                    font-size: 11pt;
                }
                /* Fix oversized checkmarks and form elements */
                input[type="checkbox"], input[type="radio"] {
                    width: 12px !important;
                    height: 12px !important;
                    font-size: 12px !important;
                    transform: scale(1) !important;
                    max-width: 16px !important;
                    max-height: 16px !important;
                }
                /* Override any icon fonts or Unicode symbols */
                .checkmark, .checkbox-icon, 
                input::before, input::after,
                [type="checkbox"]::before, [type="checkbox"]::after {
                    font-size: 12px !important;
                    max-width: 16px !important;
                    max-height: 16px !important;
                    width: 12px !important;
                    height: 12px !important;
                }
                /* Target common checkbox/checkmark classes and symbols */
                .check, .tick, .mark, .selected, .checked {
                    font-size: 12px !important;
                    max-width: 16px !important;
                    max-height: 16px !important;
                }
                /* Target only oversized checkmark elements */
                div[style*="font-size: 7"], 
                span[style*="font-size: 7"],
                div[style*="font-size:7"],
                span[style*="font-size:7"] {
                    font-size: 14px !important;
                    max-width: 20px !important;
                    max-height: 20px !important;
                    display: inline-block !important;
                }
                span[style*="font-size"], div[style*="font-size"] {
                    font-size: 12px !important;
                }
                /* Hide elements that shouldn't be printed */
                button, .btn, .button, .no-print, 
                [type="button"], [type="submit"],
                [onclick], [onload] { 
                    display: none; 
                }
                /* Ensure max width compliance and prevent oversized elements */
                * { 
                    max-width: 100%; 
                    color: black;
                    max-font-size: 14px !important;
                }
            </style>
            """
            
            # Insert the clean CSS - handle different HTML structures
            if '</head>' in html_content:
                html_content = html_content.replace('</head>', f'{clean_css}</head>')
            elif '<body>' in html_content:
                html_content = html_content.replace('<body>', f'<head>{clean_css}</head><body>')
            else:
                html_content = f'<html><head>{clean_css}</head><body>{html_content}</body></html>'
                
            return html_content
            
        except Exception as e:
            logger.error(f"Error cleaning HTML: {e}")
            return html_content
    
    def generate_dcs_pdf_fallback(self, page_html_data, customer_name, customer_folder):
        """Fallback method if weasyprint is not available"""
        try:
            logger.info("Using fallback method to create DCS PDF placeholder...")
            
            # Create a simple text-based PDF using reportlab as fallback
            from reportlab.pdfgen import canvas
            from reportlab.lib.pagesizes import letter
            
            dcs_pdf_filename = f"DCS {customer_name} Credit App.pdf"
            dcs_pdf_path = Path(customer_folder) / dcs_pdf_filename
            
            # Create a simple PDF with page information
            c = canvas.Canvas(str(dcs_pdf_path), pagesize=letter)
            width, height = letter
            
            # Add content
            c.setFont("Helvetica-Bold", 16)
            c.drawString(50, height - 50, f"DCS Credit Application - {customer_name}")
            
            c.setFont("Helvetica", 12)
            c.drawString(50, height - 80, f"Original DCS page from: {page_html_data.get('url', 'Unknown')}")
            c.drawString(50, height - 100, f"Captured: {page_html_data.get('timestamp', 'Unknown')}")
            c.drawString(50, height - 120, f"Page Title: {page_html_data.get('title', 'Unknown')}")
            
            c.drawString(50, height - 160, "Note: This is a placeholder. For full HTML-to-PDF conversion,")
            c.drawString(50, height - 180, "install weasyprint: pip install weasyprint")
            
            c.save()
            
            logger.info(f"Created DCS PDF placeholder: {dcs_pdf_path}")
            return dcs_pdf_path
            
        except Exception as e:
            logger.error(f"Error creating DCS PDF fallback: {e}")
            return None
    
    def open_pdf_in_adobe(self, pdf_path):
        """Open the filled PDF in Adobe Acrobat for review"""
        try:
            import subprocess
            import os
            
            if not pdf_path or not Path(pdf_path).exists():
                logger.warning(f"PDF file not found: {pdf_path}")
                return False
                
            logger.info(f"Opening PDF in Adobe Acrobat: {pdf_path}")
            
            # Try different methods to open PDF in Adobe
            if os.name == 'nt':  # Windows
                try:
                    # Method 1: Try to open with default PDF application
                    subprocess.Popen(['start', '', str(pdf_path)], shell=True)
                    logger.info("PDF opened with default application")
                    return True
                except Exception as e:
                    logger.warning(f"Failed to open with default app: {e}")
                    
                try:
                    # Method 2: Try to find and use Adobe Acrobat directly
                    adobe_paths = [
                        r"C:\Program Files\Adobe\Acrobat DC\Acrobat\Acrobat.exe",
                        r"C:\Program Files (x86)\Adobe\Acrobat DC\Acrobat\Acrobat.exe",
                        r"C:\Program Files\Adobe\Acrobat Reader DC\Reader\AcroRd32.exe",
                        r"C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.exe"
                    ]
                    
                    for adobe_path in adobe_paths:
                        if Path(adobe_path).exists():
                            subprocess.Popen([adobe_path, str(pdf_path)])
                            logger.info(f"PDF opened with Adobe at: {adobe_path}")
                            return True
                            
                    logger.warning("Adobe Acrobat/Reader not found in common locations")
                    
                except Exception as e:
                    logger.warning(f"Failed to open with Adobe directly: {e}")
            
            return False
            
        except Exception as e:
            logger.error(f"Error opening PDF in Adobe: {e}")
            return False
    
    def print_pdf(self, pdf_path):
        """Print PDF using system default printer"""
        try:
            import subprocess
            import os
            
            if not pdf_path or not Path(pdf_path).exists():
                logger.warning(f"PDF file not found for printing: {pdf_path}")
                return False
                
            logger.info(f"Attempting to print PDF: {pdf_path}")
            
            if os.name == 'nt':  # Windows
                try:
                    # Method 1: Try SumatraPDF for silent printing (best option)
                    sumatra_paths = [
                        r"C:\Program Files\SumatraPDF\SumatraPDF.exe",
                        r"C:\Program Files (x86)\SumatraPDF\SumatraPDF.exe",
                        r"D:\scans\sumatra.exe"
                    ]
                    
                    for sumatra_path in sumatra_paths:
                        if Path(sumatra_path).exists():
                            # Use scaling and fit options to prevent checkmark size issues
                            subprocess.run([sumatra_path, "-print-to-default", "-print-settings", "fit,shrink", str(pdf_path)], 
                                         check=True, capture_output=True)
                            logger.info(f"PDF printed using SumatraPDF with scaling: {sumatra_path}")
                            return True
                    
                    # Method 2: Fallback to Windows shell print command
                    result = subprocess.run(
                        ['powershell', '-Command', f'Start-Process -FilePath "{pdf_path}" -Verb Print -WindowStyle Hidden'],
                        capture_output=True, text=True, check=True
                    )
                    logger.info("PDF sent to default printer using PowerShell")
                    return True
                    
                except subprocess.CalledProcessError as e:
                    logger.warning(f"Print command failed: {e}")
                    
                try:
                    # Method 3: Alternative Windows print method
                    os.startfile(str(pdf_path), "print")
                    logger.info("PDF sent to printer using startfile")
                    return True
                except Exception as e:
                    logger.warning(f"startfile print failed: {e}")
            
            else:  # macOS/Linux
                try:
                    # macOS: Use lpr command
                    subprocess.run(['lpr', str(pdf_path)], check=True)
                    logger.info("PDF printed using lpr command")
                    return True
                except subprocess.CalledProcessError:
                    try:
                        # Linux: Try lp command
                        subprocess.run(['lp', str(pdf_path)], check=True)
                        logger.info("PDF printed using lp command")
                        return True
                    except subprocess.CalledProcessError as e:
                        logger.warning(f"Print commands failed: {e}")
            
            logger.warning("All print methods failed")
            return False
            
        except Exception as e:
            logger.error(f"Error printing PDF: {e}")
            return False
    
    def create_simple_pdf(self, data, output_path):
        """Create PDF - try template filling first, fallback to generated PDF"""
        try:
            # First try to fill the template
            if self.fill_template_pdf(data, output_path):
                return True
            
            # Fallback to creating a simple PDF
            logger.info("Template filling failed, creating simple PDF...")
            from reportlab.pdfgen import canvas
            from reportlab.lib.pagesizes import letter
            
            customer_name = self.generate_customer_name(data)
            
            # Create PDF
            pdf_path = output_path.with_suffix('.pdf')
            c = canvas.Canvas(str(pdf_path), pagesize=letter)
            width, height = letter
            
            # Title
            c.setFont("Helvetica-Bold", 16)
            c.drawString(50, height - 50, f"DCS Credit Application - {customer_name}")
            
            # Date
            c.setFont("Helvetica", 10)
            c.drawString(50, height - 80, f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
            
            y_position = height - 120
            line_height = 15
            
            # Applicant Information
            c.setFont("Helvetica-Bold", 12)
            c.drawString(50, y_position, "APPLICANT INFORMATION:")
            y_position -= 20
            
            c.setFont("Helvetica", 10)
            applicant_fields = [
                ("Name", f"{data.get('first_name', '')} {data.get('last_name', '')}".strip()),
                ("Address", data.get('address', '')),
                ("Address 2", data.get('address2', '')),
                ("City/State/Zip", f"{data.get('city', '')}, {data.get('state', '')} {data.get('zip', '')}".strip()),
                ("Phone", data.get('mobile_phone', '') or data.get('home_phone', '')),
                ("Email", data.get('email', '')),
                ("SSN", data.get('ssn', '')),
                ("Date of Birth", data.get('date_of_birth', '')),
                ("Driver's License", data.get('drivers_license', '')),
            ]
            
            for label, value in applicant_fields:
                if value:
                    c.drawString(50, y_position, f"{label}: {value}")
                    y_position -= line_height
            
            y_position -= 10
            
            # Employment Information
            c.setFont("Helvetica-Bold", 12)
            c.drawString(50, y_position, "EMPLOYMENT INFORMATION:")
            y_position -= 20
            
            c.setFont("Helvetica", 10)
            employment_fields = [
                ("Employer", data.get('employer', '')),
                ("Occupation", data.get('occupation', '')),
                ("Monthly Income", f"${data.get('monthly_income', '')}" if data.get('monthly_income') else ''),
                ("Time on Job", data.get('time_on_job', '')),
                ("Work Phone", data.get('work_phone', '')),
            ]
            
            for label, value in employment_fields:
                if value:
                    c.drawString(50, y_position, f"{label}: {value}")
                    y_position -= line_height
            
            y_position -= 10
            
            # Vehicle Information  
            c.setFont("Helvetica-Bold", 12)
            c.drawString(50, y_position, "VEHICLE INFORMATION:")
            y_position -= 20
            
            c.setFont("Helvetica", 10)
            vehicle_fields = [
                ("Year", data.get('vehicle_year', '')),
                ("Make", data.get('vehicle_make', '')),
                ("Model", data.get('vehicle_model', '')),
                ("VIN", data.get('vehicle_vin', '')),
                ("Mileage", data.get('vehicle_mileage', '')),
                ("Sale Price", f"${data.get('sale_price', '')}" if data.get('sale_price') else ''),
            ]
            
            for label, value in vehicle_fields:
                if value:
                    c.drawString(50, y_position, f"{label}: {value}")
                    y_position -= line_height
            
            # Add a second page if we have more data
            if y_position < 100:
                c.showPage()
                y_position = height - 50
            
            y_position -= 20
            
            # All extracted fields summary
            c.setFont("Helvetica-Bold", 12)
            c.drawString(50, y_position, f"ALL EXTRACTED FIELDS ({len([v for v in data.values() if v and str(v).strip()])} total):")
            y_position -= 20
            
            c.setFont("Helvetica", 8)
            for key, value in data.items():
                if value and str(value).strip() and y_position > 50:
                    text = f"{key}: {value}"
                    if len(text) > 80:
                        text = text[:77] + "..."
                    c.drawString(50, y_position, text)
                    y_position -= 12
                elif y_position <= 50:
                    c.showPage()
                    y_position = height - 50
            
            c.save()
            logger.info(f"Created PDF file: {pdf_path}")
            return True
            
        except ImportError:
            logger.warning("reportlab not available, creating text file instead")
            # Fallback to text file
            customer_name = self.generate_customer_name(data)
            
            text_content = f"""
DCS Credit Application - {customer_name}
Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

APPLICANT INFORMATION:
Name: {data.get('first_name', '')} {data.get('last_name', '')}
Address: {data.get('address', '')}
City/State/Zip: {data.get('city', '')}, {data.get('state', '')} {data.get('zip', '')}
Phone: {data.get('mobile_phone', '')} {data.get('home_phone', '')}
Email: {data.get('email', '')}
SSN: {data.get('ssn', '')}
Date of Birth: {data.get('date_of_birth', '')}

EMPLOYMENT INFORMATION:
Employer: {data.get('employer', '')}
Occupation: {data.get('occupation', '')}
Monthly Income: ${data.get('monthly_income', '')}
Time on Job: {data.get('time_on_job', '')}
Work Phone: {data.get('work_phone', '')}

VEHICLE INFORMATION:
Year: {data.get('vehicle_year', '')}
Make: {data.get('vehicle_make', '')}
Model: {data.get('vehicle_model', '')}
VIN: {data.get('vehicle_vin', '')}
Mileage: {data.get('vehicle_mileage', '')}

EXTRACTED FIELDS ({len([v for v in data.values() if v and str(v).strip()])} total):
"""
            
            # Add all extracted fields
            for key, value in data.items():
                if value and str(value).strip():
                    text_content += f"{key}: {value}\n"
            
            # Write as text file for now
            txt_path = output_path.with_suffix('.txt')
            with open(txt_path, 'w', encoding='utf-8') as f:
                f.write(text_content)
            
            logger.info(f"Created data file: {txt_path}")
            return True
            
        except Exception as e:
            logger.error(f"Error creating PDF: {e}")
            return False
    
    def export_json_data(self, data, customer_folder):
        """Export extracted data as JSON file"""
        try:
            customer_name = self.generate_customer_name(data)
            json_filename = f"{customer_name} Credit App.json"
            json_path = Path(customer_folder) / json_filename
            
            export_data = {
                "customer_name": customer_name,
                "processed_date": datetime.now().isoformat(),
                "extraction_method": data.get('extraction_method', 'chrome_extension'),
                "source_url": data.get('source_url', ''),
                "data": data,
                "extraction_stats": {
                    "fields_extracted": len([v for v in data.values() if v and str(v).strip()]),
                    "total_possible_fields": len(data),
                    "extraction_success_rate": f"{len([v for v in data.values() if v and str(v).strip()]) / len(data) * 100:.1f}%"
                }
            }
            
            with open(json_path, 'w', encoding='utf-8') as f:
                json.dump(export_data, f, indent=2, ensure_ascii=False)
            
            logger.info(f"JSON data exported: {json_path}")
            return True
            
        except Exception as e:
            logger.error(f"Failed to export JSON data: {e}")
            return False
    
    def organize_files(self, output_path, extracted_data, page_html_data=None, auto_print=True):
        """Move files to customer folder structure and generate original DCS PDF"""
        try:
            if not self.customer_docs_base:
                logger.warning("No customer docs base path configured")
                return output_path
            
            customer_name = self.generate_customer_name(extracted_data)
            
            # Create customer folder structure
            now = datetime.now()
            year_folder = self.customer_docs_base / str(now.year)
            month_folder = year_folder / f"{now.month:02d} - {now.strftime('%B')}"
            customer_folder = month_folder / customer_name
            
            customer_folder.mkdir(parents=True, exist_ok=True)
            
            # Generate original DCS PDF from HTML content
            dcs_pdf_path = None
            if page_html_data:
                dcs_pdf_path = self.generate_original_dcs_pdf(page_html_data, customer_name, customer_folder)
                if dcs_pdf_path:
                    logger.info(f"Generated original DCS PDF: {dcs_pdf_path}")
                    
                    # Auto-print DCS PDF if requested
                    if auto_print:
                        logger.info("Auto-printing DCS PDF...")
                        print_success = self.print_pdf(dcs_pdf_path)
                        if print_success:
                            logger.info("DCS PDF printed successfully")
                        else:
                            logger.warning("Failed to print DCS PDF")
                else:
                    logger.warning("Failed to generate original DCS PDF")
            
            # Look for the filled PDF file first, then fallback to other files
            pdf_path = output_path.with_suffix('.pdf')
            txt_path = output_path.with_suffix('.txt')
            
            source_file = None
            if pdf_path.exists():
                source_file = pdf_path
                logger.info(f"Found filled PDF file to organize: {pdf_path}")
            elif txt_path.exists():
                source_file = txt_path
                logger.info(f"Found text file to organize: {txt_path}")
            elif output_path.exists():
                source_file = output_path
                logger.info(f"Found output file to organize: {output_path}")
            
            if source_file:
                destination = customer_folder / f"{customer_name} Credit App{source_file.suffix}"
                shutil.copy2(source_file, destination)
                logger.info(f"Filled application moved to: {destination}")
                
                # Export JSON data
                self.export_json_data(extracted_data, customer_folder)
                
                return destination
            else:
                logger.error(f"No output files found to organize")
                return None
                
        except Exception as e:
            logger.error(f"Error organizing files: {e}")
            return None

# Global processor instance
processor = None

def initialize_processor():
    """Initialize the processor"""
    global processor
    
    try:
        export_folder = Path("./api_temp/output")
        export_folder.mkdir(parents=True, exist_ok=True)
        
        processor = StandaloneProcessor(
            export_folder=export_folder,
            customer_docs_base=CUSTOMER_DOCS_BASE
        )
        
        logger.info("Standalone processor initialized for API mode")
        return True
        
    except Exception as e:
        logger.error(f"Failed to initialize processor: {e}")
        return False


# Odoo API endpoints
@app.post("/odoo/config")
async def configure_odoo(config: OdooConfigRequest):
    """Configure Odoo connection settings"""
    try:
        # Prepare settings update - don't overwrite password if not provided
        update_data = {
            'url': config.url,
            'database': config.database,
            'username': config.username,
            'enable_sync': config.enable_sync,
            'tag_name': config.tag_name
        }
        
        # Only update password if provided
        if hasattr(config, 'password') and config.password:
            update_data['password'] = config.password
        
        success = odoo_config.update_settings(**update_data)
        
        if success:
            return {"success": True, "message": "Odoo configuration saved successfully"}
        else:
            return {"success": False, "message": "Failed to save configuration"}
            
    except Exception as e:
        logger.error(f"Error configuring Odoo: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/odoo/config")
async def get_odoo_config():
    """Get current Odoo configuration (without password)"""
    config = odoo_config.settings.copy()
    # Don't send actual password, but indicate if one exists
    if 'password' in config and config['password']:
        config['has_password'] = True
        config['password'] = ""  # Empty password field
    else:
        config['has_password'] = False
        config['password'] = ""
    return config

@app.post("/odoo/test")
async def test_odoo_connection():
    """Test Odoo connection"""
    result = odoo_config.test_connection()
    return result

@app.post("/odoo/search-leads")
async def search_leads(search_request: LeadSearchRequest):
    """Search for leads by name and phone"""
    try:
        client = odoo_config.get_client()
        if not client:
            return {
                "success": False,
                "message": "Odoo not configured or connection failed",
                "leads": []
            }
        
        leads = client.search_leads(search_request.name, search_request.phone)
        return {
            "success": True,
            "message": f"Found {len(leads)} matching leads",
            "leads": leads
        }
        
    except Exception as e:
        logger.error(f"Error searching leads: {str(e)}")
        return {
            "success": False,
            "message": f"Error searching leads: {str(e)}",
            "leads": []
        }

@app.post("/odoo/debug-search")
async def debug_odoo_search(search_term: str = Query(...), field: str = Query("name"), operator: str = Query("ilike")):
    """Debug endpoint to test individual Odoo search operations and show recent leads"""
    try:
        client = odoo_config.get_client()
        if not client:
            return {
                "success": False,
                "message": "Odoo not configured or connection failed",
                "results": {}
            }
        
        results = client.debug_search(search_term, field, operator)
        return {
            "success": True,
            "message": f"Debug search completed for '{search_term}' in field '{field}'",
            "results": results
        }
        
    except Exception as e:
        logger.error(f"Error in debug search: {str(e)}")
        return {
            "success": False,
            "message": f"Debug search failed: {str(e)}",
            "leads": []
        }

@app.post("/odoo/sync")
async def sync_to_odoo(sync_request: SyncToOdooRequest):
    """Sync files and data to Odoo lead"""
    try:
        client = odoo_config.get_client()
        if not client:
            return {
                "success": False,
                "message": "Odoo not configured or connection failed"
            }
        
        # Read and attach PDF files
        pdf_success = False
        dcs_pdf_success = False
        json_success = False
        tag_success = False
        
        # Attach filled AutoKing PDF
        if os.path.exists(sync_request.pdf_path):
            with open(sync_request.pdf_path, 'rb') as f:
                pdf_content = f.read()
            pdf_filename = os.path.basename(sync_request.pdf_path)
            pdf_success = client.attach_file(sync_request.lead_id, pdf_filename, pdf_content)
        
        # Attach original DCS PDF
        if sync_request.dcs_pdf_path and os.path.exists(sync_request.dcs_pdf_path):
            with open(sync_request.dcs_pdf_path, 'rb') as f:
                dcs_pdf_content = f.read()
            dcs_pdf_filename = os.path.basename(sync_request.dcs_pdf_path)
            dcs_pdf_success = client.attach_file(sync_request.lead_id, dcs_pdf_filename, dcs_pdf_content)
        
        # Read and attach JSON file  
        if os.path.exists(sync_request.json_path):
            with open(sync_request.json_path, 'rb') as f:
                json_content = f.read()
            json_filename = os.path.basename(sync_request.json_path)
            json_success = client.attach_file(sync_request.lead_id, json_filename, json_content)
        
        # Update lead with extracted data
        data_success = False
        if hasattr(sync_request, 'extracted_data') and sync_request.extracted_data:
            data_success = client.update_lead_data(sync_request.lead_id, sync_request.extracted_data)
        
        # Add synced tag
        tag_name = odoo_config.settings.get('tag_name', 'synced')
        tag_success = client.add_tag(sync_request.lead_id, tag_name)
        
        # Determine overall success
        overall_success = (pdf_success or dcs_pdf_success or json_success) and tag_success
        message_parts = []
        
        if pdf_success:
            message_parts.append("AutoKing PDF attached")
        if dcs_pdf_success:
            message_parts.append("DCS PDF attached")
        if json_success:
            message_parts.append("JSON attached")
        if data_success:
            message_parts.append("lead data updated")
        if tag_success:
            message_parts.append(f"'{tag_name}' tag added")
        
        if overall_success:
            message = f"Successfully synced to lead {sync_request.lead_id}: " + ", ".join(message_parts)
        else:
            message = f"Partial sync to lead {sync_request.lead_id}: " + ", ".join(message_parts)
        
        return {
            "success": overall_success,
            "message": message,
            "pdf_attached": pdf_success,
            "dcs_pdf_attached": dcs_pdf_success,
            "json_attached": json_success,
            "data_updated": data_success,
            "tag_added": tag_success
        }
        
    except Exception as e:
        logger.error(f"Error syncing to Odoo: {str(e)}")
        return {
            "success": False,
            "message": f"Error syncing to Odoo: {str(e)}"
        }

@app.get("/health")
async def health_check():
    """Health check endpoint"""
    return {
        "status": "healthy",
        "timestamp": datetime.now().isoformat(),
        "processor_ready": processor is not None,
        "odoo_configured": odoo_config.is_configured()
    }

@app.post("/process-application", response_model=ProcessResponse)
async def process_application(request: ProcessRequest):
    """Main endpoint to process credit application data from Chrome extension"""
    try:
        # Use custom customer_docs_path if provided, otherwise use default
        customer_docs_path = request.customer_docs_path or CUSTOMER_DOCS_BASE
        
        # Create a processor instance with the specified path
        local_processor = StandaloneProcessor(
            export_folder="./output",
            customer_docs_base=customer_docs_path
        )
        
        if not local_processor:
            raise HTTPException(status_code=500, detail="Processor not initialized")
        
        logger.info("Received credit application processing request")
        logger.info(f"Data fields received: {len([k for k, v in request.data.items() if v])}")
        
        # Add metadata to the data
        extracted_data = dict(request.data)
        extracted_data.update({
            'source_url': request.source_url,
            'processed_at': request.timestamp,
            'extraction_method': 'chrome_extension',
            'dcs_pdf_filename': request.pdf_filename  # Store PDF filename for reference
        })
        
        # Generate customer name for file naming
        customer_name = local_processor.generate_customer_name(extracted_data)
        if not customer_name or customer_name == "Unknown Customer":
            # Try to extract any name from the data
            possible_names = [v for k, v in extracted_data.items() if 'name' in k.lower() and v]
            if possible_names:
                customer_name = str(possible_names[0])
            else:
                customer_name = f"Customer_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
        
        # Generate output filename
        output_filename = f"{customer_name} Credit App.txt"
        output_path = local_processor.export_folder / output_filename
        
        # Create the data file
        logger.info(f"Creating data file for: {customer_name}")
        success = local_processor.create_simple_pdf(extracted_data, output_path)
        
        if not success:
            raise HTTPException(status_code=500, detail="Failed to create data file")
        
        # Move files to customer folder and export JSON
        customer_folder_path = None
        json_exported = False
        
        try:
            move_result = local_processor.organize_files(output_path, extracted_data, request.pageHtml, request.auto_print)
            if move_result:
                customer_folder_path = str(move_result.parent)
                json_exported = True
                logger.info(f"Files organized in: {customer_folder_path}")
                
                # Auto-open the filled PDF in Adobe Acrobat for review if enabled
                if request.auto_open and str(move_result).endswith('.pdf'):
                    local_processor.open_pdf_in_adobe(move_result)
                
        except Exception as e:
            logger.warning(f"File organization failed: {e}")
        
        # Count processed fields
        fields_processed = len([v for v in extracted_data.values() if v and str(v).strip()])
        
        # Create Odoo lead if enabled
        odoo_lead_id = None
        if request.odoo_sync and customer_folder_path:
            try:
                # Load Odoo config
                odoo_config_path = Path(ODOO_CONFIG_FILE)
                if odoo_config_path.exists():
                    with open(odoo_config_path, 'r') as f:
                        odoo_config = json.load(f)
                    
                    if odoo_config.get('enable_sync', False):
                        # Connect to Odoo
                        odoo_client = OdooClient(
                            odoo_config['url'],
                            odoo_config['database'],
                            odoo_config['username'],
                            odoo_config.get('password', odoo_config.get('api_key', ''))
                        )
                        
                        if odoo_client.connect():
                            # Search for existing lead by phone
                            phone = extracted_data.get('mobile_phone') or extracted_data.get('phone', '')
                            existing_lead = None
                            
                            if phone:
                                leads = odoo_client.search_leads(customer_name, phone)
                                if leads:
                                    # Prioritize leads tagged with "Online App"
                                    for lead in leads:
                                        # Get lead tags
                                        lead_detail = odoo_client.models.execute_kw(
                                            odoo_client.database, odoo_client.uid, odoo_client.password,
                                            'crm.lead', 'read', [[lead['id']]],
                                            {'fields': ['tag_ids']}
                                        )[0]
                                        
                                        if lead_detail.get('tag_ids'):
                                            tags = odoo_client.models.execute_kw(
                                                odoo_client.database, odoo_client.uid, odoo_client.password,
                                                'crm.tag', 'read', [lead_detail['tag_ids']],
                                                {'fields': ['name']}
                                            )
                                            tag_names = [t['name'] for t in tags]
                                            if 'Online App' in tag_names:
                                                existing_lead = lead
                                                logger.info(f"Found existing lead with 'Online App' tag: {lead['id']}")
                                                break
                                    
                                    if not existing_lead:
                                        existing_lead = leads[0]  # Use first lead if no Online App tag
                                        logger.info(f"Found existing lead: {existing_lead['id']}")
                            
                            if existing_lead:
                                # Update existing lead
                                odoo_lead_id = existing_lead['id']
                                odoo_client.update_lead_data(odoo_lead_id, extracted_data)
                                logger.info(f"Updated existing lead {odoo_lead_id}")
                            else:
                                # Create new lead
                                odoo_lead_id = odoo_client.create_lead(extracted_data)
                                if odoo_lead_id:
                                    logger.info(f"Created new Odoo lead: {odoo_lead_id}")
                                    
                                    # Add 'synced' tag
                                    tag_name = odoo_config.get('tag_name', 'synced')
                                    odoo_client.add_tag(odoo_lead_id, tag_name)
                            
                            # Attach files to lead
                            if odoo_lead_id:
                                # Attach filled PDF
                                if move_result and move_result.exists():
                                    with open(move_result, 'rb') as f:
                                        odoo_client.attach_file(odoo_lead_id, move_result.name, f.read())
                                
                                # Attach DCS PDF
                                dcs_pdf_path = Path(customer_folder_path) / f"DCS {customer_name} Credit App.pdf"
                                if dcs_pdf_path.exists():
                                    with open(dcs_pdf_path, 'rb') as f:
                                        odoo_client.attach_file(odoo_lead_id, dcs_pdf_path.name, f.read())
                                
                                # Attach JSON
                                json_path = Path(customer_folder_path) / f"{customer_name} Credit App.json"
                                if json_path.exists():
                                    with open(json_path, 'rb') as f:
                                        odoo_client.attach_file(odoo_lead_id, json_path.name, f.read())
                                
                                # Create marker file to prevent file_watcher from processing
                                marker_file = Path(customer_folder_path) / f".odoo_lead_{odoo_lead_id}"
                                marker_file.touch()
                                logger.info(f"Created marker file: {marker_file}")
                                
            except Exception as e:
                logger.error(f"Odoo sync failed: {e}")
                # Don't fail the request if Odoo sync fails
        
        logger.info(f"Successfully processed credit application: {output_filename}")
        
        return ProcessResponse(
            success=True,
            message=f"Credit application processed successfully for {customer_name}",
            filename=output_filename,
            customer_folder=customer_folder_path,
            customer_name=customer_name,
            fields_processed=fields_processed,
            json_exported=json_exported,
            odoo_lead_id=odoo_lead_id
        )
        
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error processing credit application: {str(e)}")
        raise HTTPException(status_code=500, detail=f"Processing failed: {str(e)}")

@app.post("/fill-pdf-from-data")
async def fill_pdf_from_data(data: Dict[str, Any]):
    """Fill PDF template with provided data (for Odoo regeneration)"""
    try:
        # Create a temporary processor instance
        temp_processor = StandaloneProcessor(
            export_folder="./temp",
            customer_docs_base=CUSTOMER_DOCS_BASE
        )

        # Create temporary output path
        import tempfile
        temp_dir = Path(tempfile.mkdtemp())
        temp_output = temp_dir / "temp_credit_app.pdf"

        # Try to fill the PDF template
        success = temp_processor.create_simple_pdf(data, temp_output)

        if not success or not temp_output.exists():
            raise HTTPException(status_code=500, detail="Failed to generate PDF")

        # Read the generated PDF
        with open(temp_output, 'rb') as f:
            pdf_content = f.read()

        # Clean up temporary files
        try:
            temp_output.unlink()
            temp_dir.rmdir()
        except:
            pass

        # Return the PDF content
        return Response(
            content=pdf_content,
            media_type="application/pdf",
            headers={"Content-Disposition": "attachment; filename=filled_credit_app.pdf"}
        )

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Error filling PDF from data: {str(e)}")
        raise HTTPException(status_code=500, detail=f"PDF generation failed: {str(e)}")

@app.post("/validate-data")
async def validate_application_data(data: Dict[str, Any]):
    """Validates extracted data without processing"""
    try:
        filled_count = len([v for v in data.values() if v and str(v).strip()])
        total_possible = len(data)
        
        validation_result = {
            "valid": filled_count > 0,
            "filled_fields": filled_count,
            "total_possible_fields": total_possible,
            "completion_percentage": round(filled_count / total_possible * 100, 1) if total_possible > 0 else 0,
            "has_name": bool(data.get('first_name') or data.get('last_name')),
            "has_contact": bool(data.get('email') or data.get('mobile_phone') or data.get('home_phone'))
        }
        
        return validation_result
        
    except Exception as e:
        logger.error(f"Error validating data: {e}")
        raise HTTPException(status_code=500, detail=f"Validation failed: {str(e)}")

@app.get("/")
async def root():
    """Root endpoint with API information"""
    return {
        "service": "DCS Credit Application API (Standalone)",
        "version": "1.0.0",
        "status": "running",
        "endpoints": {
            "health": "/health",
            "process": "/process-application",
            "validate": "/validate-data"
        }
    }

if __name__ == "__main__":
    print("Starting DCS Credit Application API Server (Standalone)...")
    print("Server will be available at: http://localhost:8000")
    print("Health check: http://localhost:8000/health")
    print("API docs: http://localhost:8000/docs")
    
    uvicorn.run(
        app,
        host="127.0.0.1",
        port=8000,
        log_level="info",
        reload=False
    )