import os
import paramiko
import json
import logging
import boto3
from dotenv import load_dotenv
from concurrent.futures import ThreadPoolExecutor
from botocore.exceptions import NoCredentialsError, PartialCredentialsError

logger = logging.getLogger()
logger.setLevel(logging.INFO)

load_dotenv()

def get_secret(secret_name, region_name=os.environ.get('AWS_REGION_NAME')):
    session = boto3.session.Session()
    client = session.client(
        service_name='secretsmanager',
        region_name=region_name
    )

    try:
        response = client.get_secret_value(SecretId=secret_name)
        secret = response['SecretString']
        return json.loads(secret)
    except NoCredentialsError:
        logger.error("Credentials not available")
    except PartialCredentialsError:
        logger.error("Incomplete credentials provided")
    except Exception as e:
        logger.error(f"Error retrieving secret: {e}")
        return None

def load_router_ips(file_path):
    try:
        with open(file_path, 'r') as file:
            data = json.load(file)
            return data["routers"]
    except Exception as e:
        logger.error(f"Error loading router IPs: {e}")
        return {}

def show_commands(command, routers):
    try:
        logger.info(f"Received command: {command}")
        logger.info(f"Routers to connect to: {routers}")

        if not command:
            raise ValueError('Missing required parameter: command')
        if not routers:
            raise ValueError('Missing required parameter: routers')

        secret_name = os.environ.get('AWS_SECRETS_NAME')
        credentials = get_secret(secret_name)
        if not credentials:
            raise ValueError('Could not retrieve credentials from AWS Secrets Manager')

        username = credentials.get("username")
        password = credentials.get("password")

        if not username or not password:
            raise ValueError('Missing username or password in the retrieved credentials')

        all_routers = load_router_ips('devices/routers.json')
        if not all_routers:
            raise ValueError('Could not load router IPs from file')

        routers_to_connect = {router_name: all_routers[router_name] for router_name in routers if router_name in all_routers}
        if not routers_to_connect:
            raise ValueError('None of the specified routers found in the loaded IPs')

        def execute_command(router_name, router_info):
            try:
                management_ip = router_info.get('management_ip')

                if not management_ip:
                    raise ValueError(f'Missing management_ip for router: {router_name}')

                ssh = paramiko.SSHClient()
                ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
                
                # Explicitly disable key-based authentication
                ssh.connect(management_ip, username=username, password=password, allow_agent=False, look_for_keys=False)

                stdin, stdout, stderr = ssh.exec_command(command)
                command_output = stdout.read().decode('utf-8')
                ssh.close()
                logger.info(f"Command output for router {management_ip}: {command_output}")
                return {router_name: command_output}
            except Exception as e:
                logger.error(f"Error executing command on router {management_ip}: {str(e)}")
                return {router_name: f"Error: {str(e)}"}

        with ThreadPoolExecutor() as executor:
            futures = [executor.submit(execute_command, router_name, router_info) for router_name, router_info in routers_to_connect.items()]
            results = [future.result() for future in futures]

        combined_results = {}
        for result in results:
            combined_results.update(result)

        return {
            "status": "success",
            "command": command,
            "routers": list(routers_to_connect.keys()),
            "results": combined_results
        }
    except Exception as e:
        logger.error(f"Error in show_commands: {str(e)}")
        return {
            "status": "error",
            "message": str(e),
            "command": command,
            "routers": routers
        }
