"""
Sync functions to populate modern models from legacy database tables.

This module provides functions to migrate data from the comprehensive legacy
schema to the simplified modern application models.
"""

from typing import Dict, Any
from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
import structlog

from .models import (
    # Legacy models
    Rolodex, LegacyPhone, LegacyFile, Ledger, LegacyPayment, Qdros,
    # Modern models
    Client, Phone, Case, Transaction, Payment, Document
)

logger = structlog.get_logger(__name__)

BATCH_SIZE = 500


def sync_clients(db: Session, clear_existing: bool = False) -> Dict[str, Any]:
    """
    Sync Rolodex → Client.
    
    Maps legacy rolodex entries to modern simplified client records.
    """
    result = {'success': 0, 'errors': [], 'skipped': 0}
    
    try:
        # Optionally clear existing modern client data
        if clear_existing:
            logger.info("sync_clients_clearing_existing")
            db.query(Client).delete()
            db.commit()
        
        # Query all rolodex entries
        rolodex_entries = db.query(Rolodex).all()
        logger.info("sync_clients_processing", count=len(rolodex_entries))
        
        batch = []
        for rolex in rolodex_entries:
            try:
                # Build complete address from A1, A2, A3
                address_parts = [
                    rolex.a1 or '',
                    rolex.a2 or '',
                    rolex.a3 or ''
                ]
                address = ', '.join(filter(None, address_parts))
                
                # Create modern client record
                client = Client(
                    rolodex_id=rolex.id,
                    last_name=rolex.last,
                    first_name=rolex.first,
                    middle_initial=rolex.middle,
                    company=rolex.title,  # Using title as company name
                    address=address if address else None,
                    city=rolex.city,
                    state=rolex.abrev,
                    zip_code=rolex.zip
                )
                batch.append(client)
                
                if len(batch) >= BATCH_SIZE:
                    db.bulk_save_objects(batch)
                    db.commit()
                    result['success'] += len(batch)
                    batch = []
                    
            except Exception as e:
                result['errors'].append(f"Rolodex ID {rolex.id}: {str(e)}")
                result['skipped'] += 1
        
        # Save remaining batch
        if batch:
            db.bulk_save_objects(batch)
            db.commit()
            result['success'] += len(batch)
        
        logger.info("sync_clients_complete", **result)
        
    except Exception as e:
        db.rollback()
        result['errors'].append(f"Fatal error: {str(e)}")
        logger.error("sync_clients_failed", error=str(e))
    
    return result


def sync_phones(db: Session, clear_existing: bool = False) -> Dict[str, Any]:
    """
    Sync LegacyPhone → Phone.
    
    Links phone numbers to modern client records via rolodex_id.
    """
    result = {'success': 0, 'errors': [], 'skipped': 0}
    
    try:
        # Optionally clear existing phone data
        if clear_existing:
            logger.info("sync_phones_clearing_existing")
            db.query(Phone).delete()
            db.commit()
        
        # Build lookup map: rolodex_id → client.id
        clients = db.query(Client).all()
        rolodex_to_client = {c.rolodex_id: c.id for c in clients}
        logger.info("sync_phones_client_map", client_count=len(rolodex_to_client))
        
        # Query all legacy phones
        legacy_phones = db.query(LegacyPhone).all()
        logger.info("sync_phones_processing", count=len(legacy_phones))
        
        batch = []
        for lphone in legacy_phones:
            try:
                # Find corresponding modern client
                client_id = rolodex_to_client.get(lphone.id)
                if not client_id:
                    result['errors'].append(f"No client found for rolodex ID: {lphone.id}")
                    result['skipped'] += 1
                    continue
                
                # Create modern phone record
                phone = Phone(
                    client_id=client_id,
                    phone_type=lphone.location if lphone.location else 'unknown',
                    phone_number=lphone.phone,
                    extension=None
                )
                batch.append(phone)
                
                if len(batch) >= BATCH_SIZE:
                    db.bulk_save_objects(batch)
                    db.commit()
                    result['success'] += len(batch)
                    batch = []
                    
            except Exception as e:
                result['errors'].append(f"Phone {lphone.id}/{lphone.phone}: {str(e)}")
                result['skipped'] += 1
        
        # Save remaining batch
        if batch:
            db.bulk_save_objects(batch)
            db.commit()
            result['success'] += len(batch)
        
        logger.info("sync_phones_complete", **result)
        
    except Exception as e:
        db.rollback()
        result['errors'].append(f"Fatal error: {str(e)}")
        logger.error("sync_phones_failed", error=str(e))
    
    return result


def sync_cases(db: Session, clear_existing: bool = False) -> Dict[str, Any]:
    """
    Sync LegacyFile → Case.
    
    Converts legacy file cabinet entries to modern case records.
    """
    result = {'success': 0, 'errors': [], 'skipped': 0}
    
    try:
        # Optionally clear existing case data
        if clear_existing:
            logger.info("sync_cases_clearing_existing")
            db.query(Case).delete()
            db.commit()
        
        # Build lookup map: rolodex_id → client.id
        clients = db.query(Client).all()
        rolodex_to_client = {c.rolodex_id: c.id for c in clients}
        logger.info("sync_cases_client_map", client_count=len(rolodex_to_client))
        
        # Query all legacy files
        legacy_files = db.query(LegacyFile).all()
        logger.info("sync_cases_processing", count=len(legacy_files))
        
        batch = []
        for lfile in legacy_files:
            try:
                # Find corresponding modern client
                client_id = rolodex_to_client.get(lfile.id)
                if not client_id:
                    result['errors'].append(f"No client found for rolodex ID: {lfile.id} (file {lfile.file_no})")
                    result['skipped'] += 1
                    continue
                
                # Map legacy status to modern status
                status = 'active'
                if lfile.closed:
                    status = 'closed'
                elif lfile.status and 'inactive' in lfile.status.lower():
                    status = 'inactive'
                
                # Create modern case record
                case = Case(
                    file_no=lfile.file_no,
                    client_id=client_id,
                    status=status,
                    case_type=lfile.file_type,
                    description=lfile.regarding,
                    open_date=lfile.opened,
                    close_date=lfile.closed
                )
                batch.append(case)
                
                if len(batch) >= BATCH_SIZE:
                    db.bulk_save_objects(batch)
                    db.commit()
                    result['success'] += len(batch)
                    batch = []
                    
            except Exception as e:
                result['errors'].append(f"File {lfile.file_no}: {str(e)}")
                result['skipped'] += 1
        
        # Save remaining batch
        if batch:
            db.bulk_save_objects(batch)
            db.commit()
            result['success'] += len(batch)
        
        logger.info("sync_cases_complete", **result)
        
    except Exception as e:
        db.rollback()
        result['errors'].append(f"Fatal error: {str(e)}")
        logger.error("sync_cases_failed", error=str(e))
    
    return result


def sync_transactions(db: Session, clear_existing: bool = False) -> Dict[str, Any]:
    """
    Sync Ledger → Transaction.
    
    Converts legacy ledger entries to modern transaction records.
    """
    result = {'success': 0, 'errors': [], 'skipped': 0}
    
    try:
        # Optionally clear existing transaction data
        if clear_existing:
            logger.info("sync_transactions_clearing_existing")
            db.query(Transaction).delete()
            db.commit()
        
        # Build lookup map: file_no → case.id
        cases = db.query(Case).all()
        file_no_to_case = {c.file_no: c.id for c in cases}
        logger.info("sync_transactions_case_map", case_count=len(file_no_to_case))
        
        # Query all ledger entries
        ledger_entries = db.query(Ledger).all()
        logger.info("sync_transactions_processing", count=len(ledger_entries))
        
        batch = []
        for ledger in ledger_entries:
            try:
                # Find corresponding modern case
                case_id = file_no_to_case.get(ledger.file_no)
                if not case_id:
                    result['errors'].append(f"No case found for file: {ledger.file_no}")
                    result['skipped'] += 1
                    continue
                
                # Create modern transaction record with all ledger fields
                transaction = Transaction(
                    case_id=case_id,
                    transaction_date=ledger.date,
                    transaction_type=ledger.t_type,
                    amount=float(ledger.amount) if ledger.amount else None,
                    description=ledger.note,
                    reference=str(ledger.item_no) if ledger.item_no else None,
                    # Ledger-specific fields
                    item_no=ledger.item_no,
                    employee_number=ledger.empl_num,
                    t_code=ledger.t_code,
                    t_type_l=ledger.t_type_l,
                    quantity=float(ledger.quantity) if ledger.quantity else None,
                    rate=float(ledger.rate) if ledger.rate else None,
                    billed=ledger.billed
                )
                batch.append(transaction)
                
                if len(batch) >= BATCH_SIZE:
                    db.bulk_save_objects(batch)
                    db.commit()
                    result['success'] += len(batch)
                    batch = []
                    
            except Exception as e:
                result['errors'].append(f"Ledger {ledger.file_no}/{ledger.item_no}: {str(e)}")
                result['skipped'] += 1
        
        # Save remaining batch
        if batch:
            db.bulk_save_objects(batch)
            db.commit()
            result['success'] += len(batch)
        
        logger.info("sync_transactions_complete", **result)
        
    except Exception as e:
        db.rollback()
        result['errors'].append(f"Fatal error: {str(e)}")
        logger.error("sync_transactions_failed", error=str(e))
    
    return result


def sync_payments(db: Session, clear_existing: bool = False) -> Dict[str, Any]:
    """
    Sync LegacyPayment → Payment.
    
    Converts legacy payment entries to modern payment records.
    """
    result = {'success': 0, 'errors': [], 'skipped': 0}
    
    try:
        # Optionally clear existing payment data
        if clear_existing:
            logger.info("sync_payments_clearing_existing")
            db.query(Payment).delete()
            db.commit()
        
        # Build lookup map: file_no → case.id
        cases = db.query(Case).all()
        file_no_to_case = {c.file_no: c.id for c in cases}
        logger.info("sync_payments_case_map", case_count=len(file_no_to_case))
        
        # Query all legacy payments
        legacy_payments = db.query(LegacyPayment).all()
        logger.info("sync_payments_processing", count=len(legacy_payments))
        
        batch = []
        for lpay in legacy_payments:
            try:
                # Find corresponding modern case
                if not lpay.file_no:
                    result['skipped'] += 1
                    continue
                    
                case_id = file_no_to_case.get(lpay.file_no)
                if not case_id:
                    result['errors'].append(f"No case found for file: {lpay.file_no}")
                    result['skipped'] += 1
                    continue
                
                # Create modern payment record
                payment = Payment(
                    case_id=case_id,
                    payment_date=lpay.deposit_date,
                    payment_type='deposit',  # Legacy doesn't distinguish
                    amount=float(lpay.amount) if lpay.amount else None,
                    description=lpay.note if lpay.note else lpay.regarding,
                    check_number=None  # Not in legacy PAYMENTS table
                )
                batch.append(payment)
                
                if len(batch) >= BATCH_SIZE:
                    db.bulk_save_objects(batch)
                    db.commit()
                    result['success'] += len(batch)
                    batch = []
                    
            except Exception as e:
                result['errors'].append(f"Payment {lpay.id}: {str(e)}")
                result['skipped'] += 1
        
        # Save remaining batch
        if batch:
            db.bulk_save_objects(batch)
            db.commit()
            result['success'] += len(batch)
        
        logger.info("sync_payments_complete", **result)
        
    except Exception as e:
        db.rollback()
        result['errors'].append(f"Fatal error: {str(e)}")
        logger.error("sync_payments_failed", error=str(e))
    
    return result


def sync_documents(db: Session, clear_existing: bool = False) -> Dict[str, Any]:
    """
    Sync Qdros → Document.
    
    Converts QDRO entries to modern document records.
    """
    result = {'success': 0, 'errors': [], 'skipped': 0}
    
    try:
        # Optionally clear existing document data
        if clear_existing:
            logger.info("sync_documents_clearing_existing")
            db.query(Document).delete()
            db.commit()
        
        # Build lookup map: file_no → case.id
        cases = db.query(Case).all()
        file_no_to_case = {c.file_no: c.id for c in cases}
        logger.info("sync_documents_case_map", case_count=len(file_no_to_case))
        
        # Query all QDRO entries
        qdros = db.query(Qdros).all()
        logger.info("sync_documents_processing", count=len(qdros))
        
        batch = []
        for qdro in qdros:
            try:
                # Find corresponding modern case
                case_id = file_no_to_case.get(qdro.file_no)
                if not case_id:
                    result['errors'].append(f"No case found for file: {qdro.file_no}")
                    result['skipped'] += 1
                    continue
                
                # Build description from QDRO fields
                desc_parts = []
                if qdro.case_type:
                    desc_parts.append(f"Type: {qdro.case_type}")
                if qdro.case_number:
                    desc_parts.append(f"Case#: {qdro.case_number}")
                if qdro.plan_id:
                    desc_parts.append(f"Plan: {qdro.plan_id}")
                
                description = '; '.join(desc_parts) if desc_parts else None
                
                # Create modern document record
                document = Document(
                    case_id=case_id,
                    document_type='QDRO',
                    file_name=qdro.form_name,
                    file_path=None,  # Legacy doesn't have file paths
                    description=description,
                    uploaded_date=qdro.draft_out if qdro.draft_out else qdro.judgment_date
                )
                batch.append(document)
                
                if len(batch) >= BATCH_SIZE:
                    db.bulk_save_objects(batch)
                    db.commit()
                    result['success'] += len(batch)
                    batch = []
                    
            except Exception as e:
                result['errors'].append(f"QDRO {qdro.file_no}/{qdro.version}: {str(e)}")
                result['skipped'] += 1
        
        # Save remaining batch
        if batch:
            db.bulk_save_objects(batch)
            db.commit()
            result['success'] += len(batch)
        
        logger.info("sync_documents_complete", **result)
        
    except Exception as e:
        db.rollback()
        result['errors'].append(f"Fatal error: {str(e)}")
        logger.error("sync_documents_failed", error=str(e))
    
    return result


def sync_all(db: Session, clear_existing: bool = False) -> Dict[str, Any]:
    """
    Run all sync functions in proper order.
    
    Order matters due to foreign key dependencies:
    1. Clients (no dependencies)
    2. Phones (depends on Clients)
    3. Cases (depends on Clients)
    4. Transactions (depends on Cases)
    5. Payments (depends on Cases)
    6. Documents (depends on Cases)
    """
    results = {
        'clients': None,
        'phones': None,
        'cases': None,
        'transactions': None,
        'payments': None,
        'documents': None
    }
    
    logger.info("sync_all_starting", clear_existing=clear_existing)
    
    try:
        results['clients'] = sync_clients(db, clear_existing)
        logger.info("sync_all_clients_done", success=results['clients']['success'])
        
        results['phones'] = sync_phones(db, clear_existing)
        logger.info("sync_all_phones_done", success=results['phones']['success'])
        
        results['cases'] = sync_cases(db, clear_existing)
        logger.info("sync_all_cases_done", success=results['cases']['success'])
        
        results['transactions'] = sync_transactions(db, clear_existing)
        logger.info("sync_all_transactions_done", success=results['transactions']['success'])
        
        results['payments'] = sync_payments(db, clear_existing)
        logger.info("sync_all_payments_done", success=results['payments']['success'])
        
        results['documents'] = sync_documents(db, clear_existing)
        logger.info("sync_all_documents_done", success=results['documents']['success'])
        
        logger.info("sync_all_complete")
        
    except Exception as e:
        logger.error("sync_all_failed", error=str(e))
        raise
    
    return results



