"""Modern Anvil App Scaffolder.

Generates complete modern Anvil applications with Material 3 + Reactive +
Official Routing + Layouts + Model Classes architecture.
"""

from pathlib import Path
from typing import Dict, List, Tuple, Any
from dataclasses import dataclass

from ..validators.component_validator import ComponentValidator
from ..dependencies.container import FileSystem


@dataclass
class ScaffoldConfig:
    """Configuration for app scaffolding."""

    app_name: str
    app_description: str
    output_path: str
    features: Dict[str, bool]
    theme_config: Dict[str, Any]
    routing_config: Dict[str, Any]
    data_models: List[Dict[str, Any]]


class ModernAppScaffolder:
    """Generate complete modern Anvil apps with optimal architecture."""

    def __init__(self, file_system: FileSystem) -> None:
        self.file_system: FileSystem = file_system
        self.validator: ComponentValidator = ComponentValidator()

    def create_modern_app(self, config: ScaffoldConfig) -> Tuple[bool, str]:
        """Create a complete modern Anvil application."""
        try:
            # Create project structure
            self._create_project_structure(config)

            # Generate theme configuration
            self._generate_theme_config(config)

            # Generate routing setup
            self._generate_routing_setup(config)

            # Generate data models
            self._generate_data_models(config)

            # Generate layouts
            self._generate_layouts(config)

            # Generate pages
            self._generate_pages(config)

            # Generate stores
            self._generate_stores(config)

            # Generate services
            self._generate_services(config)

            # Generate configuration files
            self._generate_config_files(config)

            return (
                True,
                f"Modern app '{config.app_name}' created successfully at {config.output_path}",
            )

        except Exception as e:
            return False, f"Error creating modern app: {str(e)}"

    def _create_project_structure(self, config: ScaffoldConfig) -> None:
        """Create the modern project directory structure."""
        base_path = Path(config.output_path) / config.app_name

        # Create main directories
        directories = [
            "client_code",
            "client_code/layouts",
            "client_code/pages",
            "client_code/pages/dashboard",
            "client_code/pages/settings",
            "client_code/pages/profile",
            "client_code/stores",
            "client_code/components",
            "client_code/components/shared",
            "server_code",
            "server_code/models",
            "server_code/services",
            "server_code/routes",
            "theme",
            "theme/m3_customisations",
            "tests",
            "docs",
        ]

        for directory in directories:
            self.file_system.mkdir(
                str(base_path / directory), parents=True, exist_ok=True
            )

    def _generate_theme_config(self, config: ScaffoldConfig) -> None:
        """Generate Material 3 theme configuration."""
        base_path = Path(config.output_path) / config.app_name

        theme_yaml = {
            "container": {
                "type": "ColumnPanel",
                "properties": {
                    "background": config.theme_config.get("background", "#FAFAFA"),
                    "role": "outlined-card",
                },
            },
            "components": [],
        }

        # Generate theme file
        theme_content = self._build_yaml_from_dict(theme_yaml)
        self.file_system.write_text(
            str(base_path / "theme" / "m3_theme.yaml"), theme_content
        )

        # Generate theme customisation Python
        theme_py = f'''"""Material 3 Theme Customisations for {config.app_name}."""

import anvil

# Material 3 color scheme
primary_color = "{config.theme_config.get("primary", "#6750A4")}"
secondary_color = "{config.theme_config.get("secondary", "#625B71")}"
tertiary_color = "{config.theme_config.get("tertiary", "#7D5260")}"

# Apply Material 3 theme
def apply_m3_theme():
    """Apply Material 3 theme to the application."""
    anvil.theme.set_primary_color(primary_color)
    anvil.theme.set_secondary_color(secondary_color)
    
    # Material 3 component theming
    anvil.theme.set_font("Roboto, Arial, sans-serif")
    anvil.theme.set_border_radius("8px")
    
    # Custom CSS for Material 3 components
    m3_css = """
    .anvil-m3-card {{
        box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
        border-radius: 8px;
        background: white;
    }}
    
    .anvil-m3-button {{
        border-radius: 20px;
        text-transform: none;
        font-weight: 500;
    }}
    
    .anvil-m3-outlined-button {{
        border: 1px solid rgba(0,0,0,0.12);
        background: transparent;
    }}
    """
    
    anvil.css.append(m3_css)
'''

        self.file_system.write_text(
            str(base_path / "theme" / "m3_customisations" / "__init__.py"), theme_py
        )

    def _generate_routing_setup(self, config: ScaffoldConfig) -> None:
        """Generate official routing configuration."""
        base_path = Path(config.output_path) / config.app_name

        # Main routing module
        routing_py = f'''"""Official Routing Configuration for {config.app_name}."""

import anvil
from anvil import routing

# Import route classes
from client_code.pages.dashboard.dashboard_route import DashboardRoute
from client_code.pages.settings.settings_route import SettingsRoute  
from client_code.pages.profile.profile_route import ProfileRoute

# Configure routing
@routing.route("/")
@routing.route("/dashboard")
class DashboardRoute(DashboardRoute):
    """Main dashboard route."""
    pass

@routing.route("/settings")
class SettingsRoute(SettingsRoute):
    """Settings page route."""
    pass

@routing.route("/profile")
class ProfileRoute(ProfileRoute):
    """Profile page route."""
    pass

# Route configuration
ROUTE_CONFIG = {{
    "default_route": "/dashboard",
    "not_found_route": "/dashboard",
    "routes": [
        {{
            "path": "/dashboard",
            "component": "DashboardRoute",
            "title": "Dashboard",
            "requires_auth": True
        }},
        {{
            "path": "/settings", 
            "component": "SettingsRoute",
            "title": "Settings",
            "requires_auth": True
        }},
        {{
            "path": "/profile",
            "component": "ProfileRoute", 
            "title": "Profile",
            "requires_auth": True
        }}
    ]
}}

def initialize_routing():
    """Initialize the routing system."""
    routing.set_error_page(DashboardRoute)
    routing.set_loading_page(DashboardRoute)
'''

        self.file_system.write_text(
            str(base_path / "client_code" / "__init__.py"), routing_py
        )

    def _generate_data_models(self, config: ScaffoldConfig) -> None:
        """Generate model classes for type-safe data access."""
        base_path = Path(config.output_path) / config.app_name

        # Base model class
        models_init = '''"""Model Classes for type-safe data access."""

import anvil.server
from dataclasses import field
from datetime import datetime
from typing import Optional, List, Dict, Any

@anvil.server.model_class
class User:
    """User model with authentication and profile data."""
    
    email = field(str, unique=True)
    name = field(str)
    created_at = field(datetime, default=lambda: datetime.now())
    is_active = field(bool, default=True)
    last_login = field(datetime, optional=True)
    profile = field(Optional["UserProfile"], default=None)
    
    def get_display_name(self) -> str:
        """Get user's display name."""
        return self.name or self.email.split('@')[0]
    
    def is_authenticated(self) -> bool:
        """Check if user is authenticated."""
        return self.is_active and self.email
    
    @classmethod
    def find_active_users(cls) -> List["User"]:
        """Find all active users."""
        return cls.search(is_active=True).order_by("created_at", ascending=False)
    
    @classmethod
    def get_by_email(cls, email: str) -> Optional["User"]:
        """Get user by email address."""
        return cls.get(email=email)

@anvil.server.model_class  
class UserProfile:
    """Extended user profile information."""
    
    user = field(User)
    bio = field(str, optional=True)
    avatar_url = field(str, optional=True)
    preferences = field(dict, default=lambda: {{}})
    timezone = field(str, default="UTC")
    
    def get_preference(self, key: str, default: Any = None) -> Any:
        """Get a user preference."""
        return self.preferences.get(key, default)
    
    def set_preference(self, key: str, value: Any) -> None:
        """Set a user preference."""
        self.preferences[key] = value
        self.save()

@anvil.server.model_class
class AppSettings:
    """Application-wide settings."""
    
    key = field(str, unique=True)
    value = field(str)
    description = field(str, optional=True)
    updated_at = field(datetime, default=lambda: datetime.now())
    updated_by = field(User, optional=True)
    
    @classmethod
    def get_setting(cls, key: str, default: str = "") -> str:
        """Get application setting."""
        setting = cls.get(key=key)
        return setting.value if setting else default
    
    @classmethod
    def set_setting(cls, key: str, value: str, description: str = None, user: User = None) -> None:
        """Set application setting."""
        setting = cls.get(key=key) or cls(key=key)
        setting.value = value
        if description:
            setting.description = description
        setting.updated_by = user
        setting.save()
'''

        self.file_system.write_text(
            str(base_path / "server_code" / "models" / "__init__.py"), models_init
        )

    def _generate_layouts(self, config: ScaffoldConfig) -> None:
        """Generate layout components."""
        base_path = Path(config.output_path) / config.app_name

        # Main layout with NavigationDrawer
        main_layout_yaml = {
            "container": {
                "type": "NavigationDrawerLayout",
                "properties": {"drawer_width": "280px"},
                "components": [
                    {
                        "name": "nav_drawer",
                        "type": "NavigationDrawer",
                        "properties": {"role": "navigation-drawer"},
                        "components": [
                            {
                                "name": "drawer_header",
                                "type": "LinearPanel",
                                "properties": {
                                    "align": "center",
                                    "spacing": "medium",
                                    "padding": "20px",
                                },
                                "components": [
                                    {
                                        "name": "app_logo",
                                        "type": "Image",
                                        "properties": {
                                            "height": "48px",
                                            "width": "48px",
                                        },
                                    },
                                    {
                                        "name": "app_title",
                                        "type": "Label",
                                        "properties": {
                                            "text": config.app_name,
                                            "font_size": "20px",
                                            "font_weight": "bold",
                                            "role": "headline-small",
                                        },
                                    },
                                ],
                            },
                            {
                                "name": "nav_links",
                                "type": "LinkPanel",
                                "properties": {"role": "navigation-list"},
                            },
                        ],
                    },
                    {
                        "name": "main_content",
                        "type": "ColumnPanel",
                        "properties": {"role": "main-content"},
                    },
                ],
            }
        }

        layout_content = self._build_yaml_from_dict(main_layout_yaml)
        self.file_system.write_text(
            str(base_path / "client_code" / "layouts" / "MainLayout.yaml"),
            layout_content,
        )

    def _generate_pages(self, config: ScaffoldConfig) -> None:
        """Generate page components."""
        base_path = Path(config.output_path) / config.app_name

        # Dashboard page
        dashboard_yaml = {
            "container": {
                "type": "ColumnPanel",
                "properties": {"spacing": "medium", "padding": "20px"},
                "components": [
                    {
                        "name": "welcome_card",
                        "type": "Card",
                        "properties": {
                            "role": "outlined-card",
                            "title": f"Welcome to {config.app_name}",
                            "content": "This is your modern Anvil dashboard with Material 3 design.",
                        },
                    },
                    {
                        "name": "stats_grid",
                        "type": "GridPanel",
                        "properties": {"columns": "3", "spacing": "medium"},
                    },
                ],
            }
        }

        dashboard_content = self._build_yaml_from_dict(dashboard_yaml)
        self.file_system.write_text(
            str(
                base_path / "client_code" / "pages" / "dashboard" / "DashboardForm.yaml"
            ),
            dashboard_content,
        )

        # Dashboard route class
        dashboard_route = f'''"""Dashboard Route with data loading and reactive integration."""

import anvil
from anvil import routing
from anvil.reactive import signal, computed, reactive_class, bind
from ..layouts.MainLayout import MainLayout
from ...stores.app_store import app_store
from ...models import User

@reactive_class
@routing.route("/dashboard")
@routing.route("/")
class DashboardRoute(DashboardRoute):
    """Main dashboard route with data loading."""
    
    def __init__(self, routing_context=None, **properties):
        self.routing_context = routing_context or {{}}
        self.user = signal(None)
        self.stats = signal({{}})
        self.loading = signal(True)
        self.init_components(**properties)
        self._load_data()
    
    def _load_data(self):
        """Load dashboard data."""
        try:
            # Load user data
            user_id = self.routing_context.data.get('user_id')
            if user_id:
                self.user.set(User.get_by_id(user_id))
            
            # Load dashboard stats
            self._load_stats()
            
        except Exception as e:
            print(f"Error loading dashboard data: {{e}}")
        finally:
            self.loading.set(False)
    
    def _load_stats(self):
        """Load dashboard statistics."""
        # Example stats - replace with real data
        self.stats.set({{
            'total_users': User.search().count(),
            'active_sessions': 0,
            'recent_activity': []
        }})
    
    @computed
    def welcome_message(self):
        """Computed welcome message."""
        if self.user.get():
            return f"Welcome back, {{self.user.get().get_display_name()}}!"
        return f"Welcome to {config.app_name}!"
    
    def form_show(self, **event_args):
        """Handle form show event."""
        self._refresh_data()
    
    def _refresh_data(self):
        """Refresh dashboard data."""
        self.loading.set(True)
        self._load_data()
'''

        self.file_system.write_text(
            str(
                base_path / "client_code" / "pages" / "dashboard" / "dashboard_route.py"
            ),
            dashboard_route,
        )

    def _generate_stores(self, config: ScaffoldConfig) -> None:
        """Generate reactive stores for state management."""
        base_path = Path(config.output_path) / config.app_name

        # App store
        app_store_py = f'''"""Application State Management Store."""

import anvil
from anvil.reactive import signal, computed, reactive_class
from ..models import User, AppSettings

@reactive_class
class AppStore:
    """Global application state management."""
    
    def __init__(self):
        # Authentication state
        self.current_user = signal(None)
        self.is_authenticated = signal(False)
        self.auth_loading = signal(True)
        
        # App state
        self.theme = signal("light")
        self.notifications = signal([])
        app_store = self  # Make available globally
        
        # UI state
        self.sidebar_open = signal(True)
        self.page_loading = signal(False)
        
        # Load initial state
        self._load_initial_state()
    
    def _load_initial_state(self):
        """Load initial application state."""
        try:
            # Load theme preference
            theme_setting = AppSettings.get_setting("theme", "light")
            self.theme.set(theme_setting)
            
            # Check authentication
            self._check_auth_status()
            
        except Exception as e:
            print(f"Error loading initial state: {{e}}")
        finally:
            self.auth_loading.set(False)
    
    def _check_auth_status(self):
        """Check if user is authenticated."""
        # Implement authentication check
        user = anvil.users.get_user()
        if user:
            self.current_user.set(user)
            self.is_authenticated.set(True)
    
    @computed
    def user_display_name(self):
        """Computed user display name."""
        user = self.current_user.get()
        return user.get_display_name() if user else "Guest"
    
    def login(self, email: str, password: str) -> bool:
        """Authenticate user."""
        try:
            self.auth_loading.set(True)
            # Implement login logic
            user = anvil.users.login_with_email(email, password)
            if user:
                self.current_user.set(user)
                self.is_authenticated.set(True)
                self.notifications.append({{"type": "success", "message": "Login successful"}})
                return True
            return False
        except Exception as e:
            self.notifications.append({{"type": "error", "message": f"Login failed: {{e}}"}})
            return False
        finally:
            self.auth_loading.set(False)
    
    def logout(self):
        """Logout current user."""
        try:
            anvil.users.logout()
            self.current_user.set(None)
            self.is_authenticated.set(False)
            self.notifications.append({{"type": "info", "message": "Logged out successfully"}})
        except Exception as e:
            self.notifications.append({{"type": "error", "message": f"Logout failed: {{e}}"}})
    
    def set_theme(self, theme: str):
        """Set application theme."""
        self.theme.set(theme)
        AppSettings.set_setting("theme", theme)
    
    def add_notification(self, notification: dict):
        """Add a notification."""
        notifications = self.notifications.get().copy()
        notifications.append(notification)
        self.notifications.set(notifications)
    
    def clear_notifications(self):
        """Clear all notifications."""
        self.notifications.set([])

# Global app store instance
app_store = AppStore()
'''

        self.file_system.write_text(
            str(base_path / "client_code" / "stores" / "app_store.py"), app_store_py
        )

    def _generate_services(self, config: ScaffoldConfig) -> None:
        """Generate server-side services."""
        base_path = Path(config.output_path) / config.app_name

        # User service
        user_service_py = f'''"""User Service for business logic operations."""

import anvil.server
from ..models import User, UserProfile

@anvil.server.callable
def get_user_profile(user_id: str) -> dict:
    """Get user profile with extended information."""
    try:
        user = User.get_by_id(user_id)
        if not user:
            raise ValueError("User not found")
        
        profile = user.profile or UserProfile(user=user)
        
        return {{
            "id": str(user.get_id()),
            "email": user.email,
            "name": user.name,
            "display_name": user.get_display_name(),
            "is_active": user.is_active,
            "created_at": user.created_at,
            "bio": profile.bio,
            "avatar_url": profile.avatar_url,
            "preferences": profile.preferences,
            "timezone": profile.timezone
        }}
    except Exception as e:
        raise anvil.server.NoServerError(f"Error getting user profile: {{e}}")

@anvil.server.callable
def update_user_profile(user_id: str, updates: dict) -> dict:
    """Update user profile information."""
    try:
        user = User.get_by_id(user_id)
        if not user:
            raise ValueError("User not found")
        
        # Update user fields
        if "name" in updates:
            user.name = updates["name"]
        
        # Update or create profile
        profile = user.profile or UserProfile(user=user)
        
        if "bio" in updates:
            profile.bio = updates["bio"]
        if "avatar_url" in updates:
            profile.avatar_url = updates["avatar_url"]
        if "timezone" in updates:
            profile.timezone = updates["timezone"]
        if "preferences" in updates:
            profile.preferences.update(updates["preferences"])
        
        # Save changes
        user.save()
        profile.save()
        
        return get_user_profile(user_id)
        
    except Exception as e:
        raise anvil.server.NoServerError(f"Error updating user profile: {{e}}")

@anvil.server.callable
def get_user_preferences(user_id: str) -> dict:
    """Get user preferences."""
    try:
        user = User.get_by_id(user_id)
        if not user:
            raise ValueError("User not found")
        
        profile = user.profile or UserProfile(user=user)
        return profile.preferences
        
    except Exception as e:
        raise anvil.server.NoServerError(f"Error getting user preferences: {{e}}")

@anvil.server.callable
def update_user_preferences(user_id: str, preferences: dict) -> dict:
    """Update user preferences."""
    try:
        user = User.get_by_id(user_id)
        if not user:
            raise ValueError("User not found")
        
        profile = user.profile or UserProfile(user=user)
        profile.preferences.update(preferences)
        profile.save()
        
        return profile.preferences
        
    except Exception as e:
        raise anvil.server.NoServerError(f"Error updating user preferences: {{e}}")
'''

        self.file_system.write_text(
            str(base_path / "server_code" / "services" / "user_service.py"),
            user_service_py,
        )

    def _generate_config_files(self, config: ScaffoldConfig) -> None:
        """Generate configuration files."""
        base_path = Path(config.output_path) / config.app_name

        # Package.json equivalent for Anvil dependencies
        dependencies_json = {
            "name": config.app_name,
            "description": config.app_description,
            "version": "1.0.0",
            "dependencies": {
                "@anvil/material-3": "latest",
                "@anvil/official-routing": "latest",
                "@anvil/reactive": "latest",
                "@anvil/layouts": "latest",
            },
            "devDependencies": {"@anvil/testing": "latest"},
        }

        self.file_system.write_text(
            str(base_path / "anvil_dependencies.json"),
            str(dependencies_json).replace("'", '"'),
        )

        # README
        readme_md = f"""# {config.app_name}

{config.app_description}

## Modern Architecture

This application uses the modern Anvil stack:

- **Material 3**: Modern Material Design components and theming
- **Official Routing**: Client-side routing with data loading
- **Reactive**: Signals-based state management for automatic UI updates
- **Model Classes**: Type-safe, validated data models
- **Layouts**: Slot-based layout architecture

## Project Structure

```
{config.app_name}/
├── client_code/
│   ├── layouts/          # Layout components
│   ├── pages/            # Page components with routing
│   ├── stores/           # Reactive state management
│   └── components/       # Shared components
├── server_code/
│   ├── models/           # Data models
│   ├── services/         # Business logic
│   └── routes/           # API endpoints
└── theme/               # Material 3 theming
```

## Development

1. Open the project in Anvil
2. The app will automatically use the modern architecture
3. All components are Material 3 styled
4. State management uses reactive signals
5. Routing is handled by the official routing system

## Features

- ✅ Material 3 Design System
- ✅ Reactive State Management  
- ✅ Official Routing
- ✅ Type-safe Model Classes
- ✅ Layout-based Architecture
- ✅ Responsive Design
- ✅ Accessibility Support
"""

        self.file_system.write_text(str(base_path / "README.md"), readme_md)

    def _build_yaml_from_dict(self, data) -> str:
        """Build YAML string from dictionary with proper formatting."""
        import yaml

        return yaml.dump(data, default_flow_style=False, indent=2, sort_keys=False)
