import gradio as gr
import matplotlib.pyplot as plt
import numpy as np
import sqlite3
from matplotlib.figure import Figure
from typing import Dict, List, Optional, Tuple
import pandas as pd
from PIL import Image
from dog_database import get_dog_description
from scoring_calculation_system import UserPreferences, calculate_compatibility_score
def create_visualization_tab(dog_breeds, get_dog_description, calculate_compatibility_score, UserPreferences):
    """Create a visualization tab for breed characteristic analysis"""
    
    # Create shared state container
    shared_preferences = gr.State({
        "living_space": "apartment",
        "yard_access": "no_yard",
        "exercise_time": 60,
        "exercise_type": "moderate_activity",
        "grooming_commitment": "medium",
        "experience_level": "beginner",
        "noise_tolerance": "medium",
        "has_children": False,
        "children_age": "school_age",
        "climate": "moderate"
    })
    
    gr.HTML("""
        
            
                Gain deeper insight into dog breed characteristics through visualization to help you make a more informed choice.
            
         
    """)
    
    with gr.Tabs():
        # Single breed radar chart analysis tab
        with gr.TabItem("Breed Radar Chart Analysis"):
            with gr.Row():
                with gr.Column(scale=1):
                    # User interface components - Left side
                    breed_choices = [(breed.replace('_', ' '), breed) for breed in sorted(dog_breeds)]
                    
                    breed_dropdown = gr.Dropdown(
                        label="Select Breed",
                        choices=breed_choices,
                        value=breed_choices[0][1] if breed_choices else None,
                        info="Select a breed to view its characteristics radar chart"
                    )
                    
                    with gr.Accordion("User Preferences (Affects Scoring)", open=False):
                        living_space = gr.Radio(
                            label="Living Space",
                            choices=["apartment", "house_small", "house_large"],
                            value="apartment",
                            info="Your residential environment type"
                        )
                        
                        yard_access = gr.Radio(
                            label="Yard Condition",
                            choices=["no_yard", "shared_yard", "private_yard"],
                            value="no_yard",
                            info="Whether you have yard space"
                        )
                        
                        exercise_time = gr.Slider(
                            label="Daily Exercise Time (minutes)",
                            minimum=15,
                            maximum=180,
                            value=60,
                            step=15,
                            info="Daily exercise time you can provide"
                        )
                        
                        exercise_type = gr.Radio(
                            label="Exercise Type",
                            choices=["light_walks", "moderate_activity", "active_training"],
                            value="moderate_activity",
                            info="Your preferred exercise method"
                        )
                        
                        grooming_commitment = gr.Radio(
                            label="Grooming Commitment",
                            choices=["low", "medium", "high"],
                            value="medium",
                            info="Level of grooming care you're willing to provide"
                        )
                        
                        experience_level = gr.Radio(
                            label="Experience Level",
                            choices=["beginner", "intermediate", "advanced"],
                            value="beginner",
                            info="Your level of dog owning experience"
                        )
                        
                        noise_tolerance = gr.Radio(
                            label="Noise Tolerance",
                            choices=["low", "medium", "high"],
                            value="medium",
                            info="Your acceptance level of dog barking"
                        )
                        
                        has_children = gr.Checkbox(
                            label="Have Children",
                            value=False,
                            info="Whether you have children at home"
                        )
                        
                        children_age = gr.Radio(
                            label="Children's Age",
                            choices=["toddler", "school_age", "teenager"],
                            value="school_age",
                            visible=False,
                            info="Age group of children at home"
                        )
                        
                        climate = gr.Radio(
                            label="Climate Environment",
                            choices=["cold", "moderate", "hot"],
                            value="moderate",
                            info="Climate characteristics of your living area"
                        )
                        
                        # Listen for has_children changes to control children_age display
                        has_children.change(
                            fn=lambda x: gr.update(visible=x),
                            inputs=has_children,
                            outputs=children_age
                        )
                        
                        # Add function to update shared preferences
                        def update_shared_preferences(*args):
                            return {
                                "living_space": args[0],
                                "yard_access": args[1],
                                "exercise_time": args[2],
                                "exercise_type": args[3],
                                "grooming_commitment": args[4],
                                "experience_level": args[5],
                                "noise_tolerance": args[6],
                                "has_children": args[7],
                                "children_age": args[8],
                                "climate": args[9]
                            }
                        
                        # Monitor preference changes and update shared state
                        all_preferences = [living_space, yard_access, exercise_time, 
                                          exercise_type, grooming_commitment, experience_level, 
                                          noise_tolerance, has_children, children_age, climate]
                        
                        for pref in all_preferences:
                            pref.change(
                                update_shared_preferences,
                                inputs=all_preferences,
                                outputs=shared_preferences
                            )
                    
                    generate_btn = gr.Button("Generate Radar Chart", variant="primary")
                
                with gr.Column(scale=2):
                    # Right display area
                    radar_plot = gr.Plot(label="Breed Characteristics Radar Chart")
                    breed_details = gr.JSON(label="Breed Detailed Information")
            
            # Button click event
            generate_btn.click(
                fn=lambda *args: generate_radar_chart(
                    args[0], create_user_preferences(*args[1:]), 
                    get_dog_description, calculate_compatibility_score
                ),
                inputs=[breed_dropdown, living_space, yard_access, exercise_time, 
                       exercise_type, grooming_commitment, experience_level, 
                       noise_tolerance, has_children, children_age, climate],
                outputs=[radar_plot, breed_details]
            )
        
        # Breed comparison analysis tab - Improved version
        with gr.TabItem("Breed Comparison Analysis"):
            with gr.Row():
                breed1_dropdown = gr.Dropdown(
                    label="Select First Breed",
                    choices=breed_choices,
                    value=breed_choices[0][1] if breed_choices else None
                )
                
                breed2_dropdown = gr.Dropdown(
                    label="Select Second Breed",
                    choices=breed_choices,
                    value=breed_choices[1][1] if len(breed_choices) > 1 else None
                )
            
            with gr.Row():
                use_shared_settings = gr.Checkbox(
                    label="Use Radar Chart Analysis Settings",
                    value=True,
                    info="Check to use the same preferences from the Radar Chart Analysis tab"
                )
            
            # Custom settings container - only visible when not using shared settings
            with gr.Column(visible=False) as custom_settings:
                with gr.Accordion("Custom Preferences", open=True):
                    comp_living_space = gr.Radio(
                        label="Living Space",
                        choices=["apartment", "house_small", "house_large"],
                        value="apartment"
                    )
                    
                    comp_yard_access = gr.Radio(
                        label="Yard Condition",
                        choices=["no_yard", "shared_yard", "private_yard"],
                        value="no_yard"
                    )
                    
                    comp_exercise_time = gr.Slider(
                        label="Daily Exercise Time (minutes)",
                        minimum=15,
                        maximum=180,
                        value=60,
                        step=15
                    )
                    
                    comp_exercise_type = gr.Radio(
                        label="Exercise Type",
                        choices=["light_walks", "moderate_activity", "active_training"],
                        value="moderate_activity"
                    )
            
            # Toggle custom settings visibility based on checkbox
            use_shared_settings.change(
                fn=lambda x: gr.update(visible=not x),
                inputs=use_shared_settings,
                outputs=custom_settings
            )
            
            compare_btn = gr.Button("Compare Breeds", variant="primary")
            comparison_plot = gr.Plot(label="Breed Characteristics Comparison")
            
            # Improved comparison function that handles both shared and custom settings
            def get_comparison_settings(use_shared, shared_prefs, *custom_prefs):
                """
                Select appropriate settings based on user choice
                
                Args:
                    use_shared: Boolean indicating whether to use shared settings
                    shared_prefs: Dictionary of shared preferences
                    custom_prefs: Custom preference values if not using shared
                    
                Returns:
                    UserPreferences object with the selected settings
                """
                if use_shared:
                    # Use settings from Radar Chart tab
                    return create_user_preferences_from_dict(shared_prefs)
                else:
                    # Use custom settings from Comparison tab
                    return create_user_preferences(
                        custom_prefs[0], custom_prefs[1], custom_prefs[2], custom_prefs[3],
                        "medium", "beginner", "medium", False, "school_age", "moderate"
                    )
            
            # Connect the comparison button
            compare_btn.click(
                fn=lambda breed1, breed2, use_shared, shared_prefs, *custom_prefs: generate_comparison_chart(
                    breed1, breed2, 
                    get_comparison_settings(use_shared, shared_prefs, *custom_prefs),
                    get_dog_description, calculate_compatibility_score
                ),
                inputs=[
                    breed1_dropdown, breed2_dropdown, 
                    use_shared_settings, shared_preferences,
                    comp_living_space, comp_yard_access, 
                    comp_exercise_time, comp_exercise_type
                ],
                outputs=comparison_plot
            )
    return None
def create_user_preferences(living_space, yard_access, exercise_time, exercise_type, 
                          grooming_commitment, experience_level, noise_tolerance, 
                          has_children, children_age, climate):
    """
    Create UserPreferences object from UI inputs
    
    Args:
        living_space: Type of living environment
        yard_access: Yard availability
        exercise_time: Minutes of daily exercise
        exercise_type: Type of exercise activity
        grooming_commitment: Level of grooming commitment
        experience_level: Dog owner experience level
        noise_tolerance: Tolerance for barking
        has_children: Whether there are children in the home
        children_age: Age group of children
        climate: Climate type of the living area
        
    Returns:
        UserPreferences object with the specified settings
    """
    return UserPreferences(
        living_space=living_space,
        yard_access=yard_access,
        exercise_time=exercise_time,
        exercise_type=exercise_type,
        grooming_commitment=grooming_commitment,
        experience_level=experience_level,
        time_availability="moderate",  # Default value
        has_children=has_children,
        children_age=children_age if has_children else "school_age",
        noise_tolerance=noise_tolerance,
        space_for_play=True,  # Default value
        other_pets=False,  # Default value
        climate=climate
    )
def create_user_preferences_from_dict(prefs_dict):
    """
    Create UserPreferences object from a dictionary
    
    Args:
        prefs_dict: Dictionary containing preference values
        
    Returns:
        UserPreferences object populated with the dictionary values
    """
    return UserPreferences(
        living_space=prefs_dict["living_space"],
        yard_access=prefs_dict["yard_access"],
        exercise_time=prefs_dict["exercise_time"],
        exercise_type=prefs_dict["exercise_type"],
        grooming_commitment=prefs_dict["grooming_commitment"],
        experience_level=prefs_dict["experience_level"],
        time_availability="moderate",  # Default value
        has_children=prefs_dict["has_children"],
        children_age=prefs_dict["children_age"],
        noise_tolerance=prefs_dict["noise_tolerance"],
        space_for_play=True,  # Default value
        other_pets=False,  # Default value
        climate=prefs_dict["climate"]
    )
def generate_radar_chart(breed_name, user_prefs, get_dog_description, calculate_compatibility_score):
    """
    Generate radar chart for a single breed
    
    Args:
        breed_name: Dog breed name
        user_prefs: UserPreferences object
        get_dog_description: Function to get breed description
        calculate_compatibility_score: Function to calculate compatibility score
    
    Returns:
        tuple: (matplotlib figure, breed description dict)
    """
    try:
        # Get breed description
        breed_info = get_dog_description(breed_name)
        
        if not breed_info:
            # Create empty figure with error message
            fig = Figure(figsize=(8, 8))
            ax = fig.add_subplot(111)
            ax.text(0.5, 0.5, f"No information found for breed: {breed_name}",
                   horizontalalignment='center', verticalalignment='center', 
                   transform=ax.transAxes, fontsize=14)
            ax.axis('off')
            return fig, {"error": f"No information found for breed: {breed_name}"}
        
        # Calculate compatibility scores
        scores = calculate_compatibility_score(breed_info, user_prefs)
        
        # Prepare data for radar chart
        categories = ['Space Compatibility', 'Exercise Needs', 'Grooming', 
                     'Experience Required', 'Health', 'Noise Level']
        values = [scores['space'], scores['exercise'], scores['grooming'], 
                 scores['experience'], scores['health'], scores['noise']]
        
        # Close the polygon by appending first value
        values_closed = values + [values[0]]
        categories_closed = categories + [categories[0]]
        
        # Calculate angles for each category
        angles = np.linspace(0, 2*np.pi, len(categories), endpoint=False).tolist()
        angles += angles[:1]  # Close the loop
        
        # Create figure and polar axis
        fig = Figure(figsize=(10, 8))
        ax = fig.add_subplot(111, polar=True)
        
        # Plot data
        ax.fill(angles, values_closed, color='skyblue', alpha=0.25)
        ax.plot(angles, values_closed, color='blue', linewidth=2)
        
        # Add category labels
        ax.set_xticks(angles[:-1])
        ax.set_xticklabels(categories, fontsize=12)
        
        # Configure y-axis
        ax.set_yticks([0.2, 0.4, 0.6, 0.8, 1.0])
        ax.set_yticklabels(['0.2', '0.4', '0.6', '0.8', '1.0'], fontsize=10)
        ax.set_ylim(0, 1)
        
        # Add a title
        breed_display_name = breed_name.replace('_', ' ')
        ax.set_title(f"{breed_display_name} Characteristic Scores", fontsize=16, pad=20)
        
        # Add value labels at each point
        for i, (angle, value) in enumerate(zip(angles[:-1], values)):
            ax.text(angle, value + 0.05, f"{value:.2f}", 
                   ha='center', va='center', fontsize=10,
                   bbox=dict(facecolor='white', alpha=0.7, boxstyle="round,pad=0.3"))
        
        # Add grid
        ax.grid(True, linestyle='--', alpha=0.7)
        
        # Add overall score text
        overall_score = scores.get('overall', 0)
        fig.text(0.5, 0.02, f"Overall Match Score: {overall_score:.2f}", 
                ha='center', fontsize=14, 
                bbox=dict(facecolor='lightgreen', alpha=0.3, boxstyle="round,pad=0.5"))
        
        # Enhance aesthetics
        fig.patch.set_facecolor('#f8f9fa')
        ax.set_facecolor('#f0f0f0')
        
        # Print debug information
        print(f"Generated radar chart for {breed_name}")
        print(f"Scores: {scores}")
        
        return fig, breed_info
    
    except Exception as e:
        # Create empty figure with error message
        fig = Figure(figsize=(8, 8))
        ax = fig.add_subplot(111)
        ax.text(0.5, 0.5, f"Error generating chart: {str(e)}",
               horizontalalignment='center', verticalalignment='center', 
               transform=ax.transAxes, fontsize=14)
        ax.axis('off')
        print(f"Error in generate_radar_chart: {str(e)}")
        return fig, {"error": f"Error generating chart: {str(e)}"}
def generate_comparison_chart(breed1, breed2, user_prefs, get_dog_description, calculate_compatibility_score):
    """
    Generate comparison chart for two breeds
    
    Args:
        breed1, breed2: Dog breed names
        user_prefs: UserPreferences object
        get_dog_description: Function to get breed description
        calculate_compatibility_score: Function to calculate compatibility score
    
    Returns:
        matplotlib figure: Comparison chart
    """
    try:
        # Get breed descriptions
        breed1_info = get_dog_description(breed1)
        breed2_info = get_dog_description(breed2)
        
        if not breed1_info or not breed2_info:
            # Create empty figure with error message
            fig = Figure(figsize=(10, 6))
            ax = fig.add_subplot(111)
            ax.text(0.5, 0.5, f"Missing breed information. Please check both breeds.",
                   horizontalalignment='center', verticalalignment='center', 
                   transform=ax.transAxes, fontsize=14)
            ax.axis('off')
            return fig
        
        # Calculate compatibility scores
        scores1 = calculate_compatibility_score(breed1_info, user_prefs)
        scores2 = calculate_compatibility_score(breed2_info, user_prefs)
        
        # Prepare data for bar chart
        categories = ['Space Compatibility', 'Exercise Needs', 'Grooming', 
                     'Experience Required', 'Health', 'Noise Level']
        values1 = [scores1['space'], scores1['exercise'], scores1['grooming'], 
                  scores1['experience'], scores1['health'], scores1['noise']]
        values2 = [scores2['space'], scores2['exercise'], scores2['grooming'], 
                  scores2['experience'], scores2['health'], scores2['noise']]
        
        # Create figure
        fig = Figure(figsize=(12, 7))
        ax = fig.add_subplot(111)
        
        # Set width of bars
        x = np.arange(len(categories))
        width = 0.35
        
        # Plot bars
        breed1_display = breed1.replace('_', ' ')
        breed2_display = breed2.replace('_', ' ')
        
        rects1 = ax.bar(x - width/2, values1, width, label=breed1_display, color='#4299e1')
        rects2 = ax.bar(x + width/2, values2, width, label=breed2_display, color='#f56565')
        
        # Add labels and title
        ax.set_xlabel('Scoring Dimensions', fontsize=12)
        ax.set_ylabel('Score (0-1)', fontsize=12)
        ax.set_title(f'{breed1_display} vs {breed2_display} Breed Comparison', fontsize=15)
        ax.set_xticks(x)
        ax.set_xticklabels(categories, rotation=30, ha='right')
        ax.legend(loc='upper right')
        
        # Add value labels on top of bars
        def add_labels(rects):
            for rect in rects:
                height = rect.get_height()
                ax.annotate(f'{height:.2f}',
                            xy=(rect.get_x() + rect.get_width() / 2, height),
                            xytext=(0, 3),  # 3 points vertical offset
                            textcoords="offset points",
                            ha='center', va='bottom',
                            fontsize=9, fontweight='bold')
        
        add_labels(rects1)
        add_labels(rects2)
        
        # Set y-axis limit
        ax.set_ylim(0, 1.1)
        
        # Add grid
        ax.grid(True, linestyle='--', alpha=0.3, axis='y')
        
        # Add overall score comparison
        overall1 = scores1.get('overall', 0)
        overall2 = scores2.get('overall', 0)
        
        fig.text(0.5, 0.02, 
                f"Overall Match Scores:  {breed1_display}: {overall1:.2f}  |  {breed2_display}: {overall2:.2f}", 
                ha='center', fontsize=13, 
                bbox=dict(facecolor='#edf2f7', alpha=0.7, boxstyle="round,pad=0.5"))
        
        # Enhance aesthetics
        fig.patch.set_facecolor('#f8f9fa')
        ax.set_facecolor('#f0f0f0')
        
        # Add a tight layout to ensure everything fits
        fig.tight_layout(rect=[0, 0.05, 1, 0.95])
        
        # Print debug information
        print(f"Generated comparison chart for {breed1} vs {breed2}")
        
        return fig
    
    except Exception as e:
        # Create empty figure with error message
        fig = Figure(figsize=(10, 6))
        ax = fig.add_subplot(111)
        ax.text(0.5, 0.5, f"Error generating comparison: {str(e)}",
               horizontalalignment='center', verticalalignment='center', 
               transform=ax.transAxes, fontsize=14)
        ax.axis('off')
        print(f"Error in generate_comparison_chart: {str(e)}")
        return fig