#!/usr/bin/env python3
"""
Azure Billing Manager - Interactive CLI
Self-installing script for Azure billing account management

This script will automatically install its dependencies and provide
an interactive interface for managing Azure billing accounts.
"""

# from calendar import month
# from re import sub
# from tracemalloc import start
# from pathlib import Path

import subprocess
import sys
import os
from datetime import datetime, timedelta
from time import sleep


def install_package(package, upgrade=False):
    """Install a package using pip with error handling"""
    try:
        cmd = [sys.executable, "-m", "pip", "install"]

        if upgrade:
            cmd.append("--upgrade")
        cmd.append(package)
        
        print(f"  📦 Installing {package}...")
        result = subprocess.run(cmd, capture_output=True, text=True, check=True)
        return True, result.stdout
    except subprocess.CalledProcessError as e:
        return False, e.stderr

def check_and_install_dependencies():
    """Check dependencies and install missing ones"""
    
    # Required packages with their import names
    dependencies = {
        "azure-identity": "azure.identity",
        "requests": "requests",
        "rich": "rich",
        "InquirerPy": "InquirerPy",
        "cryptography": "cryptography"
    }
    
    print("🔍 Checking Azure dependencies...")
    
    missing_packages = []
    
    for package, import_name in dependencies.items():
        try:
            install_package(package, True)
            # importlib.import_module(import_name)
            print(f"✅ {package} - OK")
        except ImportError:
            print(f"❌ {package} - Missing")
            missing_packages.append(package)
    
    if missing_packages:
        print(f"\n📦 Installing {len(missing_packages)} missing packages...")
        print("This may take a moment for Azure SDKs...")
        
        for package in missing_packages:
            success, output = install_package(package, True)
            
            if success:
                print(f"✅ {package} installed successfully")
            else:
                print(f"❌ Failed to install {package}")
                print(f"Error: {output}")
                
                response = input("Continue anyway? (y/N): ").lower()
                if response != 'y':
                    print("Exiting due to dependency installation failure")
                    sys.exit(1)
    
    print("🎉 All dependencies ready!\n")

# Install dependencies before importing
check_and_install_dependencies()
# Enable dynamic extension installation for Azure CLI
subprocess.run(["az", "config", "set", "extension.use_dynamic_install=yes_without_prompt"], capture_output=True)

# Now import the dependencies
from azure.identity import DefaultAzureCredential, InteractiveBrowserCredential, AzureCliCredential
import requests
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, TextColumn
from rich import print as rprint
from InquirerPy import inquirer
from InquirerPy.base.control import Choice
from InquirerPy.separator import Separator
import json
import subprocess
import uuid
import secrets
import string
from datetime import datetime, timedelta
from typing import List, Dict, Optional

class AzureBillingManager:
    def __init__(self, debug=False):
        self.console = Console()
        self.credential = None
        self.subscription_id = None
        self.debug = debug
        self.successful_exports = []  # Track successful export creations
        self.failed_commands = []  # Track failed commands for better error reporting
        
    def show_welcome(self):
        """Display welcome screen"""
        welcome_text = """
[bold blue]☁️  Azure Configuration Manager[/bold blue]

[dim]Complete Azure billing and cost management setup automation[/dim]

[yellow]Configuration Process:[/yellow]
1. 📋 Account Type Selection (EA/MACC/CSP)
2. 🎯 Billing Account Selection (if not CSP)
3. 🔐 Service Principal Creation with Certificate
4. �️ Role Assignment (Billing, Storage, Cost Management)
5. �📁 Resource Group Configuration
6. 💾 Storage Account Creation
7. 📊 Billing Export Configuration & Execution
8. ✅ Validation & Testing

[yellow]What Gets Created:[/yellow]
• Service Principal with certificate authentication
• Required role assignments for billing access
• Storage account with secure container for exports
• Automated daily billing exports (AmortizedCost)
• Complete configuration for cost management

[red]⚠️ Important Notes:[/red]
Before getting started, please ensure:
• You have the necessary permissions to create service principals, role assignments, and storage accounts in your Azure environment.
• For CSP accounts, make sure you have Owner permissions on ALL subscriptions you wish to onboard now.
        """
        
        rprint(Panel.fit(welcome_text, border_style="blue", title="Welcome"))

    def setup_authentication(self) -> bool:
        """Setup Azure authentication"""
        self.console.print("\n🔐 Setting up Azure authentication...")
        
        # Prompt for default authentication first
        use_default = inquirer.confirm(
            message="Use default authentication? Choose 'No' for more options.",
            default=True,
        ).execute()
        
        if use_default:
            self.credential = DefaultAzureCredential()
            try:
                # Test the credential by getting a token
                with self.console.status("[bold blue]Testing default authentication..."):
                    token = self.credential.get_token("https://management.azure.com/.default")
                    
                self.console.print("[bold green]🎉 Default authentication successful![/bold green]")
                return True
            except Exception as e:
                self.console.print(f"[red]❌ Default authentication failed: {str(e)}[/red]")
                self.console.print("[yellow]💡 Falling back to manual authentication selection...[/yellow]")
        
        auth_method = inquirer.select(
            message="Choose authentication method:",
            choices=[
                Choice("cloudshell", "☁️ Using cloud shell managed identity"),
                Choice("cli", "🖥️  Azure CLI (recommended - use 'az login' first)"),
                Choice("browser", "🌐 Interactive Browser Login"),
                Choice("env", "🔑 Environment Variables (Service Principal)"),
                Choice("default", "🔄 Default Credential Chain")
            ],
            default="cloudshell"
        ).execute()
        
        try:
            if auth_method == "cloudshell":
                self.credential = DefaultAzureCredential()
            
            elif auth_method == "cli":
                self.credential = AzureCliCredential()
                self.console.print("[green]✅ Using Azure CLI authentication[/green]")
                
            elif auth_method == "browser":
                self.credential = InteractiveBrowserCredential()
                self.console.print("[yellow]🌐 Opening browser for authentication...[/yellow]")
                
            elif auth_method == "env":
                # Check for required environment variables
                required_vars = ["AZURE_CLIENT_ID", "AZURE_CLIENT_SECRET", "AZURE_TENANT_ID"]
                missing_vars = [var for var in required_vars if not os.getenv(var)]
                
                if missing_vars:
                    self.console.print(f"[red]❌ Missing environment variables: {', '.join(missing_vars)}[/red]")
                    self.console.print("Please set these environment variables and try again.")
                    return False
                
                self.credential = DefaultAzureCredential()
                self.console.print("[green]✅ Using Service Principal authentication[/green]")
                
            else:  # default
                self.credential = DefaultAzureCredential()
                self.console.print("[blue]🔄 Using default credential chain[/blue]")
            
            # Test the credential by getting a token
            with self.console.status("[bold blue]Testing authentication..."):
                token = self.credential.get_token("https://management.azure.com/.default")
                
            self.console.print("[bold green]🎉 Authentication successful![/bold green]")
            return True
            
        except Exception as e:
            self.console.print(f"[red]❌ Authentication failed: {str(e)}[/red]")
            
            if "az login" in str(e) or "CLI" in str(e):
                self.console.print("\n[yellow]💡 Tip: Run 'az login' in your terminal first[/yellow]")
            
            return False

    def fetch_billing_accounts(self, api_version: str = "2020-05-01") -> List[Dict]:
        """
        Fetch billing accounts from Azure Management API.
        Returns a list of billing account objects (the 'value' array from the response).
        Uses DefaultAzureCredential so the machine should already be authenticated.
        """
        try:
            token = self.credential.get_token("https://management.azure.com/.default")
            headers = {
                "Authorization": f"Bearer {token.token}",
                "Content-Type": "application/json",
                "Accept": "application/json",
            }

            url = f"https://management.azure.com/providers/Microsoft.Billing/billingAccounts?api-version={api_version}"
            
            if self.debug:
                self.console.print(f"[dim]Debug: Making request to {url}[/dim]")
            
            resp = requests.get(url, headers=headers, timeout=30)
            resp.raise_for_status()
            body = resp.json()
            
            if self.debug:
                self.console.print(f"[dim]Debug: Response status: {resp.status_code}[/dim]")
                self.console.print(f"[dim]Debug: Found {len(body.get('value', []))} accounts[/dim]")
            
            return body.get("value", [])
            
        except Exception as e:
            self.console.print(f"[red]❌ Error fetching billing accounts via REST API: {str(e)}[/red]")
            return []

    def initialize_clients(self):
        """Initialize Azure REST API access"""
        try:
            with self.console.status("[bold blue]Testing Azure REST API access..."):
                # Test that we can get a token
                token = self.credential.get_token("https://management.azure.com/.default")
                
                # Initialize headers for API calls
                self.headers = {
                    "Authorization": f"Bearer {token.token}",
                    "Content-Type": "application/json",
                    "Accept": "application/json",
                }
                
                if self.debug:
                    self.console.print(f"[dim]Debug: Got token, expires at {datetime.fromtimestamp(token.expires_on)}[/dim]")
                
            self.console.print("[green]✅ Azure REST API access ready[/green]")
            
        except Exception as e:
            self.console.print(f"[red]❌ Failed to initialize REST API access: {str(e)}[/red]")
            raise

    def get_headers(self, resource: str = "https://management.azure.com/.default"):
        """Get fresh authorization headers for API calls"""
        try:
            token = self.credential.get_token(resource)
            return {
                "Authorization": f"Bearer {token.token}",
                "Content-Type": "application/json",
                "Accept": "application/json",
            }
        except Exception as e:
            self.console.print(f"[red]❌ Failed to get authorization token: {str(e)}[/red]")
            return None

    def get_billing_subscription_info(self) -> Dict:
        """Get billing subscription information using Azure CLI"""
        try:
            self.console.print("[blue]🔍 Getting billing subscription information via Azure CLI...[/blue]")
            
            ## az billing subscription show
            cmd = ["az", "billing", "property", "show", "--output", "json"]
            
            with self.console.status("[bold blue]Fetching billing subscription info..."):
                result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
            
            if result.returncode == 0:
                try:
                    billing_info = json.loads(result.stdout)
                    
                    # Extract relevant information
                    parsed_info = {
                        'raw_data': billing_info,
                        'billing_account_id': billing_info.get('billingAccountId'),
                        'billing_account_name': billing_info.get('billingAccountName'),
                        'subscription_id': billing_info.get('subscriptionId'),
                        'subscription_display_name': billing_info.get('subscriptionDisplayName'),
                        'billing_profile_id': billing_info.get('billingProfileId'),
                        'billing_profile_name': billing_info.get('billingProfileDisplayName'),
                        'invoice_section_id': billing_info.get('invoiceSectionId'),
                        'invoice_section_name': billing_info.get('invoiceSectionDisplayName')
                    }
                    
                    self.console.print("[green]✅ Successfully retrieved billing subscription info[/green]")
                    
                    if parsed_info['billing_account_id']:
                        self.console.print(f"[cyan]📋 Found billing account: {parsed_info['billing_account_name']} ({parsed_info['billing_account_id']})[/cyan]")
                    
                    return parsed_info
                    
                except json.JSONDecodeError as e:
                    self.console.print(f"[red]❌ Failed to parse billing subscription info: {e}[/red]")
                    return {}
            else:
                error_msg = result.stderr.strip() if result.stderr else "Unknown error"
                self.console.print(f"[yellow]⚠️ Could not get billing subscription info: {error_msg}[/yellow]")
                self.console.print("[dim]This might be normal for some account types or if no default subscription is set[/dim]")
                return {}
                
        except subprocess.TimeoutExpired:
            self.console.print("[red]❌ Command timed out[/red]")
            return {}
        except Exception as e:
            self.console.print(f"[red]❌ Error getting billing subscription info: {str(e)}[/red]")
            return {}

    def list_billing_accounts(self) -> List[Dict]:
        """List all accessible billing accounts using REST API"""
        try:
            with self.console.status("[bold blue]Fetching billing accounts via REST API..."):
                # Use the direct REST API approach
                raw_accounts = self.fetch_billing_accounts()
                
                if not raw_accounts:
                    self.console.print("[yellow]⚠️ No billing accounts found[/yellow]")
                    return []
                
                billing_accounts = []
                
                for account in raw_accounts:
                    # Parse the REST API response
                    account_info = {
                        'id': account.get('id', 'N/A'),
                        'name': account.get('name', 'N/A'),
                        'display_name': account.get('displayName', account.get('name', 'N/A')),
                        'type': account.get('properties', {}).get('accountType', 'N/A'),
                        'status': account.get('properties', {}).get('accountStatus', 'N/A'),
                        'agreement_type': account.get('properties', {}).get('agreementType', 'N/A'),
                        'country': 'N/A'  # Country info may be in soldTo properties if available
                    }
                    
                    # Try to get country info from soldTo if available
                    properties = account.get('properties', {})
                    sold_to = properties.get('soldTo', {})
                    if sold_to and 'country' in sold_to:
                        account_info['country'] = sold_to['country']
                    
                    billing_accounts.append(account_info)
                    
                    if self.debug:
                        self.console.print(f"[dim]Debug: Processed account {account_info['name']}[/dim]")
            
            return billing_accounts
            
        except Exception as e:
            self.console.print(f"[red]❌ Error fetching billing accounts: {str(e)}[/red]")
            self.console.print("[yellow]💡 This might be due to insufficient permissions or account type limitations[/yellow]")
            return []

    def test_billing_access(self) -> bool:
        """Test if we can access billing APIs via REST"""
        try:
            self.console.print("[blue]🔍 Testing billing REST API access...[/blue]")
            
            # Try to fetch billing accounts using REST API
            raw_accounts = self.fetch_billing_accounts()
            
            if raw_accounts:
                self.console.print(f"[green]✅ Billing REST API access successful! Found {len(raw_accounts)} accounts[/green]")
                
                if self.debug and raw_accounts:
                    first_account = raw_accounts[0]
                    self.console.print(f"[dim]Debug: First account keys: {list(first_account.keys())}[/dim]")
                    self.console.print(f"[dim]Debug: First account ID: {first_account.get('id', 'N/A')}[/dim]")
                
                return True
            else:
                self.console.print("[yellow]⚠️ No billing accounts found (this might be normal for your account type)[/yellow]")
                return True
                    
        except Exception as e:
            self.console.print(f"[red]❌ Cannot access billing REST API: {str(e)}[/red]")
            return False

    def display_billing_accounts_table(self, accounts: List[Dict]):
        """Display billing accounts in a formatted table"""
        if not accounts:
            self.console.print("[yellow]⚠️ No billing accounts found[/yellow]")
            return
        
        table = Table(show_header=True, header_style="bold magenta")
        table.add_column("Index", width=6, justify="center")
        table.add_column("Display Name", style="cyan")
        table.add_column("Account ID", style="dim")
        table.add_column("Type", width=15)
        table.add_column("Status", width=10)
        table.add_column("Agreement", width=15)
        
        for idx, account in enumerate(accounts, 1):
            status_color = "green" if account.get('status') == 'Active' else "yellow"
            
            table.add_row(
                str(idx),
                account.get('display_name', 'N/A'),
                account.get('name', 'N/A'),
                account.get('type', 'N/A'),
                f"[{status_color}]{account.get('status', 'N/A')}[/{status_color}]",
                account.get('agreement_type', 'N/A')
            )
        
        self.console.print("\n📋 Available Billing Accounts:")
        self.console.print(table)

    def select_billing_account(self, accounts: List[Dict]) -> Optional[Dict]:
        """Interactive billing account selection"""
        if not accounts:
            self.console.print("[red]❌ No billing accounts available for selection[/red]")
            return None
        
        self.console.print(f"\n[bold blue]Found {len(accounts)} billing account(s)[/bold blue]")
        
        # Create choices for inquirer with more detailed info
        choices = []
        for idx, account in enumerate(accounts, 1):
            # Create a more informative label
            name = account.get('display_name', account.get('name', 'Unknown'))
            account_type = account.get('type', 'Unknown')
            status = account.get('status', 'Unknown')
            
            # Create status indicator
            status_emoji = "✅" if status.lower() == "active" else "⚠️" if status.lower() == "suspended" else "❓"
            
            label = f"{status_emoji} {name} | {account_type} | {status}"
            choices.append(Choice(value=account, name=label))
        
        # Add a cancel option
        choices.append(Separator())
        choices.append(Choice(value=None, name="🔙 Back to Main Menu"))
        
        selected_account = inquirer.select(
            message="Choose a billing account to manage:",
            choices=choices,
            instruction="(Use ↑↓ arrow keys, Enter to select, Ctrl+C to cancel)"
        ).execute()
        
        return selected_account

    def select_account_type(self) -> str:
        """Let user select their Azure account type"""
        self.console.print("\n[bold blue]📋 Azure Account Type Selection[/bold blue]")
        self.console.print("[dim]Please select your Azure account type:[/dim]\n")
        
        account_type = inquirer.select(
            message="What type of Azure account are you using?",
            choices=[
                Choice("EA", "🏢 Enterprise Agreement (EA)"),
                Choice("MACC", "🌐 Microsoft Azure Consumption Commitment (MACC)"),
                Choice("CSP", "🤝 Cloud Solution Provider (CSP) or specific subscriptions only"),
            ],
            instruction="(Use ↑↓ arrow keys, Enter to select)"
        ).execute()
        
        return account_type

    def quick_account_chooser(self, account_type: str = None) -> Optional[Dict]:
        """Quick account selection without showing the table first"""
        try:
            # Display account type context if provided
            if account_type:
                self.console.print(f"[blue]🎯 Looking for {account_type} billing accounts...[/blue]")
            
            # First, try to get billing subscription info to see if we have a default billing account
            billing_sub_info = self.get_billing_subscription_info()
            
            if billing_sub_info.get('billing_account_id'):
                # We found a billing account from the subscription info, prompt user to choose
                use_default = inquirer.confirm(
                    message=f"Use billing account {billing_sub_info['billing_account_id']} from your subscription?",
                    default=True,
                ).execute()
                
                if use_default:
                    # Create a mock account dict from the billing subscription info
                    selected_account = {
                        'id': billing_sub_info['billing_account_id'],
                        'name': billing_sub_info['billing_account_id'].split('/')[-1] if '/' in billing_sub_info['billing_account_id'] else billing_sub_info['billing_account_id'],
                        'display_name': billing_sub_info['billing_account_name'] or billing_sub_info['billing_account_id'],
                        'type': 'Unknown',  # Will be determined later
                        'status': 'Active',  # Assume active if it's the default
                        'agreement_type': 'Unknown',
                        'country': 'N/A',
                        'subscription_info': billing_sub_info  # Store the full info for later use
                    }
                    
                    self.console.print(f"[blue]→ Selected: {selected_account.get('display_name', 'N/A')}[/blue]")
                    return selected_account
            
            # If no default billing account or user chose not to use it, proceed with full list
            with self.console.status("[bold blue]Loading billing accounts..."):
                accounts = self.list_billing_accounts()
            
            if not accounts:
                self.console.print(f"[yellow]⚠️ No billing accounts found in your Azure environment[/yellow]")
                if account_type and account_type != "Unknown":
                    self.console.print(f"[dim]Note: This might be expected for {account_type} accounts depending on your permissions[/dim]")
                return None
            
            # Filter or annotate accounts based on account type
            filtered_accounts = self.filter_accounts_by_type(accounts, account_type)
            
            self.console.print(f"\n[green]✅ Found {len(filtered_accounts)} billing account(s)[/green]")
            if account_type and account_type != "Unknown":
                self.console.print(f"[dim]Filtered for {account_type} account type[/dim]")
            
            if len(filtered_accounts) == 1:
                # Auto-select if only one account
                account = filtered_accounts[0]
                self.console.print(f"[blue]→ Auto-selected: {account.get('display_name', 'N/A')}[/blue]")
                return account
            
            # Multiple accounts - let user choose
            return self.select_billing_account(filtered_accounts)
            
        except Exception as e:
            self.console.print(f"[red]❌ Error loading accounts: {str(e)}[/red]")
            return None

    def filter_accounts_by_type(self, accounts: List[Dict], account_type: str) -> List[Dict]:
        """Filter or annotate accounts based on the selected account type"""
        if not account_type or account_type == "Unknown":
            return accounts
        
        filtered_accounts = []
        
        for account in accounts:
            # Add account type context to display name for better identification
            agreement_type = account.get('agreement_type', '').upper()
            account_status = account.get('status', '').upper()
            
            # Try to match account type with agreement type or other indicators
            include_account = True
            type_indicator = ""
            
            if account_type == "EA":
                # EA accounts typically have "EnterpriseAgreement" in agreement_type
                if "ENTERPRISE" in agreement_type:
                    type_indicator = " [EA]"
                elif agreement_type and "ENTERPRISE" not in agreement_type:
                    # Might not be EA, but include with indicator
                    type_indicator = f" [{agreement_type}]"
            
            elif account_type == "CSP":
                # CSP accounts might have different agreement types
                if "CSP" in agreement_type or "PARTNER" in agreement_type:
                    type_indicator = " [CSP]"
                else:
                    type_indicator = f" [?]"
            
            elif account_type == "MACC":
                # MACC accounts might have specific agreement indicators
                if "MACC" in agreement_type or "COMMITMENT" in agreement_type:
                    type_indicator = " [MACC]"
                else:
                    type_indicator = f" [?]"
            
            elif account_type == "PayGo":
                # PayGo might be indicated by specific agreement types
                if not agreement_type or "PAYG" in agreement_type or "PAY" in agreement_type:
                    type_indicator = " [PayGo]"
                else:
                    type_indicator = f" [?]"
            
            if include_account:
                # Add type indicator to display name for clarity
                account_copy = account.copy()
                original_name = account_copy.get('display_name', account_copy.get('name', 'N/A'))
                account_copy['display_name'] = f"{original_name}{type_indicator}"
                filtered_accounts.append(account_copy)
        
        return filtered_accounts

    def show_account_details(self, account: Dict):
        """Display detailed information about selected billing account"""
        details_text = f"""
[bold cyan]Billing Account Details[/bold cyan]

[yellow]Basic Information:[/yellow]
• Display Name: {account.get('display_name', 'N/A')}
• Account ID: {account.get('name', 'N/A')}
• Full ID: {account.get('id', 'N/A')}

[yellow]Account Properties:[/yellow]
• Type: {account.get('type', 'N/A')}
• Status: {account.get('status', 'N/A')}
• Agreement Type: {account.get('agreement_type', 'N/A')}
• Country: {account.get('country', 'N/A')}
        """
        
        rprint(Panel.fit(details_text, border_style="cyan", title="Selected Account"))

    def collect_resource_info(self) -> tuple[str, str]:
        """Collect resource group and storage account information from user"""
        self.console.print("\n[bold yellow]📋 Additional Resource Information Required[/bold yellow]")
        self.console.print("[dim]Please provide the following details for your Azure resources:[/dim]\n")
        
        # Get resource group name
        resource_group = inquirer.text(
            message="Enter Resource Group name:",
            instruction="(Required - the resource group containing your storage account)"
        ).execute()
        
        if not resource_group or not resource_group.strip():
            self.console.print("[red]❌ Resource Group name is required[/red]")
            return None, None
        
        # Get storage account name
        storage_account = inquirer.text(
            message="Enter Storage Account name:",
            instruction="(Required - the name of your Azure Storage Account)"
        ).execute()
        
        if not storage_account or not storage_account.strip():
            self.console.print("[red]❌ Storage Account name is required[/red]")
            return None, None
        
        return resource_group.strip(), storage_account.strip()

    def list_existing_storage_accounts(self) -> Dict:
        """List existing storage accounts in the subscription and allow user to choose one"""
        try:
            self.console.print("\n[bold blue]📋 Select Existing Azure Storage Account[/bold blue]")
            self.console.print("[dim]Choose from available storage accounts in your subscription.[/dim]\n")
            
            headers = self.get_headers("https://management.azure.com/.default")
            if not headers:
                self.console.print("[red]❌ Failed to get authentication headers[/red]")
                return {}
            
            # Get all storage accounts in the subscription
            storage_url = f"https://management.azure.com/subscriptions/{self.subscription_id}/providers/Microsoft.Storage/storageAccounts"
            storage_params = {"api-version": "2023-01-01"}
            
            self.console.print("[blue]🔍 Fetching existing storage accounts...[/blue]")
            
            with self.console.status("[bold blue]Loading storage accounts..."):
                response = requests.get(storage_url, headers=headers, params=storage_params)
            
            if response.status_code != 200:
                self.console.print(f"[red]❌ Failed to fetch storage accounts: HTTP {response.status_code}[/red]")
                if response.text:
                    try:
                        error_data = response.json()
                        error_msg = error_data.get('error', {}).get('message', response.text[:200])
                        self.console.print(f"[red]Error: {error_msg}[/red]")
                    except:
                        self.console.print(f"[red]Error: {response.text[:200]}[/red]")
                return {}
            
            storage_accounts_data = response.json()
            storage_accounts = storage_accounts_data.get('value', [])
            
            if not storage_accounts:
                self.console.print("[yellow]⚠️ No existing storage accounts found in this subscription[/yellow]")
                self.console.print("[blue]💡 You may want to create a new storage account instead[/blue]")
                return {}
            
            self.console.print(f"[green]✅ Found {len(storage_accounts)} storage account(s)[/green]")
            
            # Format storage accounts for selection
            formatted_accounts = []
            for account in storage_accounts:
                # Extract resource group from resource ID
                resource_id = account.get('id', '')
                resource_group = ''
                if '/resourceGroups/' in resource_id:
                    resource_group = resource_id.split('/resourceGroups/')[1].split('/')[0]
                
                formatted_account = {
                    'name': account.get('name', 'Unknown'),
                    'resource_group': resource_group,
                    'location': account.get('location', 'Unknown'),
                    'sku': account.get('sku', {}).get('name', 'Unknown'),
                    'kind': account.get('kind', 'Unknown'),
                    'access_tier': account.get('properties', {}).get('accessTier', 'Unknown'),
                    'resource_id': resource_id,
                    'subscription_id': self.subscription_id
                }
                formatted_accounts.append(formatted_account)
            
            # Create choices for user selection
            choices = []
            choices.append(Choice(value="__new__", name="🆕 Create New Storage Account Instead"))
            choices.append(Choice(value="__cancel__", name="🔙 Cancel"))
            for account in formatted_accounts:
                # Create descriptive label
                label = f"💾 {account['name']} | {account['resource_group']} | {account['location']} | {account['sku']}"
                choices.append(Choice(value=account, name=label))
            
            # Add option to create new instead
            # choices.append(Separator())
            choices.append(Choice(value="__new__", name="🆕 Create New Storage Account Instead"))
            choices.append(Choice(value="__cancel__", name="🔙 Cancel"))
            
            selected = inquirer.fuzzy(
                message="Choose an existing storage account:",
                choices=choices,
                instruction="(Use ↑↓ arrow keys, Enter to select)"
            ).execute()
            
            if selected == "__cancel__":
                self.console.print("[yellow]⚠️ Storage account selection cancelled[/yellow]")
                return {}
            elif selected == "__new__":
                self.console.print("[blue]→ Redirecting to create new storage account...[/blue]")
                return self.create_new_storage_account()
            
            # User selected an existing storage account
            selected_account = selected
            self.console.print(f"[blue]→ Selected: {selected_account['name']} in {selected_account['resource_group']}[/blue]")
            
            # Show account details
            account_details = f"""
[bold cyan]Selected Storage Account Details[/bold cyan]

• Name: [cyan]{selected_account['name']}[/cyan]
• Resource Group: [cyan]{selected_account['resource_group']}[/cyan]
• Location: [cyan]{selected_account['location']}[/cyan]
• SKU: [cyan]{selected_account['sku']}[/cyan]
• Kind: [cyan]{selected_account['kind']}[/cyan]
• Access Tier: [cyan]{selected_account['access_tier']}[/cyan]
            """
            
            rprint(Panel.fit(account_details, border_style="cyan", title="Selected Account"))
            
            # Confirm selection
            confirm = inquirer.confirm(
                message="Use this storage account for billing exports?",
                default=True
            ).execute()
            
            if not confirm:
                self.console.print("[yellow]⚠️ Storage account selection cancelled[/yellow]")
                return {}
                
            self.create_storage_container(subscription_id=selected_account['subscription_id'],
                                          resource_group_name=selected_account['resource_group'],
                                          storage_account_name=selected_account['name'],
                                          container_name="cloudhiro")
            
            # Return the same format as create_new_storage_account
            return {
                'subscription_id': selected_account['subscription_id'],
                'resource_group': selected_account['resource_group'],
                'storage_account': selected_account['name'],
                'location': selected_account['location'],
                'resource_id': selected_account['resource_id']
            }
            
        except Exception as e:
            self.console.print(f"[red]❌ Error listing storage accounts: {str(e)}[/red]")
            return {}

    def create_new_storage_account(self) -> Dict:
        """Create a new Azure Storage Account with user input"""
        try:
            self.console.print("\n[bold blue]💾 Create New Azure Storage Account(Recommended)[/bold blue]")
            self.console.print("[dim]This will create a new storage account for billing exports and cost management.[/dim]\n")
            
            # Get available subscriptions
            headers = self.get_headers("https://management.azure.com/.default")
            if not headers:
                return {}
            # Get storage account name from user with availability check
            while True:
                storage_account_name = inquirer.text(
                    message="Enter Storage Account name:",
                    instruction="(3-24 characters, lowercase letters and numbers only)",
                    validate=lambda x: len(x) >= 3 and len(x) <= 24 and x.islower() and x.isalnum()
                ).execute()
                
                if not storage_account_name:
                    self.console.print("[red]❌ Storage Account name is required[/red]")
                    return {}
                
                # Check if storage account name is available
                if self.check_storage_account_availability(storage_account_name):
                    break
                else:
                    self.console.print(f"[red]❌ Storage account name '{storage_account_name}' is not available. Please choose another name.[/red]")
                    continue
            
            # Let user choose resource group approach
            rg_choice = inquirer.select(
                message="How would you like to handle the Resource Group?",
                choices=[
                    Choice("new", "🆕 Create a new Resource Group(Recommended)"),
                    Choice("existing", "📁 Use an existing Resource Group"),
                ],
                instruction="(Resource Group will contain the storage account)"
            ).execute()
            
            if rg_choice == "new":
                # Create new resource group
                resource_group_name = inquirer.text(
                    message="Enter new Resource Group name:",
                    instruction="(Must be unique within the subscription)",
                    default=f"rg-{storage_account_name}"
                ).execute()
                
                if not resource_group_name:
                    self.console.print("[red]❌ Resource Group name is required[/red]")
                    return {}
                
                self.console.print(f"[cyan]🆕 New resource group '{resource_group_name}' will be created[/cyan]")
                
            else:  # existing
                # List existing resource groups for the user to choose from
                existing_rgs = self.list_resource_groups(self.subscription_id)
                
                if not existing_rgs:
                    self.console.print("[yellow]⚠️ No existing resource groups found. Creating a new one instead.[/yellow]")
                    resource_group_name = inquirer.text(
                        message="Enter Resource Group name:",
                        instruction="(Will be created as no existing groups found)",
                        default=f"rg-{storage_account_name}"
                    ).execute()
                    
                    if not resource_group_name:
                        self.console.print("[red]❌ Resource Group name is required[/red]")
                        return {}
                else:
                    # Let user choose from existing resource groups
                    self.console.print(f"\n[yellow]📁 Found {len(existing_rgs)} existing resource group(s):[/yellow]")
                    choices = []
                    choices.append(Choice(value="__new__", name="🆕 Create new Resource Group instead"))
                    for rg in existing_rgs:
                        name = rg.get('name', 'Unknown')
                        location = rg.get('location', 'Unknown')
                        choices.append(Choice(value=name, name=f"📁 {name} ({location})"))
                    
                    # Add option to create new instead
                    # choices.append(Separator())
                    choices.append(Choice(value="__new__", name="🆕 Create new Resource Group instead"))

                    selected_rg = inquirer.fuzzy(
                        message="Select existing Resource Group:",
                        choices=choices,
                        instruction="(Type to search, ↑↓ arrow keys, Enter to select)" # <--- Updated instruction
                    ).execute()
                    
                    # selected_rg = inquirer.select(
                    #     message="Select existing Resource Group:",
                    #     choices=choices,
                    #     instruction="(Use ↑↓ arrow keys, Enter to select)"
                    # ).execute()
                    
                    
                    if selected_rg == "__new__":
                        resource_group_name = inquirer.text(
                            message="Enter new Resource Group name:",
                            instruction="(Will be created)",
                            default=f"rg-{storage_account_name}"
                        ).execute()
                        
                        if not resource_group_name:
                            self.console.print("[red]❌ Resource Group name is required[/red]")
                            return {}
                        
                        self.console.print(f"[cyan]🆕 New resource group '{resource_group_name}' will be created[/cyan]")
                    else:
                        resource_group_name = selected_rg
                        self.console.print(f"[cyan]📁 Using existing resource group '{resource_group_name}'[/cyan]")
            
            # Get location for the storage account
            location = inquirer.select(
                message="Select Azure region:",
                choices=[
                    Choice("eastus", "🌎 East US"),
                    Choice("westus2", "🌎 West US 2"),
                    Choice("centralus", "🌎 Central US"),
                    Choice("eastus2", "🌎 East US 2"),
                    Choice("westus3", "🌎 West US 3"),
                    Choice("northeurope", "🌍 North Europe"),
                    Choice("westeurope", "🌍 West Europe"),
                    Choice("uksouth", "🇬🇧 UK South"),
                    Choice("eastasia", "🌏 East Asia"),
                    Choice("southeastasia", "🌏 Southeast Asia"),
                    Choice("japaneast", "🇯🇵 Japan East"),
                    Choice("australiaeast", "🇦🇺 Australia East"),
                    Choice("canadacentral", "🇨🇦 Canada Central"),
                    Choice("brazilsouth", "🇧🇷 Brazil South"),
                ],
                default="eastus"
            ).execute()
            
            config_summary = f"""
[bold yellow]📋 Storage Account Configuration Summary[/bold yellow]

• Subscription ID: [cyan]{self.subscription_id}[/cyan]
• Resource Group: [cyan]{resource_group_name}[/cyan]
• Storage Account: [cyan]{storage_account_name}[/cyan]
• Location: [cyan]{location}[/cyan]
            """
            
            rprint(Panel.fit(config_summary, border_style="yellow", title="Configuration"))
            
            # Confirm creation
            confirm = inquirer.confirm(
                message="Do you want to create this storage account?",
                default=True
            ).execute()
            
            if not confirm:
                self.console.print("[yellow]⚠️ Storage account creation cancelled[/yellow]")
                return {}
            
            # Create the storage account
            result = self.create_storage_account_api(
                subscription_id=self.subscription_id,
                resource_group_name=resource_group_name,
                storage_account_name=storage_account_name,
                location=location,
            )
            
            if result.get('success'):
                self.console.print(f"\n[green]✅ Storage account '{storage_account_name}' created successfully![/green]")
                return {
                    'subscription_id': self.subscription_id,
                    'resource_group': resource_group_name,
                    'storage_account': storage_account_name,
                    'location': location,
                    'resource_id': result.get('resource_id')
                }
            else:
                self.console.print(f"[red]❌ Failed to create storage account: {result.get('error', 'Unknown error')}[/red]")
                return {}
            
        except Exception as e:
            self.console.print(f"[red]❌ Error creating storage account: {str(e)}[/red]")
            return {}

    def get_management_groups(self, subscription_id: str) -> List[Dict]:
        """
        Deprecate?
        Get management groups for the subscription"""
        try:
            headers = self.get_headers("https://management.azure.com/.default")
            if not headers:
                return []
            
            # Get management groups
            mgmt_url = "https://management.azure.com/providers/Microsoft.Management/managementGroups"
            mgmt_params = {"api-version": "2020-05-01"}
            
            response = requests.get(mgmt_url, headers=headers, params=mgmt_params)
            
            if response.status_code == 200:
                mgmt_data = response.json()
                return mgmt_data.get('value', [])
            else:
                self.console.print(f"[yellow]⚠️ Could not fetch management groups (this is normal for some account types)[/yellow]")
                return []
                
        except Exception as e:
            self.console.print(f"[yellow]⚠️ Error fetching management groups: {e}[/yellow]")
            return []

    def list_resource_groups(self, subscription_id: str) -> List[Dict]:
        """List existing resource groups in the subscription"""
        try:
            headers = self.get_headers("https://management.azure.com/.default")
            if not headers:
                return []
            
            # Get resource groups for the subscription
            rg_url = f"https://management.azure.com/subscriptions/{subscription_id}/resourcegroups"
            rg_params = {"api-version": "2021-04-01"}
            
            response = requests.get(rg_url, headers=headers, params=rg_params)
            
            if response.status_code == 200:
                rg_data = response.json()
                resource_groups = rg_data.get('value', [])
                
                # Extract relevant information
                formatted_rgs = []
                for rg in resource_groups:
                    formatted_rgs.append({
                        'name': rg.get('name', 'Unknown'),
                        'location': rg.get('location', 'Unknown'),
                        'id': rg.get('id', ''),
                        'tags': rg.get('tags', {})
                    })
                
                return formatted_rgs
            else:
                self.console.print(f"[yellow]⚠️ Could not fetch resource groups: HTTP {response.status_code}[/yellow]")
                return []
                
        except Exception as e:
            self.console.print(f"[yellow]⚠️ Error fetching resource groups: {e}[/yellow]")
            return []

    def check_storage_account_availability(self, storage_account_name: str) -> bool:
        """Check if storage account name is available"""
        try:
            headers = self.get_headers("https://management.azure.com/.default")
            if not headers:
                return False
            check_url = f"https://management.azure.com/subscriptions/{self.subscription_id}/providers/Microsoft.Storage/checkNameAvailability"
            
            check_payload = {
                "name": storage_account_name,
                "type": "Microsoft.Storage/storageAccounts"
            }
            
            check_params = {"api-version": "2023-01-01"}
            
            response = requests.post(check_url, headers=headers, params=check_params, json=check_payload)
            
            if response.status_code == 200:
                result = response.json()
                return result.get("nameAvailable", False)
            else:
                # If check fails, assume name is available to avoid blocking user
                self.console.print(f"[yellow]⚠️ Could not check name availability (HTTP {response.status_code})[/yellow]")
                return True
                
        except Exception as e:
            # If check fails, assume name is available to avoid blocking user
            self.console.print(f"[yellow]⚠️ Could not check name availability: {e}[/yellow]")
            return True

    def create_storage_account_api(self, subscription_id: str, resource_group_name: str, 
                                 storage_account_name: str, location: str) -> Dict:
        """Create storage account using Azure REST API"""
        try:
            headers = self.get_headers("https://management.azure.com/.default")
            if not headers:
                return {'success': False, 'error': 'Failed to get authentication headers'}
            
            # First, create or ensure resource group exists
            rg_result = self.create_resource_group(subscription_id, resource_group_name, location)
            if not rg_result.get('success'):
                return {'success': False, 'error': f"Failed to create resource group: {rg_result.get('error')}"}
            
            # Create storage account
            storage_url = f"https://management.azure.com/subscriptions/{subscription_id}/resourceGroups/{resource_group_name}/providers/Microsoft.Storage/storageAccounts/{storage_account_name}"
            
            storage_definition = {
                "sku": {
                    "name": "Standard_LRS"
                },
                "kind": "StorageV2",
                "location": location,
                "properties": {
                    "accessTier": "Hot",
                    "supportsHttpsTrafficOnly": True,
                    "allowBlobPublicAccess": False,
                    "minimumTlsVersion": "TLS1_2"
                },
                "tags": {
                    "purpose": "billing-exports",
                    "created-by": "azure-billing-manager"
                }
            }
            
            storage_params = {"api-version": "2023-01-01"}
            
            self.console.print(f"[blue]🔄 Creating storage account '{storage_account_name}'...[/blue]")
            
            response = requests.put(
                storage_url,
                headers=headers,
                params=storage_params,
                json=storage_definition
            )
            
            if response.status_code in [200, 201, 202]:
                # Storage account creation initiated successfully
                # response_data = response.json()
                # resource_id = response_data.get('id', storage_url)
                
                # If it's async (202), we might want to poll for completion
                if response.status_code == 202:
                    self.console.print("[yellow]⏳ Storage account creation in progress...[/yellow]")
                    # You could add polling logic here if needed
                
                # Create a default container for billing exports
                container_result = self.create_storage_container(
                    subscription_id, resource_group_name, storage_account_name, "cloudhiro"
                )
                
                return {
                    'success': True,
                    'resource_id': storage_url,
                    'storage_account_name': storage_account_name,
                    'resource_group': resource_group_name,
                    'container_created': container_result.get('success', False)
                }
            else:
                error_msg = f"HTTP {response.status_code}"
                if response.text:
                    try:
                        error_data = response.json()
                        error_msg = error_data.get('error', {}).get('message', error_msg)
                    except:
                        error_msg = response.text[:200]
                
                return {'success': False, 'error': error_msg}
                
        except Exception as e:
            return {'success': False, 'error': str(e)}

    def create_resource_group(self, subscription_id: str, resource_group_name: str, location: str) -> Dict:
        """Create or ensure resource group exists"""
        try:
            headers = self.get_headers("https://management.azure.com/.default")
            if not headers:
                return {'success': False, 'error': 'Failed to get authentication headers'}
            
            rg_url = f"https://management.azure.com/subscriptions/{subscription_id}/resourcegroups/{resource_group_name}"
            
            rg_definition = {
                "location": location,
                "tags": {
                    "purpose": "billing-exports",
                    "created-by": "azure-billing-manager"
                }
            }
            
            rg_params = {"api-version": "2021-04-01"}
            
            self.console.print(f"[blue]🔄 Creating/checking resource group '{resource_group_name}'...[/blue]")
            
            response = requests.put(
                rg_url,
                headers=headers,
                params=rg_params,
                json=rg_definition
            )
            
            if response.status_code in [200, 201]:
                return {'success': True}
            else:
                error_msg = f"HTTP {response.status_code}"
                if response.text:
                    try:
                        error_data = response.json()
                        error_msg = error_data.get('error', {}).get('message', error_msg)
                    except:
                        error_msg = response.text[:200]
                
                return {'success': False, 'error': error_msg}
                
        except Exception as e:
            return {'success': False, 'error': str(e)}

    def create_storage_container(self, subscription_id: str, resource_group_name: str, 
                                storage_account_name: str, container_name: str) -> Dict:
        """Create a container in the storage account for billing exports"""
        try:
            headers = self.get_headers("https://management.azure.com/.default")
            if not headers:
                return {'success': False, 'error': 'Failed to get authentication headers'}
            
            container_url = f"https://management.azure.com/subscriptions/{subscription_id}/resourceGroups/{resource_group_name}/providers/Microsoft.Storage/storageAccounts/{storage_account_name}/blobServices/default/containers/{container_name}"
            
            container_definition = {
                "properties": {
                    "publicAccess": "None"
                }
            }
            
            container_params = {"api-version": "2023-01-01"}
            
            self.console.print(f"[blue]🔄 Creating container '{container_name}' for billing exports...[/blue]")
            
            response = requests.put(
                container_url,
                headers=headers,
                params=container_params,
                json=container_definition
            )
            
            if response.status_code in [200, 201]:
                return {'success': True}
            else:
                error_msg = f"HTTP {response.status_code}"
                if response.text:
                    try:
                        error_data = response.json()
                        error_msg = error_data.get('error', {}).get('message', error_msg)
                    except:
                        error_msg = response.text[:200]
                
                self.console.print(f"[yellow]⚠️ Could not create container: {error_msg}[/yellow]")
                return {'success': False, 'error': error_msg}
                
        except Exception as e:
            self.console.print(f"[yellow]⚠️ Error creating container: {e}[/yellow]")
            return {'success': False, 'error': str(e)}

    def billing_account_menu(self, account: Dict) -> str:
        """Show billing account management menu"""
        return inquirer.select(
            message="What would you like to do with this billing account?",
            choices=[
                Choice("profiles", "📄 View Billing Profiles"),
                Choice("invoices", "🧾 View Invoices"),
                Choice("subscriptions", "📋 View Subscriptions"),
                Choice("cost_analysis", "💰 Cost Analysis"),
                Choice("usage_details", "📊 Usage Details"),
                Choice("permissions", "🔐 Manage Permissions"),
                Separator(),
                Choice("back", "🔙 Back to Account Selection"),
                Choice("exit", "🚪 Exit")
            ]
        ).execute()

    def main_menu(self) -> str:
        """Show main application menu"""
        return inquirer.select(
            message="What would you like to do?",
            choices=[
                Choice("start_config", "🚀 Start Configuration Process"),
                Separator(),
                Choice("list_sp", "📋 List Service Principals (Costi*)"),
                Choice("test_access", "🔍 Test Billing API Access"),
                Choice("auth_info", "🔐 Authentication Info"),
                Separator(),
                Choice("exit", "🚪 Exit")
            ]
        ).execute()

    def show_auth_info(self):
        """Display current authentication information"""
        auth_info = "🔐 Authentication Information\n\n"
        
        if hasattr(self.credential, '__class__'):
            auth_info += f"Method: {self.credential.__class__.__name__}\n"
        
        # Try to get current user info
        try:
            with self.console.status("Getting user information..."):
                # This is a simple way to test the credential
                token = self.credential.get_token("https://management.azure.com/.default")
                auth_info += "Status: ✅ Active\n"
                auth_info += f"Token expires: {datetime.fromtimestamp(token.expires_on)}\n"
        except Exception as e:
            auth_info += f"Status: ❌ Error - {str(e)}\n"
        
        rprint(Panel.fit(auth_info, border_style="blue", title="Authentication"))

    def create_service_principal_with_cert(self, principal_name: str = None) -> Dict:
        """Create Azure AD Service Principal with certificate using Azure CLI"""
        try:
            self.console.print("\n[bold blue]🔐 Creating Azure AD Service Principal with Certificate (via Azure CLI)[/bold blue]")
            
            # Get principal name if not provided
            if not principal_name:
                principal_name = inquirer.text(
                    message="Enter Service Principal name:",
                    instruction="(e.g., 'MyApp-ServicePrincipal')",
                    default="Costi"
                ).execute()
                
                if not principal_name or not principal_name.strip():
                    self.console.print("[red]❌ Service Principal name is required[/red]")
                    return {}
            
            # Check if Azure CLI is available
            try:
                subprocess.run(["az", "--version"], check=True, capture_output=True, text=True)
                self.console.print("[green]✅ Azure CLI detected[/green]")
            except (subprocess.CalledProcessError, FileNotFoundError):
                self.console.print("[red]❌ Azure CLI not found. Please install Azure CLI first.[/red]")
                self.console.print("[yellow]💡 Install: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli[/yellow]")
                return {}
            
            # Check if user is logged in to Azure CLI
            try:
                result = subprocess.run(["az", "account", "show"], check=True, capture_output=True, text=True)
                account_info = json.loads(result.stdout)
                self.console.print(f"[green]✅ Logged in as: {account_info.get('user', {}).get('name', 'Unknown')}[/green]")
            except subprocess.CalledProcessError:
                self.console.print("[red]❌ Not logged in to Azure CLI. Please run 'az login' first.[/red]")
                return {}
            
            # Execute Azure CLI command to create service principal with certificate
            self.console.print(f"[blue]🔄 Creating service principal '{principal_name}' with certificate...[/blue]")
            
            cmd = [
                "az", "ad", "sp", "create-for-rbac",
                "--name", principal_name,
                "--create-cert",
                "--years", "3",
                "--output", "json"
            ]
            
            # Show the command being executed
            self.console.print(f"[dim]Executing: {' '.join(cmd)}[/dim]")
            
            # Execute the command
            with self.console.status("[bold blue]Creating service principal..."):
                result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
            
            # Print the raw output from Azure CLI
            if result.stdout:
                self.console.print(f"\n[bold blue]Azure CLI Output:[/bold blue]")
                self.console.print(f"[dim]{result.stdout}[/dim]")
            
            if result.stderr:
                self.console.print(f"\n[yellow]Azure CLI Messages:[/yellow]")
                self.console.print(f"[dim]{result.stderr}[/dim]")
            
            if result.returncode != 0:
                self.console.print(f"[red]❌ Azure CLI command failed with exit code {result.returncode}[/red]")
                return {}
            
            # Parse the JSON response
            try:
                sp_data = json.loads(result.stdout)
            except json.JSONDecodeError as e:
                self.console.print(f"[red]❌ Failed to parse Azure CLI response: {e}[/red]")
                self.console.print(f"[dim]Raw output: {result.stdout}[/dim]")
                return {}
            
            self.console.print(f"[green]✅ Service Principal created successfully![/green]")
            
            # Extract information from Azure CLI response
            app_id = sp_data.get("appId")
            tenant_id = sp_data.get("tenant")
            cert_file = sp_data.get("fileWithCertAndPrivateKey")

            # Prompt the user to download the PEM certificate to their local machine
            if cert_file:
                download_cert = inquirer.confirm(
                    message="Do you want to download the PEM certificate to your local machine(for cloud shell only)?",
                    default=True
                ).execute()

                if download_cert:
                    try:
                        cmd = "download " + cert_file
                        subprocess.run(cmd, shell=True, check=True)
                        self.console.print(f"[yellow]✅ PEM certificate downloaded successfully: {cert_file}.\n If it did not dowload automatically you should do it manually[/yellow]")
                    except Exception as e:
                        self.console.print(f"[red]❌ Failed to download PEM certificate: {e}[/red]")
            # Get additional info using Azure CLI
            try:
                # Get service principal object ID
                sp_show_cmd = ["az", "ad", "sp", "show", "--id", app_id, "--query", "id", "--output", "tsv"]
                sp_result = subprocess.run(sp_show_cmd, capture_output=True, text=True, check=True)
                sp_object_id = sp_result.stdout.strip()
            except subprocess.CalledProcessError:
                sp_object_id = "unknown"
            
            result = {
                "displayName": sp_data.get("displayName", principal_name),
                "appId": app_id,
                "objectId": sp_object_id,
                "tenantId": tenant_id,
                "certificatePath": cert_file,
                "authMethod": "certificate",
                "createdVia": "azure_cli"
            }
            
            # Don't assign roles here - this will be handled in the configuration process
            self.console.print("[blue]ℹ️  Service principal created successfully. Role assignment will be handled in the configuration process.[/blue]")
            
            return result
            
        except subprocess.TimeoutExpired:
            self.console.print("[red]❌ Command timed out. Please try again.[/red]")
            return {}
        except Exception as e:
            self.console.print(f"[red]❌ Error creating service principal: {str(e)}[/red]")
            return {}

    def grant_log_analytics_api_permission(self, app_id: str) -> bool:
        """Grant Log Analytics API permissions to service principal using Azure CLI"""
        try:
            self.console.print("[blue]🔐 Log Analytics API Permission Request[/blue]")
            
            # Log Analytics API constants
            log_analytics_api_id = "ca7f3f0b-7d91-482c-8e09-c5d840d0eac5"  # Log Analytics API ID
            data_read_permission_id = "b40776e3-0e77-4dca-874c-40a36d0d9f6c"  # Data.Read permission ID
            
            # Show permission details and ask for confirmation
            permission_info = f"""
[bold yellow]📋 Log Analytics API Permission Details[/bold yellow]

[yellow]This permission allows the service principal to:[/yellow]
• Read data from Log Analytics workspaces
• Query Log Analytics tables and views
• Access monitoring and diagnostic data
            """
            
            rprint(Panel.fit(permission_info, border_style="yellow", title="Permission Grant Request"))
            
            # Ask for user confirmation
            confirm = inquirer.confirm(
                message="Do you want to grant Log Analytics API permissions to this service principal?",
                default=True,
            ).execute()
            
            if not confirm:
                self.console.print("[yellow]⚠️ Log Analytics API permission grant cancelled by user[/yellow]")
                return False
            
            self.console.print("[blue]🔄 Proceeding with Log Analytics API permission grant...[/blue]")
            
            
            # Grant API permission using Azure CLI
            cmd = [
                "az", "ad", "app", "permission", "add",
                "--id", app_id,
                "--api", log_analytics_api_id,
                "--api-permissions", f"{data_read_permission_id}=Scope"
            ]
            
            self.console.print(f"[dim]Executing: {' '.join(cmd)}[/dim]")
            
            self.console.print(f"[blue]🔄 Adding Log Analytics Data.Read permission...[/blue]")
            
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
            
            if result.returncode == 0:
                self.console.print("[green]✅ Log Analytics API permission added successfully[/green]")
                
                # Grant admin consent for the permission
                consent_cmd = [
                    "az", "ad", "app", "permission", "admin-consent",
                    "--id", app_id
                ]
                
                self.console.print("[blue]🔄 Granting admin consent for API permissions...[/blue]")
                
                if self.debug:
                    self.console.print(f"[dim]Debug: Executing: {' '.join(consent_cmd)}[/dim]")
                
                consent_result = subprocess.run(consent_cmd, capture_output=True, text=True, timeout=30)
                
                if consent_result.returncode == 0:
                    self.console.print("[green]✅ Admin consent granted successfully[/green]")
                    return True
                else:
                    error_msg = consent_result.stderr.strip() if consent_result.stderr else "Unknown error"
                    self.console.print(f"[yellow]⚠️ Could not grant admin consent: {error_msg}[/yellow]")
                    self.console.print("[dim]You may need to grant consent manually in the Azure Portal[/dim]")
                    return True  # Permission was added, consent can be done manually
            else:
                error_msg = result.stderr.strip() if result.stderr else "Unknown error"
                self.console.print(f"[red]❌ Failed to add Log Analytics API permission: {error_msg}[/red]")
                
                # Provide helpful error suggestions
                if "Insufficient privileges" in error_msg:
                    self.console.print("[yellow]💡 Tip: You need 'Application Administrator' or 'Global Administrator' role[/yellow]")
                elif "already exists" in error_msg.lower():
                    self.console.print("[yellow]ℹ️ Permission already exists, attempting to grant consent...[/yellow]")
                    # Try to grant consent anyway
                    consent_cmd = [
                        "az", "ad", "app", "permission", "admin-consent",
                        "--id", app_id
                    ]
                    consent_result = subprocess.run(consent_cmd, capture_output=True, text=True, timeout=30)
                    if consent_result.returncode == 0:
                        self.console.print("[green]✅ Admin consent granted successfully[/green]")
                        return True
                
                return False
                
        except subprocess.TimeoutExpired:
            self.console.print("[red]❌ Command timed out[/red]")
            return False
        except Exception as e:
            self.console.print(f"[red]❌ Error granting Log Analytics API permission: {str(e)}[/red]")
            return False

    def assign_roles_via_cli(self, app_id: str, subscription_id: str, tenant_id: str = None, resource_group: str = None, storage_account: str = None) -> bool:
        """Assign roles to service principal using Azure CLI with proper scope"""
        # Build role definitions with actual resource group and storage account names
        required_roles = []
        
        # Storage-specific roles (only if resource group and storage account are provided)
        if resource_group and storage_account:
            storage_scope = f"/subscriptions/{subscription_id}/resourceGroups/{resource_group}/providers/Microsoft.Storage/storageAccounts/{storage_account}"
            required_roles.extend([
                ("Storage Blob Data Reader", storage_scope),
                ("Reader and Data Access", storage_scope),
            ])
        
        # Management group and tenant-level roles
        if tenant_id:
            mgmt_scope = f"/providers/Microsoft.Management/managementGroups/{tenant_id}"
            required_roles.extend([
                ("Reader", mgmt_scope),
                ("Monitoring Reader", mgmt_scope),
            ])
        
        # Capacity-level roles (for reservations)
        required_roles.append(("Reservations Reader", "/providers/Microsoft.Capacity"))
        
        # Display role summary based on what will actually be assigned
        self.display_role_assignment_summary(required_roles, resource_group, storage_account, tenant_id)
        
        rprint("\n[yellow]📋 Assigning roles to service principal via Azure CLI...[/yellow]")
        
        # Display resource information
        if resource_group and storage_account:
            rprint(f"[cyan]📁 Resource Group: {resource_group}[/cyan]")
            rprint(f"[cyan]💾 Storage Account: {storage_account}[/cyan]")
        
        # Construct the subscription scope
        subscription_scope = f"/subscriptions/{subscription_id}"
        
        if tenant_id:
            rprint(f"[cyan]🏢 Tenant ID: {tenant_id}[/cyan]")
        rprint(f"[cyan]📊 Subscription Scope: {subscription_scope}[/cyan]")
        
        success_count = 0
        for role_name, role_scope in required_roles:
            try:
                cmd = [
                    "az", "role", "assignment", "create",
                    "--assignee", app_id,
                    "--role", role_name,
                    "--scope", role_scope,
                    "--output", "json"
                ]
                
                # Show the command being executed for transparency
                cmd_str = " ".join(cmd[:-2])  # Exclude --output json for display
                rprint(f"  [dim]Executing: {cmd_str}[/dim]")
                
                result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
                
                if result.returncode == 0:
                    rprint(f"  [green]✅ Assigned role: {role_name}[/green]")
                    rprint(f"    [dim]Scope: {role_scope}[/dim]")
                    success_count += 1
                    
                    # Parse the output to get assignment details
                    try:
                        assignment_data = json.loads(result.stdout)
                        assignment_id = assignment_data.get("id", "").split("/")[-1]
                        if assignment_id:
                            rprint(f"    [dim]Assignment ID: {assignment_id}[/dim]")
                    except (json.JSONDecodeError, KeyError):
                        pass  # Don't fail if we can't parse the response
                        
                else:
                    rprint(f"  [yellow]⚠️  Failed to assign role {role_name}[/yellow]")
                    rprint(f"    [yellow]Attempted scope: {role_scope}[/yellow]")
                    if result.stderr:
                        error_msg = result.stderr.strip()
                        rprint(f"    [yellow]Error: {error_msg}[/yellow]")
                        
                        # Provide helpful suggestions based on common errors
                        if "AuthorizationFailed" in error_msg:
                            rprint(f"    [yellow]💡 Tip: You may need 'User Access Administrator' or 'Owner' role on the scope[/yellow]")
                        elif "PrincipalNotFound" in error_msg:
                            rprint(f"    [yellow]💡 Tip: Service principal might still be propagating, try again in a few seconds[/yellow]")
                        elif "InvalidScope" in error_msg or "NotFound" in error_msg:
                            rprint(f"    [yellow]💡 Tip: The scope might not exist or you may need different permissions[/yellow]")
                        ## The process should stop to make sure user(and our operators) is aware of the failure
                        self.failed_commands.append({
                            "cmd": cmd_str,
                            "error": error_msg,
                        })
                    
            except subprocess.TimeoutExpired:
                rprint(f"  [red]❌ Timeout assigning role {role_name}[/red]")
            except Exception as e:
                rprint(f"  [red]❌ Error assigning role {role_name}: {e}[/red]")
        
        rprint(f"\n[cyan]📊 Successfully assigned {success_count}/{len(required_roles)} roles[/cyan]")
        
        if success_count < len(required_roles):
            rprint(f"[yellow]💡 You can manually assign missing roles in the Azure Portal:[/yellow]")
            rprint(f"[dim]https://portal.azure.com/#view/Microsoft_Azure_SubscriptionManagement/SubscriptionMenuBlade/~/accessControl/subscriptionId/{subscription_id}[/dim]")
        
        return success_count > 0

    def create_export_via_cli(self, subscription_id: str, 
                            resource_group: str, storage_account: str, container_name: str = "cloudhiro",
                            folder_path: str = None, definition_type: str = "AmortizedCost",
                            timeframe: str = "MonthToDate", recurrence: str = "Daily",
                            export_subscription_id: str = None, export_subcription_name: str = None,
                            start_date: str = None, end_date: str = None, billing_account_id = None) -> Dict:
        """Create cost management export using Azure CLI"""
        try:
            if definition_type == "AmortizedCost":
                export_name = "CloudhiroAmortizedCost"
            elif definition_type == "ActualCost":
                export_name = "CloudhiroActualCost"
            if billing_account_id:
                scope = billing_account_id
                storage_directory = definition_type.lower()
            elif export_subscription_id and export_subcription_name:
                scope = f"/subscriptions/{export_subscription_id}"
                storage_directory = f"{export_subcription_name}_{export_subscription_id}/{definition_type.lower()}"
            # Build the storage account resource ID
            storage_account_id = f"/subscriptions/{subscription_id}/resourceGroups/{resource_group}/providers/Microsoft.Storage/storageAccounts/{storage_account}"
            
            # Set default folder path if not provided
            if not folder_path:
                folder_path = f"exports/{export_name}"
            
            # Format dates in yyyy-MM-ddTHH:mm:ss format (no Z suffix)
            if not start_date:
                now = datetime.utcnow() + timedelta(minutes=5)
                start_date = now.strftime("%Y-%m-%dT%H:%M:%S")
                end_date = (now + timedelta(weeks=52*5)).strftime("%Y-%m-%dT%H:%M:%S")
            else:
                # start_date = start_date.replace()
                start_date_original = start_date[:10] if len(start_date) > 10 else start_date  # Convert to yyyy-mm-dd format
                
                # Ensure provided dates are in the correct format
                try:
                    # Parse and reformat start_date if it's not in the correct format
                    if 'T' not in start_date:
                        # If only date is provided, add time
                        start_date = f"{start_date}T00:00:00"
                    elif start_date.endswith('Z'):
                        # Remove Z suffix if present
                        start_date = start_date.rstrip('Z')
                    
                    # Parse and reformat end_date if it's not in the correct format
                    if 'T' not in end_date:
                        # If only date is provided, add time
                        end_date = f"{end_date}T23:59:59"
                    elif end_date.endswith('Z'):
                        # Remove Z suffix if present
                        end_date = end_date.rstrip('Z')
                        
                except Exception:
                    # If date parsing fails, use default dates
                    now = datetime.utcnow()
                    start_date = now.strftime("%Y-%m-%dT%H:%M:%S")
                    end_date = (now + timedelta(weeks=52*5)).strftime("%Y-%m-%dT%H:%M:%S")
            
            recurrence_period = f'from={start_date} to={end_date}'
            # Build the base command
            if timeframe == "MonthToDate":
                cmd = [
                    "az", "costmanagement", "export", "create",
                    "--name", export_name,
                    "--type", definition_type,
                    "--scope", scope,
                    "--timeframe", timeframe,
                    "--storage-account-id", storage_account_id,
                    "--storage-container", container_name,
                    "--storage-directory", storage_directory,
                    "--recurrence", "Daily",
                    "--schedule-status", "Active",
                    "--subscription", subscription_id,
                    "--recurrence-period", recurrence_period,  # Placeholder for optional recurrence period
                    "--output", "json"
                ]
            elif timeframe == "Custom":
                # For Custom timeframe, we need to provide specific start and end dates
                export_name = f"{export_name}_{start_date_original}"
                time_period = f"from={start_date} to={end_date}"
                cmd = [
                    "az", "costmanagement", "export", "create",
                    "--name", export_name,
                    "--type", definition_type,
                    "--scope", scope,
                    "--timeframe", timeframe,
                    "--time-period", time_period, 
                    "--storage-account-id", storage_account_id,
                    "--storage-container", container_name,
                    "--storage-directory", storage_directory,
                    "--subscription", subscription_id,
                    "--output", "json"
                ]
            
            # Add recurrence period if dates are provided
            
            self.console.print(f"[blue]🔄 Creating cost management export '{export_name}'...[/blue]")
            self.console.print(f"[dim]Executing: {' '.join(cmd[:-2])}[/dim]") 
            
            # Execute the command
            cmd = ' '.join(cmd)
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=60, shell=True)
            
            if result.returncode == 0:
                try:
                    export_data = json.loads(result.stdout)
                    self.console.print(f"[green]✅ Export '{export_name}' created successfully![/green]")
                    
                    # Add full export name to successful exports array
                    full_export_name = f"{scope}/providers/Microsoft.CostManagement/exports/{export_name}"
                    self.successful_exports.append(full_export_name)
                    
                    return {
                        "success": True,
                        "export_name": export_name,
                        "export_data": export_data,
                        "scope": scope,
                        "storage_account": storage_account,
                        "container": container_name,
                        "folder_path": storage_directory
                    }
                except json.JSONDecodeError:
                    # Command succeeded but couldn't parse JSON
                    self.console.print(f"[green]✅ Export '{export_name}' created successfully![/green]")
                    
                    # Add full export name to successful exports array
                    full_export_name = f"{scope}/providers/Microsoft.CostManagement/exports/{export_name}"
                    self.successful_exports.append(full_export_name)
                    
                    return {
                        "success": True,
                        "export_name": export_name,
                        "message": "Export created successfully (JSON parsing failed)",
                        "raw_output": result.stdout
                    }
            else:
                error_msg = result.stderr.strip() if result.stderr else "Unknown error"
                self.console.print(f"[red]❌ Failed to create export: {error_msg}[/red]")
                self.failed_commands.append({
                    "cmd": cmd,
                    "error": error_msg,
                })
                return {
                    "success": False,
                    "error": error_msg,
                    "stdout": result.stdout
                }
                
        except subprocess.TimeoutExpired:
            return {"success": False, "error": "Command timed out"}
        except Exception as e:
            return {"success": False, "error": str(e)}

    def display_successful_exports(self):
        """Display all successful exports created in this session"""
        if not self.successful_exports:
            self.console.print("[yellow]No successful exports yet in this session[/yellow]")
            return
        
        self.console.print(f"\n[green]✅ Successful Exports ({len(self.successful_exports)}):[/green]")
        for export_name in self.successful_exports:
            self.console.print(f"  • {export_name}")
    
    def display_role_assignment_summary(self, required_roles: List[tuple], resource_group: str = None, storage_account: str = None, tenant_id: str = None):
        """Display a summary of roles that will be assigned"""
        
        # Build role descriptions
        role_descriptions = {
            "Storage Blob Data Reader": "Read billing export files from storage containers",
            "Reader and Data Access": "Access storage account data and metadata",
            "Reader": "View management group resources and configurations", 
            "Monitoring Reader": "Access monitoring data and metrics",
            "Reservations Reader": "View reservation information and capacity data"
        }
        
        # Build scope descriptions
        scope_info = []
        storage_roles = []
        mgmt_roles = []
        capacity_roles = []
        
        for role_name, scope in required_roles:
            if "Storage" in scope and resource_group and storage_account:
                storage_roles.append(f"• [cyan]{role_name}[/cyan] - {role_descriptions.get(role_name, 'Azure role')}")
            elif "Management" in scope and tenant_id:
                mgmt_roles.append(f"• [cyan]{role_name}[/cyan] - {role_descriptions.get(role_name, 'Azure role')}")
            elif "Capacity" in scope:
                capacity_roles.append(f"• [cyan]{role_name}[/cyan] - {role_descriptions.get(role_name, 'Azure role')}")
        
        # Build the summary
        summary_parts = []
        
        if storage_roles:
            summary_parts.append(f"""[yellow]Storage Account Roles:[/yellow]
{chr(10).join(storage_roles)}
[dim]Scope: Storage Account '{storage_account}' in Resource Group '{resource_group}'[/dim]""")
        
        if mgmt_roles:
            summary_parts.append(f"""[yellow]Management Group Roles:[/yellow]
{chr(10).join(mgmt_roles)}
[dim]Scope: Management Group (Tenant ID: {tenant_id})[/dim]""")
        
        if capacity_roles:
            summary_parts.append(f"""[yellow]Capacity/Reservation Roles:[/yellow]
{chr(10).join(capacity_roles)}
[dim]Scope: Microsoft Capacity Provider (tenant-wide)[/dim]""")
        
        if summary_parts:
            role_summary = "\n\n".join(summary_parts)
            rprint(Panel.fit(role_summary, border_style="yellow", title=f"Roles to Assign ({len(required_roles)} total)"))
        else:
            rprint(Panel.fit("[yellow]No roles to assign[/yellow]", border_style="yellow", title="Role Assignment"))

    def create_service_principal_with_secret(self, principal_name: str, app_id: str, object_id: str, headers: dict) -> Dict:
        """Create service principal with client secret instead of certificate"""
        try:
            self.console.print("[blue]Creating service principal with client secret...[/blue]")
            
            # Add a client secret to the application
            # Generate a secure random password
            secret_value = ''.join(secrets.choice(string.ascii_letters + string.digits + '!@#$%^&*') for _ in range(32))
            
            secret_payload = {
                "passwordCredential": {
                    "displayName": f"{principal_name}-secret",
                    "endDateTime": (datetime.now() + timedelta(days=365)).isoformat() + "Z",
                    "secretText": secret_value
                }
            }
            
            secret_url = f"https://graph.microsoft.com/v1.0/applications/{object_id}/addPassword"
            secret_response = requests.post(secret_url, headers=headers, json=secret_payload, timeout=30)
            secret_response.raise_for_status()
            secret_data = secret_response.json()
            
            # Create service principal
            sp_payload = {"appId": app_id}
            sp_url = "https://graph.microsoft.com/v1.0/servicePrincipals"
            sp_response = requests.post(sp_url, headers=headers, json=sp_payload, timeout=30)
            sp_response.raise_for_status()
            sp_data = sp_response.json()
            
            sp_object_id = sp_data.get("id")
            
            # Get tenant ID
            tenant_info_url = "https://graph.microsoft.com/v1.0/organization"
            tenant_response = requests.get(tenant_info_url, headers=headers, timeout=30)
            tenant_response.raise_for_status()
            tenant_data = tenant_response.json()
            tenant_id = tenant_data["value"][0]["id"] if tenant_data.get("value") else "unknown"
            
            result = {
                "displayName": principal_name,
                "appId": app_id,
                "objectId": sp_object_id,
                "tenantId": tenant_id,
                "clientSecret": secret_value,
                "secretId": secret_data.get("keyId"),
                "authMethod": "client_secret"
            }
            
            self.console.print(f"[green]✅ Service Principal created with client secret[/green]")
            return result
            
        except Exception as e:
            self.console.print(f"[red]❌ Error creating service principal with secret: {str(e)}[/red]")
            return {}

    def list_service_principals_by_name(self, name_prefix: str = "Costi") -> List[Dict]:
        """List service principals whose display name starts with the specified prefix"""
        rprint(f"\n[yellow]🔍 Searching for service principals starting with '{name_prefix}'...[/yellow]")
        
        try:
            # Get fresh headers for Graph API
            headers = self.get_headers("https://graph.microsoft.com/.default")
            if not headers:
                return []
            
            # Search for applications (service principals are based on applications)
            apps_url = "https://graph.microsoft.com/v1.0/applications"
            apps_params = {
                "$filter": f"startswith(displayName,'{name_prefix}')",
                "$select": "id,appId,displayName,createdDateTime"
            }
            
            apps_response = requests.get(apps_url, headers=headers, params=apps_params)
            
            if apps_response.status_code != 200:
                rprint(f"[red]❌ Failed to fetch applications: {apps_response.status_code}[/red]")
                return []
            
            apps_data = apps_response.json()
            applications = apps_data.get('value', [])
            
            if not applications:
                rprint(f"[yellow]📭 No applications found with names starting with '{name_prefix}'[/yellow]")
                return []
            
            # For each application, get the corresponding service principal
            service_principals = []
            
            for app in applications:
                try:
                    # Find service principal for this application
                    sp_url = "https://graph.microsoft.com/v1.0/servicePrincipals"
                    sp_params = {
                        "$filter": f"appId eq '{app['appId']}'",
                        "$select": "id,appId,displayName,servicePrincipalType,createdDateTime"
                    }
                    
                    sp_response = requests.get(sp_url, headers=headers, params=sp_params)
                    
                    if sp_response.status_code == 200:
                        sp_data = sp_response.json()
                        sp_list = sp_data.get('value', [])
                        
                        if sp_list:
                            sp = sp_list[0]  # Should be only one SP per app
                            service_principals.append({
                                "displayName": sp.get('displayName'),
                                "appId": sp.get('appId'),
                                "objectId": sp.get('id'),
                                "servicePrincipalType": sp.get('servicePrincipalType'),
                                "createdDateTime": sp.get('createdDateTime'),
                                "applicationId": app.get('id')
                            })
                
                except Exception as e:
                    rprint(f"[yellow]⚠️  Error fetching SP for app {app.get('displayName')}: {e}[/yellow]")
                    continue
            
            if service_principals:
                self.display_service_principals_table(service_principals, name_prefix)
            else:
                rprint(f"[yellow]📭 No service principals found for applications starting with '{name_prefix}'[/yellow]")
            
            return service_principals
            
        except Exception as e:
            rprint(f"[red]❌ Error searching service principals: {e}[/red]")
            return []

    def display_service_principals_table(self, service_principals: List[Dict], name_prefix: str):
        """Display service principals in a formatted table"""
        table = Table(title=f"🔍 Service Principals starting with '{name_prefix}'")
        table.add_column("Display Name", style="cyan", width=30)
        table.add_column("Application ID", style="blue", width=36)
        table.add_column("Object ID", style="dim", width=36)
        table.add_column("Type", style="green", width=12)
        table.add_column("Created", style="yellow", width=12)
        
        for sp in service_principals:
            # Format creation date
            created_date = "Unknown"
            if sp.get('createdDateTime'):
                try:
                    dt = datetime.fromisoformat(sp['createdDateTime'].replace('Z', '+00:00'))
                    created_date = dt.strftime('%Y-%m-%d')
                except:
                    created_date = sp['createdDateTime'][:10]  # Take first 10 chars
            
            table.add_row(
                sp.get('displayName', 'N/A'),
                sp.get('appId', 'N/A'),
                sp.get('objectId', 'N/A'),
                sp.get('servicePrincipalType', 'N/A'),
                created_date
            )
        
        rprint(table)
        rprint(f"\n[green]✅ Found {len(service_principals)} service principal(s)[/green]")

    def create_billing_export_wrapper(self, cost_export_types, months_back: int, resource_group: str, storage_account: str, export_subcscription_id: str = None, export_subscription_name: str = None, billing_account_id: str = None):
        for cost_type in cost_export_types:
            # first create the recurring month to date exports
            self.create_export_via_cli(self.subscription_id, resource_group, storage_account,
                                       definition_type=cost_type, timeframe="MonthToDate", 
                                       recurrence="Monthly",
                                       export_subcription_name=export_subscription_name,
                                       export_subscription_id=export_subcscription_id, billing_account_id=billing_account_id)
            if months_back > 0:
                # then create custom exports for past months
                for month_offset in range(1, months_back + 1):
                    start_date = (datetime.now().replace(day=1) - timedelta(days=1)).replace(day=1) - timedelta(days=(month_offset - 1) * 30)
                    start_date = start_date.replace(day=1)
                    end_date = (start_date + timedelta(days=31)).replace(day=1) - timedelta(days=1)
                    self.create_export_via_cli(self.subscription_id, resource_group, storage_account,
                                               definition_type=cost_type, timeframe="Custom", recurrence="None",
                                               start_date=start_date.strftime("%Y-%m-%d"),
                                               end_date=end_date.strftime("%Y-%m-%d"),
                                               export_subcription_name=export_subscription_name,
                                               export_subscription_id=export_subcscription_id,
                                               billing_account_id=billing_account_id)

    def trigger_export_execution(self, scope: str = None, export_name: str = None, export_full_name: str = None) -> bool:
        """Trigger immediate execution of an export"""
        try:
            # Get fresh headers for management API
            headers = self.get_headers("https://management.azure.com/.default")
            if not headers:
                return False
            
            if export_full_name:
                trigger_url = f"https://management.azure.com{export_full_name}/run"
            else:
                trigger_url = f"https://management.azure.com{scope}/providers/Microsoft.CostManagement/exports/{export_name}/run"


            trigger_params = {"api-version": "2023-03-01"}
            
            rprint(f"[yellow]⚡ Triggering export execution...[/yellow]")
            
            trigger_response = requests.post(trigger_url, headers=headers, params=trigger_params)
            
            if trigger_response.status_code in [200, 202]:
                rprint("[green]✅ Export execution triggered successfully![/green]")
                rprint("[dim]Note: The export will be processed asynchronously and may take several minutes to complete.[/dim]")
                self.failed_commands.append({
                    "cmd": f"POST {trigger_url}",
                    "response": f"HTTP {trigger_response.status_code} - {trigger_response.text}"
                })
                return True
            elif trigger_response.status_code == 429:
                sleep(5)
                trigger_response = requests.post(trigger_url, headers=headers, params=trigger_params)
                if trigger_response.status_code in [200, 202]:
                    rprint("[green]✅ Export execution triggered successfully after retry![/green]")
                    return True
                self.failed_commands.append({
                    "cmd": f"POST {trigger_url}",
                    "error": f"HTTP {trigger_response.status_code} - {trigger_response.text}"
                })
                return False
            else:
                self.failed_commands.append({
                    "cmd": f"POST {trigger_url}",
                    "error": f"HTTP {trigger_response.status_code} - {trigger_response.text}"
                })
                rprint(f"[yellow]⚠️  Failed to trigger export: {trigger_response.status_code}[/yellow]")
                rprint(f"[yellow]⚠️  Failed URL was {trigger_url}[/yellow]")
                return False
                
        except Exception as e:
            rprint(f"[yellow]⚠️  Error triggering export: {e}[/yellow]")
            return False

    def display_export_info(self, export_data: Dict, export_name: str, start_date: str, end_date: str, storage_info: Dict):
        """Display information about the created export"""
        
        info_text = f"""
[bold green]📊 Billing Export Created Successfully[/bold green]

[yellow]Export Details:[/yellow]
• Name: [cyan]{export_name}[/cyan]
• Type: [cyan]Amortized Cost[/cyan]
• Period: [cyan]{start_date} to {end_date}[/cyan]
• Granularity: [cyan]Daily[/cyan]
• Format: [cyan]CSV[/cyan]

[yellow]Destination:[/yellow]
• Storage Account: [cyan]{storage_info.get('storageAccountName', 'N/A')}[/cyan]
• Container: [cyan]{storage_info.get('containerName', 'N/A')}[/cyan]
• Path: [cyan]exports/{export_name}/[/cyan]

[yellow]Schedule:[/yellow]
• Recurrence: [cyan]Monthly[/cyan]
• Status: [cyan]Active[/cyan]

[bold blue]Next Steps:[/bold blue]
[dim]• The export will run automatically each month[/dim]
[dim]• Files will be saved to the specified storage container[/dim]
[dim]• You can trigger manual runs anytime through the Azure portal[/dim]
        """
        
        rprint(Panel.fit(info_text, border_style="green", title="Export Configuration"))

    def select_subscription_context(self) -> Dict:
        """Select and set the Azure subscription context for the configuration process"""
        try:
            self.console.print("[bold blue]Step 0: Subscription Selection[/bold blue]")
            self.console.print("[dim]Choose the Azure subscription to use for this configuration[/dim]\n")
            
            # Get available subscriptions
            headers = self.get_headers("https://management.azure.com/.default")
            if not headers:
                self.console.print("[red]❌ Failed to get authentication headers[/red]")
                return {}
            
            subscription_url = "https://management.azure.com/subscriptions"
            sub_params = {"api-version": "2020-01-01"}
            sub_response = requests.get(subscription_url, headers=headers, params=sub_params)
            
            if sub_response.status_code != 200:
                self.console.print(f"[red]❌ Failed to fetch subscriptions: {sub_response.status_code}[/red]")
                return {}
            
            subscriptions = sub_response.json().get("value", [])
            if not subscriptions:
                self.console.print("[red]❌ No subscriptions found[/red]")
                return {}
            
            # Let user choose subscription
            if len(subscriptions) == 1:
                selected_subscription = subscriptions[0]
                subscription_id = selected_subscription["subscriptionId"]
                subscription_name = selected_subscription.get("displayName", subscription_id)
                self.console.print(f"[cyan]🎯 Using subscription: {subscription_name}[/cyan]")
            else:
                self.console.print(f"\n[yellow]📋 Found {len(subscriptions)} subscriptions. Please choose one:[/yellow]")
                choices = []
                for sub in subscriptions:
                    name = sub.get('displayName', sub['subscriptionId'])
                    choices.append(Choice(value=sub, name=f"📊 {name}"))
                
                selected_subscription = inquirer.fuzzy(
                    message="Select subscription for this configuration:",
                    choices=choices,
                    instruction="(Use ↑↓ arrow keys, Enter to select)"
                ).execute()
                
                subscription_id = selected_subscription["subscriptionId"]
                subscription_name = selected_subscription.get("displayName", subscription_id)
            
            # Store the selected subscription for use throughout the process
            self.subscription_id = subscription_id
            self.console.print(f"[green]✅ Selected subscription: {subscription_name}[/green]")
            self.console.print(f"[dim]Subscription ID: {subscription_id}[/dim]\n")
            subprocess.run(["az", "account", "set", "--subscription", subscription_id], capture_output=True)
            
            return {
                "subscription_id": subscription_id,
                "subscription_name": subscription_name,
                "subscription_data": selected_subscription
            }
            
        except Exception as e:
            self.console.print(f"[red]❌ Error selecting subscription: {str(e)}[/red]")
            return {}

    def _normalize_subscription_name(self, name: str) -> str:
        """Normalize subscription name: lowercase and replace special chars/spaces with underscores"""
        import re
        # Convert to lowercase and replace spaces and special characters with underscores
        normalized = re.sub(r'[^a-zA-Z0-9]', '_', name.lower())
        # Remove multiple consecutive underscores
        normalized = re.sub(r'_+', '_', normalized)
        # Remove leading/trailing underscores
        return normalized.strip('_')

    def select_multiple_subscriptions_context(self) -> List[tuple]:
        """Select multiple Azure subscriptions with checkbox interface and 'Select All' option"""
        try:
            self.console.print("[bold blue]Step 0: Multiple Subscription Selection[/bold blue]")
            self.console.print("[dim]Choose the Azure subscriptions to use for this configuration[/dim]\n")
            
            # Get available subscriptions
            headers = self.get_headers("https://management.azure.com/.default")
            if not headers:
                self.console.print("[red]❌ Failed to get authentication headers[/red]")
                return []
            
            subscription_url = "https://management.azure.com/subscriptions"
            sub_params = {"api-version": "2020-01-01"}
            sub_response = requests.get(subscription_url, headers=headers, params=sub_params)
            
            if sub_response.status_code != 200:
                self.console.print(f"[red]❌ Failed to fetch subscriptions: {sub_response.status_code}[/red]")
                return []
            
            subscriptions = sub_response.json().get("value", [])
            if not subscriptions:
                self.console.print("[red]❌ No subscriptions found[/red]")
                return []
            
            # If only one subscription, return it automatically
            if len(subscriptions) == 1:
                selected_subscription = subscriptions[0]
                subscription_name = selected_subscription.get("displayName", selected_subscription["subscriptionId"])
                subscription_id = selected_subscription["subscriptionId"]
                normalized_name = self._normalize_subscription_name(subscription_name)
                self.console.print(f"[cyan]🎯 Using only available subscription: {subscription_name}[/cyan]")
                return [(subscription_id, normalized_name)]
            
            # Show subscription count and ask for selection method
            self.console.print(f"[yellow]📋 Found {len(subscriptions)} subscriptions[/yellow]")
            
            # Ask if user wants to select all or choose specific ones
            selection_method = inquirer.select(
                message="How would you like to select subscriptions?",
                choices=[
                    Choice("select_all", "🎯 Select All Subscriptions"),
                    Choice("choose_specific", "📋 Choose Specific Subscriptions"),
                    Choice("cancel", "🔙 Cancel Selection")
                ],
                instruction="(Use ↑↓ arrow keys, Enter to select)"
            ).execute()
            
            if selection_method == "cancel":
                self.console.print("[yellow]⚠️ Subscription selection cancelled[/yellow]")
                return []
            elif selection_method == "select_all":
                self.console.print(f"[green]✅ Selected all {len(subscriptions)} subscriptions[/green]")
                
                # Convert to tuples and display selected subscriptions
                result_tuples = []
                for i, sub in enumerate(subscriptions, 1):
                    name = sub.get("displayName", sub["subscriptionId"])
                    subscription_id = sub["subscriptionId"]
                    normalized_name = self._normalize_subscription_name(name)
                    result_tuples.append((subscription_id, normalized_name))
                    self.console.print(f"  {i}. [cyan]{name}[/cyan] → [dim]{normalized_name}[/dim] ([cyan]{subscription_id}[/cyan])")
                
                return result_tuples
            else:
                # Let user choose specific subscriptions using checkbox
                self.console.print("\n[yellow]📋 Choose specific subscriptions:[/yellow]")
                
                choices = []
                for sub in subscriptions:
                    name = sub.get('displayName', sub['subscriptionId'])
                    # Format: "Display Name (subscription-id-preview)"
                    label = f"📊 {name}"
                    if len(name) > 40:  # Truncate long names
                        label = f"📊 {name[:37]}..."
                    
                    choices.append(Choice(value=sub, name=label))
                
                selected_subscriptions = inquirer.checkbox(
                    message="Select subscriptions to configure:",
                    choices=choices,
                    instruction="(Use ↑↓ arrow keys, Space to select/deselect, Enter to confirm)"
                ).execute()
                
                if not selected_subscriptions:
                    self.console.print("[yellow]⚠️ No subscriptions selected[/yellow]")
                    return []
                
                # Convert to tuples and display selected subscriptions
                result_tuples = []
                self.console.print(f"[green]✅ Selected {len(selected_subscriptions)} subscription(s):[/green]")
                for i, sub in enumerate(selected_subscriptions, 1):
                    name = sub.get("displayName", sub["subscriptionId"])
                    subscription_id = sub["subscriptionId"]
                    normalized_name = self._normalize_subscription_name(name)
                    result_tuples.append((subscription_id, normalized_name))
                    self.console.print(f"  {i}. [cyan]{name}[/cyan] → [dim]{normalized_name}[/dim] ([cyan]{subscription_id}[/cyan])")
                
                return result_tuples
            
        except Exception as e:
            self.console.print(f"[red]❌ Error selecting subscriptions: {str(e)}[/red]")
            return []

    def start_configuration_process(self):
        """Execute the streamlined configuration process"""
        try:
            self.console.print("\n[bold green]🚀 Starting Azure Configuration Process[/bold green]\n")
            
            # Step 0: Select Subscription Context
            subscription_context = self.select_subscription_context()
            if not subscription_context:
                self.console.print("[red]❌ Failed to select subscription. Cannot continue.[/red]")
                return False
            
            self.subscription_id = subscription_context["subscription_id"]
            
            # Step 1: Create Service Principal first
            self.console.print("[bold blue]Step 1: Service Principal Creation[/bold blue]")
            self.console.print("[dim]Creating a service principal for secure Azure resource access[/dim]\n")
            
            create_sp = inquirer.confirm(
                message="Would you like to create a new service principal with certificate authentication?",
                default=True
            ).execute()
            
            sp_info = None
            if create_sp:
                sp_info = self.create_service_principal_with_cert()
                if not sp_info:
                    self.console.print("[yellow]⚠️ Service principal creation failed or was cancelled[/yellow]")
                    # Ask if user wants to continue without service principal
                    continue_without_sp = inquirer.confirm(
                        message="Would you like to continue the configuration process without creating a service principal?",
                        default=False
                    ).execute()
                    
                    if not continue_without_sp:
                        self.console.print("[red]❌ Configuration process cancelled[/red]")
                        return False
            else:
                self.console.print("[yellow]⚠️ Skipping service principal creation[/yellow]")
            
            ##################################################################################################
            # Step 2: Create Storage Account
            self.console.print(f"\n[bold blue]Step 2: Storage Account Creation[/bold blue]")
            self.console.print("[dim]Create a new storage account for billing exports and cost management[/dim]\n")
            
            use_existing_storage = inquirer.select(
                message="would you like to use a new storage account or an existing one?",
                choices=[
                    Choice("create_new", "🆕 Create New Storage Account"),
                    Choice("use_existing", "📂 Use Existing Storage Account"),
                    Choice("skip", "🚫 Skip Storage Account Creation"),
                ]
            ).execute()

            # create_storage = inquirer.confirm(
            #     message="Would you like to create a new storage account? \n This is required if this tenant is also the billing account tenant.",
            #     default=True
            # ).execute()
            
            storage_info = None
            if use_existing_storage == "create_new":
                storage_info = self.create_new_storage_account()
                if not storage_info:
                    self.console.print("[yellow]⚠️ Storage account creation failed or was cancelled[/yellow]")
                    # Ask if user wants to continue without creating storage account
                    continue_without_storage = inquirer.confirm(
                        message="Would you like to continue the configuration process without creating a storage account?",
                        default=False
                    ).execute()
                    
                    if not continue_without_storage:
                        self.console.print("[red]❌ Configuration process cancelled[/red]")
                        return False
            elif use_existing_storage == "use_existing":
                storage_info = self.list_existing_storage_accounts()
                if not storage_info:
                    self.console.print("[yellow]⚠️ Failed to get existing storage account information[/yellow]")
                    # Ask if user wants to continue without storage account
                    continue_without_storage = inquirer.confirm(
                        message="Would you like to continue the configuration process without specifying a storage account?",
                        default=False
                    ).execute()
                    
                    if not continue_without_storage:
                        self.console.print("[red]❌ Configuration process cancelled[/red]")
                        return False
            else:
                self.console.print("[yellow]⚠️ Skipping storage account creation[/yellow]")
            
            # Step 3: Grant Permissions
            self.console.print(f"\n[bold blue]Step 3: Role Assignment (Permissions)[/bold blue]")
            
            # Show role summary and offer to assign permissions if we have a service principal
            if sp_info:
                if storage_info:
                    self.console.print("[dim]Assign roles to the service principal for secure access to Azure resources[/dim]\n")
                else:
                    self.console.print("[dim]Assign tenant-level roles to the service principal (storage-specific roles will be skipped)[/dim]\n")
                
                assign_roles_now = inquirer.confirm(
                    message="Would you like to assign roles to the service principal now?",
                    default=True,
                    instruction="(Recommended - enables monitoring and reservation access)"
                ).execute()
                
                if assign_roles_now:
                    # Get required information for role assignment
                    tenant_id = sp_info.get('tenantId')
                    app_id = sp_info.get('appId')
                    
                    # Get subscription ID (from storage_info if available, otherwise need to get it)
                    subscription_id = None
                    resource_group = None
                    storage_account = None
                    
                    if storage_info:
                        subscription_id = storage_info.get('subscription_id')
                        resource_group = storage_info.get('resource_group')
                        storage_account = storage_info.get('storage_account')
                    
                    if app_id and tenant_id:
                        success = self.assign_roles_via_cli(
                            app_id=app_id,
                            subscription_id=subscription_id,
                            tenant_id=tenant_id,
                            resource_group=resource_group,
                            storage_account=storage_account
                        )
                        if success:
                            self.console.print(f"[green]✅ Roles assigned successfully![/green]")
                            # Update sp_info to track role assignment
                            sp_info['roles_assigned'] = True
                        else:
                            self.console.print(f"[yellow]⚠️ Some roles may not have been assigned. You can assign them later.[/yellow]")
                            sp_info['roles_assigned'] = False
                    else:
                        self.console.print(f"[red]❌ Missing required information for role assignment (App ID or Tenant ID)[/red]")
                        sp_info['roles_assigned'] = False
                else:
                    self.console.print("[yellow]⚠️ Skipping role assignment - you can assign roles later from the main menu[/yellow]")
                    sp_info['roles_assigned'] = False
                self.grant_log_analytics_api_permission(app_id=app_id)
            else:
                self.console.print("[yellow]⚠️ No service principal created - skipping role assignment[/yellow]")
            
            # Step 4: Choose account type
            self.console.print(f"\n[bold blue]Step 4: Account Type Selection[/bold blue]")
            account_type = self.select_account_type()
            
            selected_account = None
            
            # Step 5: If not CSP, fetch and choose billing account
            if account_type != "CSP":
                self.console.print(f"\n[bold blue]Step 5: Billing Account Selection ({account_type})[/bold blue]")
                selected_account = self.quick_account_chooser(account_type)
                
                if not selected_account:
                    self.console.print("[red]❌ No billing account selected. Cannot continue.[/red]")
                    return False
                
                self.show_account_details(selected_account)
            else:
                subscriptions_for_export = self.select_multiple_subscriptions_context()
                self.console.print(f"\n[blue]ℹ️ CSP account type selected - skipping billing account selection[/blue]")
                # For CSP, create a placeholder account object
                selected_account = {
                    'display_name': 'CSP Partner Account',
                    'name': 'CSP_PARTNER_MANAGED',
                    'id': 'CSP_PARTNER_MANAGED',
                    'type': 'CSP',
                    'status': 'Partner Managed',
                    'agreement_type': 'Cloud Solution Provider',
                    'country': 'N/A'
                }
            
            # Step 6: Resource group and storage account configuration
            self.console.print(f"\n[bold blue]Step 6: Resource Configuration[/bold blue]")
            
            # Use storage account info if created, otherwise ask user for existing details
            if storage_info:
                resource_group = storage_info.get('resource_group')
                storage_account = storage_info.get('storage_account')
                self.console.print(f"[green]✅ Using newly created storage account:[/green]")
                self.console.print(f"[cyan]• Resource Group: {resource_group}[/cyan]")
                self.console.print(f"[cyan]• Storage Account: {storage_account}[/cyan]")
            else:
                # Ask for existing resource information
                self.console.print("[dim]Please provide details for your existing Azure resources:[/dim]")
                
                resource_group = inquirer.text(
                    message="Enter Resource Group name:",
                    instruction="(Required - the resource group containing your storage account)"
                ).execute()
                
                if not resource_group or not resource_group.strip():
                    self.console.print("[red]❌ Resource Group name is required[/red]")
                    return False
                
                storage_account = inquirer.text(
                    message="Enter Storage Account name:",
                    instruction="(Required - the name of your Azure Storage Account)"
                ).execute()
                
                if not storage_account or not storage_account.strip():
                    self.console.print("[red]❌ Storage Account name is required[/red]")
                    return False
                
                resource_group = resource_group.strip()
                storage_account = storage_account.strip()

            #  step 8: prompt user to choose multiple options - amortized and/or actual
            cost_export_types = inquirer.checkbox(
                message="Choose the type(s) of billing export to create:",
                choices=[
                    Choice("AmortizedCost", "Amortized Costs", enabled=True),
                    Choice("ActualCost", "Actual Costs")
                ],
                instruction="(Use ↑↓ arrow keys, Space to select/deselect, Enter to confirm)"
            ).execute()
            
            if not cost_export_types:
                self.console.print("[yellow]⚠️ No export types selected. Skipping export creation.[/yellow]")
                cost_export_types = []
            else:
                type_names = []
                for export_type in cost_export_types:
                    if export_type == "AmortizedCost":
                        type_names.append("Amortized Costs")
                    elif export_type == "ActualCost":
                        type_names.append("Actual Costs")
                self.console.print(f"[cyan]📊 Selected export types: {', '.join(type_names)}[/cyan]")
            
            ## now prompt for how many months to go back
            months_back = inquirer.number(
                message="Enter the number of past months to include in the export (e.g., 1 for last month):",
                default=6,
                instruction="(This will create an export covering the specified number of past months)"
            ).execute()

            try:
                months_back = int(months_back)
            except Exception:
                months_back = 0

            
            if account_type != "CSP":
                self.create_billing_export_wrapper(cost_export_types=cost_export_types,
                                                months_back=int(months_back),
                                                resource_group=resource_group,
                                                storage_account=storage_account,
                                                billing_account_id=selected_account.get('id'))
            elif account_type == "CSP" and len(subscriptions_for_export) > 0:
                for subscription_tuple in subscriptions_for_export:
                    export_subscription_id = subscription_tuple[0]
                    export_subscription_name = subscription_tuple[1]
                    self.create_billing_export_wrapper(cost_export_types=cost_export_types,
                                                    months_back=int(months_back),
                                                    resource_group=resource_group,
                                                    storage_account=storage_account,
                                                    export_subcscription_id=export_subscription_id,
                                                    export_subscription_name=export_subscription_name,
                                                    billing_account_id=None)

            # step 6.5: Run the export creation process
            for index, successful_export in enumerate(self.successful_exports):
                self.console.print(f"[green]✅ runnig export now {successful_export} export(s)[/green]")
                self.trigger_export_execution(export_full_name=successful_export),
                if index % 10 == 0:
                    sleep(3)

            # Step 7: Display final configuration
            self.console.print(f"\n[bold blue]Step 7: Final Configuration Summary[/bold blue]")
            
            # Show service principal info if created
            if sp_info:
                sp_summary = f"""
[yellow]Service Principal:[/yellow]
• Name: [cyan]{sp_info.get('displayName', 'N/A')}[/cyan]
• App ID(Client ID): [cyan]{sp_info.get('appId', 'N/A')}[/cyan]
• Tenant ID: [cyan]{sp_info.get('tenantId', 'N/A')}[/cyan]
• Certificate: [cyan]{sp_info.get('certificatePath', 'N/A')}[/cyan]
• Roles Assigned: [cyan]No (will be assigned later)[/cyan]
** save the information above securely, it is required for CloudHiro registration **
** Download the certificate to your local machine and upload to CloudHiro  ** 
"""
                rprint(Panel.fit(sp_summary, border_style="green", title="Service Principal Created"))
            
            # Show storage account info if created
            if storage_info:
                storage_summary = f"""
[yellow]Storage Account:[/yellow]
• Name: [cyan]{storage_info.get('storage_account', 'N/A')}[/cyan]
• Resource Group: [cyan]{storage_info.get('resource_group', 'N/A')}[/cyan]
• Location: [cyan]{storage_info.get('location', 'N/A')}[/cyan]
• Subscription: [cyan]{storage_info.get('subscription_id', 'N/A')}[/cyan]
• Container: [cyan]{'cloudhiro' if storage_info.get('container_created') else 'Not created'}[/cyan]

Go to https://ops.cloudhiro.com/AZR/AZRRegistration.php to register your Azure account with CloudHiro using the service principal and storage account information above. 
"""
                rprint(Panel.fit(storage_summary, border_style="blue", title="Storage Account Created"))
            
            # Final summary message
            components_created = []
            if sp_info:
                components_created.append(f"Service Principal: {sp_info.get('displayName', 'Created')}")
            if storage_info:
                components_created.append(f"Storage Account: {storage_info.get('storage_account', 'Created')}")
            
            if components_created:
                self.console.print(f"\n[bold green]✅ Configuration process completed successfully![/bold green]")
                self.console.print(f"[green]• {selected_account.get('display_name', 'Billing Account')}: Selected[/green]")
                for component in components_created:
                    self.console.print(f"[green]• {component}[/green]")
                self.console.print(f"[green]• Resource Group: {resource_group}[/green]")
                self.console.print(f"[green]• Storage Account: {storage_account}[/green]")
                
                # Show role assignment status
                if sp_info and sp_info.get('roles_assigned'):
                    self.console.print(f"[green]• Roles: Assigned successfully[/green]")
                elif sp_info:
                    self.console.print(f"[yellow]• Roles: Not assigned (can be done later)[/yellow]")
            else:
                self.console.print(f"\n[bold yellow]⚠️ Configuration process completed (no new resources created)[/bold yellow]")
                self.console.print(f"[yellow]• Billing Account: {selected_account.get('display_name', 'Selected')}[/yellow]")
                self.console.print(f"[yellow]• Resource Group: {resource_group}[/yellow]")
                self.console.print(f"[yellow]• Storage Account: {storage_account}[/yellow]")
                self.console.print(f"[dim]You can create resources later using the main menu[/dim]")

            if self.failed_commands:
                self.console.print(f"\n[bold red]⚠️ Some operations failed during the configuration process, full data saved to file[/bold red]")
                print_errors = inquirer.confirm(
                    message="would you like to print to the errors as well?",
                    default=False
                ).execute()
                if print_errors:
                    for cmd in self.failed_commands:
                        self.console.print(f"[red]• Command: {cmd['cmd']}[/red]")
                        self.console.print(f"[red]  Error: {cmd['error']}[/red]")
                    self.console.print(f"[yellow]⚠️ Please review the errors above. You may need to manually complete some steps or assign roles from the Azure portal.[/yellow]")
                with open("failed_commands.json", "w") as f:
                    json.dump(self.failed_commands, f, indent=4)

            
            return True
            
        except KeyboardInterrupt:
            self.console.print(f"\n[yellow]⚠️ Configuration process cancelled by user[/yellow]")
            return False
        except Exception as e:
            self.console.print(f"[red]❌ Error during configuration: {str(e)}[/red]")
            return False

    def run(self):
        """Main application loop"""
        try:
            self.show_welcome()
            
            # Setup authentication
            if not self.setup_authentication():
                self.console.print("[red]Cannot proceed without authentication[/red]")
                return
            
            # Initialize clients
            self.initialize_clients()
            
            while True:
                choice = self.main_menu()
                
                if choice == "exit":
                    break
                
                elif choice == "start_config":
                    success = self.start_configuration_process()
                    if success:
                        self.console.print(f"\n[green]✅ Configuration completed successfully![/green]")
                    else:
                        self.console.print(f"\n[yellow]⚠️ Configuration was not completed.[/yellow]")
                    input("\nPress Enter to continue...")
                
                elif choice == "create_storage":
                    storage_info = self.create_new_storage_account()
                    if storage_info:
                        self.console.print(f"\n[green]✅ Storage Account created successfully![/green]")
                        
                        # Display the created storage account information
                        container_status = "✅ Created" if storage_info.get('container_created') else "⚠️  Creation failed (can be created manually)"
                        
                        info_text = f"""
[bold green]💾 Storage Account Created[/bold green]

[yellow]Storage Account Details:[/yellow]
• Name: [cyan]{storage_info.get('storage_account', 'N/A')}[/cyan]
• Resource Group: [cyan]{storage_info.get('resource_group', 'N/A')}[/cyan]
• Location: [cyan]{storage_info.get('location', 'N/A')}[/cyan]
• Subscription: [dim]{storage_info.get('subscription_id', 'N/A')}[/dim]
• Container 'cloudhiro': {container_status}

[yellow]Next Steps:[/yellow]
• You can now use this storage account for billing exports
• The storage account is configured with secure defaults (Standard LRS, HTTPS only)
• A 'cloudhiro' container has been created for billing data
• Consider setting up lifecycle management policies for cost optimization
                        """
                        rprint(Panel.fit(info_text, border_style="green", title="Storage Account Created"))
                    else:
                        self.console.print(f"\n[yellow]⚠️ Storage Account creation failed or was cancelled.[/yellow]")
                    input("\nPress Enter to continue...")
                
                elif choice == "create_sp":
                    sp_info = self.create_service_principal_with_cert()
                    if sp_info:
                        self.console.print(f"\n[green]✅ Service Principal created successfully![/green]")
                    else:
                        self.console.print(f"\n[yellow]⚠️ Service Principal creation failed.[/yellow]")
                    input("\nPress Enter to continue...")
                
                elif choice == "list_sp":
                    service_principals = self.list_service_principals_by_name("Costi")
                    if service_principals:
                        self.console.print(f"\n[green]✅ Found {len(service_principals)} service principal(s)[/green]")
                    else:
                        self.console.print(f"\n[yellow]📭 No service principals found starting with 'Costi'[/yellow]")
                    input("\nPress Enter to continue...")
                
                elif choice == "test_access":
                    self.test_billing_access()
                    input("\nPress Enter to continue...")
                
                elif choice == "auth_info":
                    self.show_auth_info()
                    input("\nPress Enter to continue...")
        
        except KeyboardInterrupt:
            rprint("\n[yellow]Operation cancelled by user[/yellow]")
        except Exception as e:
            self.console.print(f"[red]Unexpected error: {e}[/red]")
        finally:
            rprint("\n[blue]👋 Thanks for using Azure Billing Manager![/blue]")

def main():
    """Entry point"""
    import sys
    debug = "--debug" in sys.argv or "-v" in sys.argv
    
    app = AzureBillingManager(debug=debug)
    
    if debug:
        print("🐛 Debug mode enabled")
    
    app.run()

if __name__ == "__main__":
    main()