"""
Settings Tab UI

This module provides the UI for managing application settings,
specifically MongoDB connection configuration.

Key principles:
- Manual control: All actions require explicit user confirmation
- Clear feedback: Success and error messages are always shown
- No automation: No background reconnections or retries
"""

import streamlit as st
from services.settings_service import SettingsService
from services.mongo import MongoService


def render_settings_tab(settings_service: SettingsService, mongo_service: MongoService):
    """
    Render the Settings tab with MongoDB configuration.
    
    Args:
        settings_service: Settings service instance
        mongo_service: MongoDB service instance
    """
    st.title("⚙️ Settings")
    st.markdown("Configure MongoDB connection and application settings.")
    
    st.markdown("---")
    
    # MongoDB Connection Settings Section
    st.header("🔌 MongoDB Connection Settings")
    
    # Get current settings
    current_settings = settings_service.get_mongodb_settings()
    
    # Show current connection status
    _render_connection_status(mongo_service, current_settings)
    
    st.markdown("---")
    
    # Configuration form
    _render_configuration_form(settings_service, mongo_service, current_settings)
    
    st.markdown("---")
    st.markdown("---")
    
    # Collection Configuration Section
    st.header("📋 Collection Configuration")
    _render_collection_configuration(settings_service, mongo_service)
    
    st.markdown("---")
    st.markdown("---")
    
    # Record Limit Section
    st.header("⚙️ Data Loading Settings")
    _render_record_limit_settings(settings_service)
    
    st.markdown("---")
    st.markdown("---")
    
    # Index Management Section
    _render_index_management(mongo_service)


def _render_connection_status(mongo_service: MongoService, current_settings: dict):
    """
    Render current connection status.
    
    Args:
        mongo_service: MongoDB service instance
        current_settings: Current MongoDB settings
    """
    st.subheader("📊 Current Connection Status")
    
    col1, col2 = st.columns(2)
    
    with col1:
        if mongo_service.is_connected():
            st.success("✅ **Status:** Connected")
        else:
            st.error("❌ **Status:** Not Connected")
            error = mongo_service.get_connection_error()
            if error:
                st.error(f"**Error:** {error}")
    
    with col2:
        st.info(f"**Active Database:** `{current_settings['database']}`")
    
    # Show masked URI
    uri = current_settings['uri']
    masked_uri = _mask_uri(uri)
    st.text(f"Connection URI: {masked_uri}")


def _render_configuration_form(
    settings_service: SettingsService,
    mongo_service: MongoService,
    current_settings: dict
):
    """
    Render the configuration form for MongoDB settings.
    
    Args:
        settings_service: Settings service instance
        mongo_service: MongoDB service instance
        current_settings: Current MongoDB settings
    """
    st.subheader("🔧 Configuration")
    
    st.markdown("""
    **Important:**
    - Changes will be saved to `app_config.yaml`
    - The MongoDB URI will be stored in the environment variable
    - You must click "Save Settings" to apply changes
    - After saving, the application will attempt to reconnect
    """)
    
    # Create form
    with st.form("mongodb_settings_form"):
        st.markdown("### Connection Details")
        
        # MongoDB URI
        uri = st.text_input(
            "MongoDB URI",
            value=current_settings['uri'] if current_settings['uri'] else "mongodb://AdminUser:WnE0TjGIafVJutreZL7cTIoWNWU52YyxLVgwCUqIGCGg3YUT@18.184.249.241:27017/?authMechanism=DEFAULT",
            type="password",
            help="Full MongoDB connection string (e.g., mongodb://user:pass@host:port/)",
            placeholder="mongodb://AdminUser:WnE0TjGIafVJutreZL7cTIoWNWU52YyxLVgwCUqIGCGg3YUT@18.184.249.241:27017/?authMechanism=DEFAULT"
        )
        
        # Database name
        database = st.text_input(
            "Database Name",
            value=current_settings['database'],
            help="Name of the MongoDB database to use",
            placeholder="IGF_WORK"
        )
        
        st.markdown("### Timeout Settings (milliseconds)")
        
        col1, col2 = st.columns(2)
        
        with col1:
            connection_timeout = st.number_input(
                "Connection Timeout",
                min_value=1000,
                max_value=60000,
                value=current_settings['connection_timeout'],
                step=1000,
                help="Time to wait for initial connection"
            )
        
        with col2:
            server_selection_timeout = st.number_input(
                "Server Selection Timeout",
                min_value=1000,
                max_value=60000,
                value=current_settings['server_selection_timeout'],
                step=1000,
                help="Time to wait for server selection"
            )
        
        st.markdown("---")
        
        # Action buttons
        col1, col2, col3 = st.columns([1, 1, 2])
        
        with col1:
            test_button = st.form_submit_button(
                "🧪 Test Connection",
                type="secondary"
            )
        
        with col2:
            save_button = st.form_submit_button(
                "💾 Save Settings",
                type="primary"
            )
    
    # Handle test connection
    if test_button:
        _handle_test_connection(
            settings_service,
            uri,
            database,
            connection_timeout,
            server_selection_timeout
        )
    
    # Handle save settings
    if save_button:
        # Store settings in session state for confirmation
        st.session_state.pending_settings = {
            'uri': uri,
            'database': database,
            'connection_timeout': connection_timeout,
            'server_selection_timeout': server_selection_timeout
        }
        st.rerun()
    
    # Show confirmation dialog if there are pending settings
    if 'pending_settings' in st.session_state:
        _show_save_confirmation(
            settings_service,
            mongo_service,
            st.session_state.pending_settings
        )


def _handle_test_connection(
    settings_service: SettingsService,
    uri: str,
    database: str,
    connection_timeout: int,
    server_selection_timeout: int
):
    """
    Handle test connection action.
    
    Args:
        settings_service: Settings service instance
        uri: MongoDB URI
        database: Database name
        connection_timeout: Connection timeout in ms
        server_selection_timeout: Server selection timeout in ms
    """
    with st.spinner("Testing connection..."):
        success, error = settings_service.test_connection(
            uri,
            database,
            connection_timeout,
            server_selection_timeout
        )
    
    if success:
        st.success("✅ **Connection test successful!**")
        st.info(f"Successfully connected to database: `{database}`")
    else:
        st.error("❌ **Connection test failed!**")
        st.error(f"**Error:** {error}")
        st.warning("Please check your connection settings and try again.")


def _show_save_confirmation(
    settings_service: SettingsService,
    mongo_service: MongoService,
    pending_settings: dict
):
    """
    Show confirmation dialog for pending settings.
    
    Args:
        settings_service: Settings service instance
        mongo_service: MongoDB service instance
        pending_settings: Dictionary with pending settings
    """
    st.markdown("---")
    st.warning("⚠️ **Confirm Settings Change**")
    st.markdown(f"""
    You are about to update the MongoDB connection settings:
    - **Database:** `{pending_settings['database']}`
    - **Connection Timeout:** {pending_settings['connection_timeout']}ms
    - **Server Selection Timeout:** {pending_settings['server_selection_timeout']}ms
    
    This will:
    1. Save settings to `app_config.yaml`
    2. Update the environment variable for this session
    3. Require a manual reconnection
    """)
    
    # Create confirmation buttons
    col1, col2 = st.columns(2)
    
    with col1:
        if st.button("✅ Confirm Save", use_container_width=True, type="primary", key="confirm_save_btn"):
            _execute_save(
                settings_service,
                mongo_service,
                pending_settings['uri'],
                pending_settings['database'],
                pending_settings['connection_timeout'],
                pending_settings['server_selection_timeout']
            )
            # Clear pending settings
            del st.session_state.pending_settings
    
    with col2:
        if st.button("❌ Cancel", use_container_width=True, key="cancel_save_btn"):
            # Clear pending settings
            del st.session_state.pending_settings
            st.info("Save cancelled. No changes were made.")
            st.rerun()


def _execute_save(
    settings_service: SettingsService,
    mongo_service: MongoService,
    uri: str,
    database: str,
    connection_timeout: int,
    server_selection_timeout: int
):
    """
    Execute the save operation.
    
    Args:
        settings_service: Settings service instance
        mongo_service: MongoDB service instance
        uri: MongoDB URI
        database: Database name
        connection_timeout: Connection timeout in ms
        server_selection_timeout: Server selection timeout in ms
    """
    with st.spinner("Saving settings..."):
        success, error = settings_service.save_mongodb_settings(
            uri,
            database,
            connection_timeout,
            server_selection_timeout
        )
    
    if success:
        st.success("✅ **Settings saved successfully!**")
        st.info("Settings have been saved to `app_config.yaml`")
        
        # Reload config in settings service
        settings_service.reload_config()
        
        # Auto-reconnect with new settings
        with st.spinner("Reconnecting to MongoDB with new settings..."):
            # Reload mongo service config from the updated file
            mongo_service.config = mongo_service._load_config("config/app_config.yaml")
            reconnect_success = mongo_service.connect()
        
        if reconnect_success:
            st.success("✅ **Reconnected successfully!**")
            st.info("MongoDB connection updated with new settings")
            st.balloons()
            # Force a full rerun to update all components
            import time
            time.sleep(1)
            st.rerun()
        else:
            st.error("❌ **Reconnection failed!**")
            conn_error = mongo_service.get_connection_error()
            if conn_error:
                st.error(f"**Error:** {conn_error}")
            st.warning("Please check your settings and try again.")
            
            if st.button("🔄 Retry Reconnection", type="primary", key="reconnect_btn"):
                st.rerun()
    else:
        st.error("❌ **Failed to save settings!**")
        st.error(f"**Error:** {error}")
        st.warning("Please verify your settings and try again.")


def _mask_uri(uri: str) -> str:
    """
    Mask sensitive parts of MongoDB URI.
    
    Args:
        uri: MongoDB connection URI
    
    Returns:
        Masked URI string
    """
    if not uri:
        return "Not configured"
    
    # Simple masking: show only protocol and host structure
    if '@' in uri:
        # Format: mongodb://user:pass@host:port/
        parts = uri.split('@')
        protocol_part = parts[0].split('//')[0] if '//' in parts[0] else 'mongodb:'
        host_part = parts[1] if len(parts) > 1 else 'unknown'
        return f"{protocol_part}//***:***@{host_part}"
    else:
        # Format: mongodb://host:port/
        return uri


def _render_collection_configuration(settings_service: SettingsService, mongo_service: MongoService):
    """
    Render collection configuration section.
    
    Args:
        settings_service: Settings service instance
        mongo_service: MongoDB service instance
    """
    st.markdown("""
    Configure business keys, alternative keys, and matching strategies for each collection.
    This determines how duplicate records are detected during import.
    """)
    
    # Get all collections
    collections = settings_service.get_all_collections()
    
    if not collections:
        st.info("ℹ️ No collections configured yet.")
        return
    
    # Select collection to configure
    collection_names = list(collections.keys())
    selected_collection = st.selectbox(
        "Select Collection to Configure",
        collection_names,
        help="Choose which collection you want to configure"
    )
    
    if selected_collection:
        _render_collection_editor(settings_service, selected_collection)


def _render_collection_editor(settings_service: SettingsService, collection_name: str):
    """
    Render editor for a specific collection.
    
    Args:
        settings_service: Settings service instance
        collection_name: Name of the collection to edit
    """
    # Get current configuration
    config = settings_service.get_collection_config(collection_name)
    
    st.subheader(f"⚙️ Configure: {collection_name}")
    
    with st.form(f"collection_config_{collection_name}"):
        st.markdown("### Business Keys (Primary)")
        st.markdown("Fields used to identify unique records. Enter one field name per line.")
        
        # Business keys
        current_business_keys = config.get('business_keys', [])
        business_keys_text = st.text_area(
            "Business Keys",
            value='\n'.join(current_business_keys),
            height=100,
            help="Example: code, athlete_id, player_id (one per line)",
            placeholder="code\nathlete_id"
        )
        
        st.markdown("### Alternative Keys (Secondary)")
        st.markdown("Additional fields used when primary keys don't match. Useful for finding the same person with different codes.")
        
        # Alternative keys
        current_alternative_keys = config.get('alternative_keys', [])
        alternative_keys_text = st.text_area(
            "Alternative Keys",
            value='\n'.join(current_alternative_keys),
            height=100,
            help="Example: given_name, family_name, birth_date (one per line)",
            placeholder="given_name\nfamily_name\nbirth_date"
        )
        
        st.markdown("### Matching Strategy")
        
        # Matching strategy
        current_strategy = config.get('matching_strategy', 'primary_only')
        matching_strategy = st.selectbox(
            "Matching Strategy",
            ['primary_only', 'primary_or_alternative', 'alternative_only'],
            index=['primary_only', 'primary_or_alternative', 'alternative_only'].index(current_strategy),
            help="""
            - primary_only: Match only by business keys
            - primary_or_alternative: Try business keys first, then alternative keys
            - alternative_only: Match only by alternative keys
            """
        )
        
        # Enabled checkbox
        enabled = st.checkbox(
            "Collection Enabled",
            value=config.get('enabled', True),
            help="Enable or disable this collection"
        )
        
        st.markdown("---")
        
        # Submit button
        submitted = st.form_submit_button(
            "💾 Save Collection Configuration",
            type="primary"
        )
        
        if submitted:
            # Parse keys
            business_keys = [k.strip() for k in business_keys_text.split('\n') if k.strip()]
            alternative_keys = [k.strip() for k in alternative_keys_text.split('\n') if k.strip()]
            
            # Validate
            if not business_keys and matching_strategy != 'alternative_only':
                st.error("❌ Business keys are required unless using 'alternative_only' strategy")
                return
            
            if not alternative_keys and matching_strategy in ['primary_or_alternative', 'alternative_only']:
                st.error("❌ Alternative keys are required for this matching strategy")
                return
            
            # Save configuration
            success, error = settings_service.update_collection_config(
                collection_name,
                business_keys,
                alternative_keys,
                matching_strategy,
                enabled
            )
            
            if success:
                st.success(f"✅ Configuration saved for collection: {collection_name}")
                st.info("""
                **Configuration updated:**
                - Business Keys: """ + ', '.join(business_keys) + """
                - Alternative Keys: """ + ', '.join(alternative_keys) + """
                - Matching Strategy: """ + matching_strategy + """
                - Enabled: """ + str(enabled) + """
                
                💡 Changes will take effect on the next import operation.
                """)
                st.balloons()
            else:
                st.error(f"❌ Failed to save configuration: {error}")
    
    # Show current configuration summary
    st.markdown("---")
    st.markdown("### Current Configuration")
    
    col1, col2 = st.columns(2)
    
    with col1:
        st.markdown("**Business Keys:**")
        if current_business_keys:
            for key in current_business_keys:
                st.text(f"  • {key}")
        else:
            st.text("  (none)")
    
    with col2:
        st.markdown("**Alternative Keys:**")
        if current_alternative_keys:
            for key in current_alternative_keys:
                st.text(f"  • {key}")
        else:
            st.text("  (none)")
    
    st.markdown(f"**Matching Strategy:** `{current_strategy}`")
    st.markdown(f"**Enabled:** {'✅ Yes' if config.get('enabled', True) else '❌ No'}")
    
    # Foreign Keys Section
    st.markdown("---")
    st.markdown("### Foreign Key Relationships")
    _render_foreign_key_editor(settings_service, collection_name)


def _render_record_limit_settings(settings_service: SettingsService):
    """
    Render record limit settings section.
    
    Args:
        settings_service: Settings service instance
    """
    st.markdown("""
    Configure the maximum number of records to load from MongoDB when comparing with JSON files.
    
    **Important:** If your collection has more records than this limit, only the first N records will be loaded for comparison.
    This may cause duplicate detection to miss some existing records.
    """)
    
    # Get current limit
    current_limit = settings_service.get_record_limit()
    
    st.info(f"📊 Current record limit: **{current_limit:,}** records")
    
    with st.form("record_limit_form"):
        st.markdown("### Set Record Limit")
        
        new_limit = st.number_input(
            "Maximum Records to Load",
            min_value=100,
            max_value=100000,
            value=current_limit,
            step=1000,
            help="Number of records to load from MongoDB for comparison (100 - 100,000)"
        )
        
        st.markdown("""
        **Recommendations:**
        - **Small collections (<1,000 records):** Use 5,000
        - **Medium collections (1,000-10,000 records):** Use 10,000-20,000
        - **Large collections (>10,000 records):** Use 50,000-100,000
        
        ⚠️ **Warning:** Higher limits use more memory and may slow down the comparison process.
        """)
        
        st.markdown("---")
        
        submitted = st.form_submit_button(
            "💾 Save Record Limit",
            type="primary"
        )
        
        if submitted:
            if new_limit == current_limit:
                st.info("ℹ️ Record limit unchanged")
            else:
                success, error = settings_service.update_record_limit(new_limit)
                
                if success:
                    st.success(f"✅ Record limit updated: {current_limit:,} → {new_limit:,}")
                    st.info("💡 The new limit will be used for all future comparison operations.")
                    st.balloons()
                else:
                    st.error(f"❌ Failed to update record limit: {error}")


def _render_foreign_key_editor(settings_service: SettingsService, collection_name: str):
    """
    Render foreign key relationship editor.
    
    Args:
        settings_service: Settings service instance
        collection_name: Name of the collection
    """
    st.markdown("""
    Define foreign key relationships to validate data integrity.
    The system will check if referenced values exist in target collections.
    """)
    
    # Get current foreign keys
    current_fks = settings_service.get_foreign_keys(collection_name)
    
    # Get all available collections for dropdown
    all_collections = list(settings_service.get_all_collections().keys())
    
    with st.form(f"foreign_keys_{collection_name}"):
        st.markdown("**Configure Foreign Keys**")
        st.markdown("Add relationships where fields in this collection reference fields in other collections.")
        
        # Number of foreign keys to configure
        num_fks = st.number_input(
            "Number of Foreign Keys",
            min_value=0,
            max_value=10,
            value=len(current_fks) if current_fks else 0,
            help="How many foreign key relationships to configure"
        )
        
        foreign_keys = []
        
        if num_fks > 0:
            st.markdown("---")
            
            for i in range(num_fks):
                st.markdown(f"**Foreign Key #{i+1}**")
                
                col1, col2, col3 = st.columns(3)
                
                # Get current values if exists
                current_fk = current_fks[i] if i < len(current_fks) else {}
                
                with col1:
                    field = st.text_input(
                        f"Field in {collection_name}",
                        value=current_fk.get('field', ''),
                        key=f"fk_field_{i}",
                        help="Field name in this collection (e.g., athlete_code, competition_code)",
                        placeholder="athlete_code"
                    )
                
                with col2:
                    ref_collection = st.selectbox(
                        "References Collection",
                        options=[''] + all_collections,
                        index=all_collections.index(current_fk.get('references_collection', '')) + 1 if current_fk.get('references_collection', '') in all_collections else 0,
                        key=f"fk_ref_coll_{i}",
                        help="Target collection name"
                    )
                
                with col3:
                    ref_field = st.text_input(
                        "References Field",
                        value=current_fk.get('references_field', ''),
                        key=f"fk_ref_field_{i}",
                        help="Field name in target collection (e.g., code, id)",
                        placeholder="code"
                    )
                
                if field and ref_collection and ref_field:
                    foreign_keys.append({
                        'field': field,
                        'references_collection': ref_collection,
                        'references_field': ref_field
                    })
                
                st.markdown("---")
        
        # Submit button
        submitted = st.form_submit_button(
            "💾 Save Foreign Keys",
            type="primary"
        )
        
        if submitted:
            # Validate that all foreign keys are complete
            if num_fks > 0 and len(foreign_keys) != num_fks:
                st.error("❌ Please fill in all foreign key fields or reduce the number of foreign keys")
                return
            
            # Save foreign keys
            success, error = settings_service.update_foreign_keys(collection_name, foreign_keys)
            
            if success:
                st.success(f"✅ Foreign keys saved for collection: {collection_name}")
                if foreign_keys:
                    st.info(f"**{len(foreign_keys)} foreign key(s) configured:**")
                    for fk in foreign_keys:
                        st.text(f"  • {fk['field']} → {fk['references_collection']}.{fk['references_field']}")
                else:
                    st.info("Foreign key validation disabled for this collection.")
                st.balloons()
            else:
                st.error(f"❌ Failed to save foreign keys: {error}")
    
    # Show current foreign keys
    if current_fks:
        st.markdown("**Current Foreign Keys:**")
        for idx, fk in enumerate(current_fks, 1):
            st.text(f"{idx}. {fk['field']} → {fk['references_collection']}.{fk['references_field']}")
    else:
        st.info("ℹ️ No foreign keys configured. Data integrity validation is disabled.")


def _render_index_management(mongo_service: MongoService):
    """
    Render index management section.
    
    Args:
        mongo_service: MongoDB service instance
    """
    st.header("🔍 Index Management")
    st.markdown("""
    Manage MongoDB indexes to fix duplicate key errors and optimize queries.
    
    **Common Issue:** Unique index on `email` field prevents importing records with `null` email values.
    
    **Solution:** Replace unique index with partial unique index that ignores `null` values.
    """)
    
    if not mongo_service.is_connected():
        st.warning("⚠️ Not connected to MongoDB. Please configure connection first.")
        return
    
    # Collection selector
    collections = mongo_service.list_collections()
    if not collections:
        st.info("ℹ️ No collections found in database")
        return
    
    selected_collection = st.selectbox(
        "Select Collection",
        options=collections,
        key="index_mgmt_collection"
    )
    
    if not selected_collection:
        return
    
    st.markdown("---")
    
    # List current indexes
    st.subheader(f"📋 Current Indexes for `{selected_collection}`")
    
    indexes = mongo_service.list_indexes(selected_collection)
    
    if indexes:
        for idx in indexes:
            index_name = idx.get('name', 'N/A')
            keys = idx.get('key', {})
            unique = idx.get('unique', False)
            partial = 'partialFilterExpression' in idx
            
            col1, col2, col3, col4 = st.columns([3, 1, 1, 2])
            
            with col1:
                st.text(f"📌 {index_name}")
                st.caption(f"Fields: {', '.join(keys.keys())}")
            
            with col2:
                if unique:
                    st.markdown("🔒 **Unique**")
                else:
                    st.text("Regular")
            
            with col3:
                if partial:
                    st.markdown("✅ **Partial**")
                else:
                    st.text("Full")
            
            with col4:
                # Don't allow dropping _id index
                if index_name != "_id_":
                    if st.button(f"🗑️ Drop", key=f"drop_{index_name}", help=f"Drop index {index_name}"):
                        if mongo_service.drop_index(selected_collection, index_name):
                            st.success(f"✅ Dropped index: {index_name}")
                            st.rerun()
                        else:
                            st.error(f"❌ Failed to drop index: {index_name}")
            
            st.markdown("---")
    else:
        st.info("ℹ️ No indexes found")
    
    # Quick fix for email index
    st.markdown("---")
    st.subheader("🔧 Quick Fix: Email Index")
    
    st.markdown("""
    **Problem:** Unique index on `email` field prevents multiple `null` values.
    
    **Solution:** 
    1. Drop the existing `email_1` unique index
    2. Create a partial unique index that only applies to non-null email values
    
    This allows multiple records with `null` email while maintaining uniqueness for actual email addresses.
    """)
    
    col1, col2 = st.columns(2)
    
    with col1:
        if st.button("🔧 Fix Email Index", type="primary", help="Drop old email_1 index and create partial unique index"):
            with st.spinner("Fixing email index..."):
                # Try to drop existing email_1 index
                dropped = mongo_service.drop_index(selected_collection, "email_1")
                
                # Create partial unique index
                created = mongo_service.create_partial_unique_index(selected_collection, "email")
                
                if created:
                    st.success("✅ Email index fixed! You can now import records with null email values.")
                    st.balloons()
                    st.rerun()
                else:
                    if dropped:
                        st.warning("⚠️ Old index dropped but failed to create new partial index")
                    else:
                        st.error("❌ Failed to fix email index")
    
    with col2:
        st.info("""
        **After fixing:**
        - Multiple records can have `null` email
        - Actual email addresses must be unique
        - Import will work without duplicate key errors
        """)
