"""
Generic Collection Tab

A universal tab component that can display and edit ANY MongoDB collection.
This single component handles all collections configured in app_config.yaml.

Key features:
- Load any collection by name
- Display data in editable table
- Preview changes before applying
- Apply changes to MongoDB
- Discard changes and reload
"""

import streamlit as st
import pandas as pd
from typing import Dict, Any, List, Optional
from services.data_service import DataService
from services.schema_manager import SchemaManager
from ui.components.editable_table import render_editable_table, render_change_preview


def render_collection_tab(
    collection_name: str,
    mongo_service,
    data_service: DataService,
    schema_manager: SchemaManager = None
) -> None:
    """
    Render a generic collection management tab.
    
    This is a universal component that works with ANY collection.
    It loads data, displays an editable table, and handles save operations.
    
    Args:
        collection_name: Name of the collection to manage
        mongo_service: MongoService instance
        data_service: DataService instance
        schema_manager: SchemaManager instance (optional)
    """
    
    # Check MongoDB connection
    if not mongo_service.is_connected():
        st.error("❌ Not connected to MongoDB. Please check the sidebar.")
        return
    
    # Initialize schema manager if not provided
    if schema_manager is None:
        schema_manager = SchemaManager(mongo_service)
    
    # Initialize session state for this collection
    _init_collection_state(collection_name)
    
    # Schema Manager Panel (collapsible)
    _render_schema_manager_panel(collection_name, schema_manager)
    
    st.markdown("---")
    
    # Load data button and controls
    _render_load_controls(collection_name, data_service)
    
    # If data is loaded, show editable table
    if st.session_state[f'{collection_name}_loaded']:
        _render_data_editor(collection_name, data_service)
    else:
        # Show placeholder
        st.info(f"👆 Click 'Load Data' to view and edit **{collection_name}** collection")


def _init_collection_state(collection_name: str) -> None:
    """
    Initialize session state for a collection.
    
    Args:
        collection_name: Name of the collection
    """
    # Track if data is loaded
    if f'{collection_name}_loaded' not in st.session_state:
        st.session_state[f'{collection_name}_loaded'] = False
    
    # Store original DataFrame
    if f'{collection_name}_original_df' not in st.session_state:
        st.session_state[f'{collection_name}_original_df'] = None
    
    # Store edited DataFrame
    if f'{collection_name}_edited_df' not in st.session_state:
        st.session_state[f'{collection_name}_edited_df'] = None
    
    # Track if preview is shown
    if f'{collection_name}_show_preview' not in st.session_state:
        st.session_state[f'{collection_name}_show_preview'] = False


def _render_load_controls(collection_name: str, data_service: DataService) -> None:
    """
    Render controls for loading collection data.
    
    Args:
        collection_name: Name of the collection
        data_service: DataService instance
    """
    col1, col2, col3 = st.columns([2, 1, 1])
    
    with col1:
        # Document limit selector
        limit = st.number_input(
            "Documents to load",
            min_value=10,
            max_value=5000,
            value=500,
            step=50,
            key=f'{collection_name}_limit',
            help="Maximum number of documents to load from MongoDB"
        )
    
    with col2:
        # Load button
        if st.button(
            "📥 Load Data",
            key=f'{collection_name}_load_btn',
            width="stretch"
        ):
            _load_collection_data(collection_name, data_service, limit)
    
    with col3:
        # Reload button (only show if data is loaded)
        if st.session_state[f'{collection_name}_loaded']:
            if st.button(
                "🔄 Reload",
                key=f'{collection_name}_reload_btn',
                width="stretch",
                help="Discard changes and reload from MongoDB"
            ):
                _load_collection_data(collection_name, data_service, limit)
                st.session_state[f'{collection_name}_show_preview'] = False
                st.rerun()


def _load_collection_data(
    collection_name: str,
    data_service: DataService,
    limit: int
) -> None:
    """
    Load collection data from MongoDB.
    
    Args:
        collection_name: Name of the collection
        data_service: DataService instance
        limit: Maximum documents to load
    """
    with st.spinner(f"Loading {collection_name}..."):
        try:
            # Load data as DataFrame
            df = data_service.load_collection_as_dataframe(
                collection_name,
                limit=limit
            )
            
            # Store in session state
            st.session_state[f'{collection_name}_original_df'] = df.copy()
            st.session_state[f'{collection_name}_edited_df'] = df.copy()
            st.session_state[f'{collection_name}_loaded'] = True
            st.session_state[f'{collection_name}_show_preview'] = False
            
            st.success(f"✅ Loaded {len(df)} documents from {collection_name}")
        
        except Exception as e:
            st.error(f"❌ Error loading data: {str(e)}")
            st.session_state[f'{collection_name}_loaded'] = False


def _render_data_editor(collection_name: str, data_service: DataService) -> None:
    """
    Render the data editor and action buttons.
    
    Args:
        collection_name: Name of the collection
        data_service: DataService instance
    """
    st.markdown("---")
    
    # Get DataFrames from session state
    original_df = st.session_state[f'{collection_name}_original_df']
    
    # Render editable table
    edited_df = render_editable_table(
        collection_name,
        st.session_state[f'{collection_name}_edited_df'],
        key_suffix=str(hash(collection_name))
    )
    
    # Update edited DataFrame in session state
    st.session_state[f'{collection_name}_edited_df'] = edited_df
    
    st.markdown("---")
    
    # Action buttons
    _render_action_buttons(collection_name, data_service, original_df, edited_df)
    
    # Show preview if requested
    if st.session_state[f'{collection_name}_show_preview']:
        _render_preview_section(collection_name, data_service, original_df, edited_df)


def _render_action_buttons(
    collection_name: str,
    data_service: DataService,
    original_df: pd.DataFrame,
    edited_df: pd.DataFrame
) -> None:
    """
    Render action buttons for preview, apply, and discard.
    
    Args:
        collection_name: Name of the collection
        data_service: DataService instance
        original_df: Original DataFrame
        edited_df: Edited DataFrame
    """
    col1, col2, col3, col4 = st.columns([2, 1, 1, 1])
    
    with col2:
        if st.button(
            "🔍 Preview Changes",
            key=f'{collection_name}_preview_btn',
            width="stretch",
            type="secondary"
        ):
            st.session_state[f'{collection_name}_show_preview'] = True
            st.rerun()
    
    with col3:
        if st.button(
            "✅ Apply Changes",
            key=f'{collection_name}_apply_btn',
            width="stretch",
            type="primary"
        ):
            _apply_changes(collection_name, data_service, original_df, edited_df)
    
    with col4:
        if st.button(
            "❌ Discard Changes",
            key=f'{collection_name}_discard_btn',
            width="stretch"
        ):
            # Reset to original
            st.session_state[f'{collection_name}_edited_df'] = original_df.copy()
            st.session_state[f'{collection_name}_show_preview'] = False
            st.success("✅ Changes discarded")
            st.rerun()


def _render_preview_section(
    collection_name: str,
    data_service: DataService,
    original_df: pd.DataFrame,
    edited_df: pd.DataFrame
) -> None:
    """
    Render the change preview section.
    
    Args:
        collection_name: Name of the collection
        data_service: DataService instance
        original_df: Original DataFrame
        edited_df: Edited DataFrame
    """
    st.markdown("---")
    
    # Generate preview
    with st.spinner("Analyzing changes..."):
        preview_data = data_service.preview_changes(original_df, edited_df)
    
    # Check if there are any changes
    total_changes = (
        preview_data['new_count'] +
        preview_data['updated_count'] +
        preview_data['deleted_count']
    )
    
    if total_changes == 0:
        st.info("ℹ️ No changes detected")
        
        # Hide preview button
        if st.button("Close Preview", key=f'{collection_name}_close_preview'):
            st.session_state[f'{collection_name}_show_preview'] = False
            st.rerun()
    else:
        # Render detailed preview
        render_change_preview(preview_data)
        
        # Confirmation section
        st.markdown("---")
        
        col1, col2 = st.columns([3, 1])
        
        with col1:
            st.warning(
                f"⚠️ **You are about to modify {total_changes} document(s) in {collection_name}**",
                icon="⚠️"
            )
        
        with col2:
            if st.button(
                "Close Preview",
                key=f'{collection_name}_close_preview',
                use_container_width=True
            ):
                st.session_state[f'{collection_name}_show_preview'] = False
                st.rerun()


def _apply_changes(
    collection_name: str,
    data_service: DataService,
    original_df: pd.DataFrame,
    edited_df: pd.DataFrame
) -> None:
    """
    Apply changes to MongoDB.
    
    Args:
        collection_name: Name of the collection
        data_service: DataService instance
        original_df: Original DataFrame
        edited_df: Edited DataFrame
    """
    # Show confirmation dialog
    st.warning("⚠️ **Confirm Changes**")
    st.markdown(f"You are about to apply changes to **{collection_name}** collection.")
    
    col1, col2 = st.columns(2)
    
    with col1:
        if st.button(
            "✅ Confirm Apply",
            key=f'{collection_name}_confirm_apply',
            type="primary",
            use_container_width=True
        ):
            # Apply changes
            with st.spinner("Applying changes to MongoDB..."):
                results = data_service.apply_changes(
                    collection_name,
                    original_df,
                    edited_df
                )
            
            # Show results
            if results['success']:
                st.success(f"""
                ✅ **Changes Applied Successfully**
                
                - Inserted: {results['inserted']} documents
                - Updated: {results['updated']} documents
                - Deleted: {results['deleted']} documents
                """)
                
                # Reload data to reflect changes
                limit = st.session_state.get(f'{collection_name}_limit', 500)
                _load_collection_data(collection_name, data_service, limit)
                
                st.rerun()
            else:
                st.error(f"❌ Error applying changes: {results.get('error', 'Unknown error')}")
                
                if results.get('errors'):
                    with st.expander("Error Details"):
                        for error in results['errors']:
                            st.error(error)
    
    with col2:
        if st.button(
            "❌ Cancel",
            key=f'{collection_name}_cancel_apply',
            use_container_width=True
        ):
            st.info("Operation cancelled")
            st.rerun()


def _render_schema_manager_panel(
    collection_name: str,
    schema_manager: SchemaManager
) -> None:
    """
    Render the Schema Manager panel for adding fields to a collection.
    
    This panel allows users to manually add new fields to all documents
    in the collection with explicit preview and confirmation.
    
    Args:
        collection_name: Name of the collection
        schema_manager: SchemaManager instance
    """
    with st.expander("🧩 Schema Manager", expanded=False):
        st.markdown("""
        **Add a new field to all documents in this collection.**
        
        - Field will be added with NULL value
        - Existing data will NOT be modified
        - Operation is safe and idempotent
        """)
        
        # Initialize session state for schema manager
        if f'{collection_name}_schema_preview' not in st.session_state:
            st.session_state[f'{collection_name}_schema_preview'] = None
        
        # Get existing fields
        existing_fields = schema_manager.get_existing_fields(collection_name)
        
        if existing_fields:
            with st.expander("📋 Existing Fields", expanded=False):
                st.info(f"**Total Fields:** {len(existing_fields)}")
                # Display in columns
                cols = st.columns(3)
                for i, field in enumerate(existing_fields):
                    with cols[i % 3]:
                        st.text(f"• {field}")
        
        st.markdown("---")
        
        # Field addition form
        col1, col2 = st.columns([2, 1])
        
        with col1:
            new_field_name = st.text_input(
                "Field Name",
                key=f'{collection_name}_new_field_name',
                placeholder="e.g., email, phone, status",
                help="Name of the new field to add"
            )
        
        with col2:
            field_type = st.selectbox(
                "Field Type",
                options=list(schema_manager.FIELD_TYPES.keys()),
                key=f'{collection_name}_field_type',
                help="Type of the field (all will be null initially)"
            )
        
        # Action buttons
        col1, col2, col3 = st.columns([2, 1, 1])
        
        with col2:
            if st.button(
                "🔍 Preview",
                key=f'{collection_name}_schema_preview_btn',
                width="stretch",
                disabled=not new_field_name or not new_field_name.strip()
            ):
                # Generate preview
                preview = schema_manager.preview_field_addition(
                    collection_name,
                    new_field_name.strip(),
                    field_type
                )
                st.session_state[f'{collection_name}_schema_preview'] = preview
                st.rerun()
        
        with col3:
            # Show Apply button only if preview is valid
            preview = st.session_state[f'{collection_name}_schema_preview']
            apply_disabled = not preview or not preview.get('valid', False)
            
            if st.button(
                "✅ Apply",
                key=f'{collection_name}_schema_apply_btn',
                width="stretch",
                type="primary",
                disabled=apply_disabled
            ):
                _apply_field_addition(collection_name, schema_manager, preview)
        
        # Show preview if available
        if preview:
            st.markdown("---")
            _render_schema_preview(collection_name, preview)


def _render_schema_preview(collection_name: str, preview: Dict[str, Any]) -> None:
    """
    Render the schema change preview.
    
    Args:
        collection_name: Name of the collection
        preview: Preview data from schema_manager
    """
    if not preview.get('valid'):
        # Show error
        if preview.get('warning'):
            st.warning(f"⚠️ {preview.get('error')}")
        else:
            st.error(f"❌ {preview.get('error')}")

        # Clear preview button
        if st.button("Clear", key=f'{collection_name}_clear_preview'):
            st.session_state[f'{collection_name}_schema_preview'] = None
            st.rerun()
    
    else:
        # Show valid preview
        st.success("✅ Field addition is valid")
        
        # Preview details
        st.markdown("### 📊 Preview")
        
        col1, col2 = st.columns(2)
        
        with col1:
            st.metric("Collection", preview['collection_name'])
            st.metric("Field Name", preview['field_name'])
            st.metric("Field Type", preview['field_type'])
        
        with col2:
            st.metric("Total Documents", f"{preview['total_documents']:,}")
            st.metric("Documents to Update", f"{preview['documents_to_update']:,}")
            st.metric("Already Have Field", f"{preview['documents_already_have_field']:,}")
        
        # Warning message
        st.warning(f"""
        ⚠️ **This will add a NULL field to {preview['documents_to_update']:,} document(s)**
        
        - Field name: `{preview['field_name']}`
        - Field type: `{preview['field_type']}`
        - Default value: `null`
        - Existing data will NOT be modified
        - Operation is idempotent (safe to re-run)
        """)
        
        # Clear preview button
        if st.button("Clear Preview", key=f'{collection_name}_clear_schema_preview'):
            st.session_state[f'{collection_name}_schema_preview'] = None
            st.rerun()


def _apply_field_addition(
    collection_name: str,
    schema_manager: SchemaManager,
    preview: Dict[str, Any]
) -> None:
    """
    Apply the field addition to the collection.
    
    Args:
        collection_name: Name of the collection
        schema_manager: SchemaManager instance
        preview: Preview data
    """
    if not preview or not preview.get('valid'):
        st.error("❌ Invalid preview data")
        return
    
    # Show confirmation
    st.warning("⚠️ **Confirm Field Addition**")
    st.markdown(f"""
    You are about to add field **`{preview['field_name']}`** to **{preview['documents_to_update']:,}** documents.
    """)
    
    col1, col2 = st.columns(2)
    
    with col1:
        if st.button(
            "✅ Confirm Add Field",
            key=f'{collection_name}_confirm_schema_apply',
            type="primary",
            use_container_width=True
        ):
            # Apply the field addition
            with st.spinner(f"Adding field '{preview['field_name']}' to {collection_name}..."):
                result = schema_manager.add_field_to_collection(
                    preview['collection_name'],
                    preview['field_name'],
                    preview['field_type']
                )
            
            # Show results
            if result['success']:
                st.success(f"""
                ✅ **Field Added Successfully**
                
                {result['message']}
                
                - Documents matched: {result['documents_matched']:,}
                - Documents modified: {result['documents_modified']:,}
                """)
                
                # Clear preview
                st.session_state[f'{collection_name}_schema_preview'] = None
                
                # Reload data if it's loaded
                if st.session_state.get(f'{collection_name}_loaded', False):
                    st.info("💡 Reload the data to see the new field")
                
                st.rerun()
            else:
                st.error(f"❌ Error adding field: {result.get('error', 'Unknown error')}")
    
    with col2:
        if st.button(
            "❌ Cancel",
            key=f'{collection_name}_cancel_schema_apply',
            use_container_width=True
        ):
            st.info("Operation cancelled")
            st.rerun()
