import requests
import random
import string
import json
from cw_rpa import Input
from m365_user_manager.managers.environment_manager import EnvironmentManager
from m365_user_manager.exceptions.exceptions import PasswordManagerError

class PasswordManager:
    """
    Handles password generation and secure sharing functionality.
    """
    def __init__(self, discord_webhook_url: str = None):
        # Initialize environment manager to get config and logger
        self.discord_webhook_url = discord_webhook_url
        print(f"Discord webhook url: {self.discord_webhook_url}")
        env_manager = EnvironmentManager(discord_webhook_url=self.discord_webhook_url)
        self.running_in_asio, self.config = env_manager.initialize()
        self.logger = env_manager.setup_logging()
        if self.running_in_asio:
            self.input_form = Input()

        self.MIN_PASSWORD_LENGTH = 12
        self.MAX_TTL = 5  # Maximum TTL in days
        self.MAX_TTL_SECONDS = self.MAX_TTL * 86400  # Convert days to seconds

    def generate_secure_password(self) -> str:
        """Generate a secure password with mixed characters."""
        try:
            chars = {
                'uppercase': string.ascii_uppercase,
                'lowercase': string.ascii_lowercase,
                'digits': string.digits,
                'special': "!@#$%^&*()_+-="
            }
            
            # Ensure at least one character from each set
            password = [
                random.choice(chars['uppercase']),
                random.choice(chars['lowercase']),
                random.choice(chars['digits']),
                random.choice(chars['special'])
            ]
            
            # Fill remaining length with random characters
            all_chars = ''.join(chars.values())
            password.extend(random.choices(
                all_chars,
                k=self.MIN_PASSWORD_LENGTH - len(password)
            ))
            
            # Shuffle the password
            random.shuffle(password)
            
            return ''.join(password)
            
        except Exception as e:
            error_msg = f"Failed to generate secure password: {str(e)}"
            self.logger.error(error_msg)
            raise PasswordManagerError(error_msg, code=500)

    def create_secure_link(self, secret: str, ttl_days: int = 3) -> str:
        """Create a secure one-time link using Password Pusher service, falling back to onetimesecret.com."""
        if not secret:
            raise PasswordManagerError(
                "Cannot create secure link: secret cannot be empty",
                code=400
            )
            
        if not isinstance(ttl_days, int) or ttl_days <= 0:
            raise PasswordManagerError(
                "TTL must be a positive integer in days",
                code=400
            )
            
        ttl_days = min(ttl_days, self.MAX_TTL)

        # Try Password Pusher first
        try:
            # Get credentials based on environment
            if self.running_in_asio:
                try:
                    api_token = self.input_form.get_value("PasswordPushToken_1737575340807")
                except Exception as e:
                    self.logger.warning(f"Failed to get Password Pusher token from Asio: {str(e)}")
                    self.logger.info("Falling back to backup service...")
                    return self.backup_secure_link(secret, ttl_days)
            else:
                api_token = self.config.get('pwpush_token')

            if not api_token:
                self.logger.warning("Password Pusher token not found")
                self.logger.info("Falling back to backup service...")
                return self.backup_secure_link(secret, ttl_days)

            url = 'https://pwpush.com/p.json'
            headers = {
                'Accept': 'application/json',
                'Authorization': f"Bearer {api_token}"
            }
            
            data = {
                'password[payload]': secret,
                'password[expire_after_days]': ttl_days,
                'password[expire_after_views]': 3
            }
            
            self.logger.info(f"Creating Password Pusher link with TTL: {ttl_days} days")
            response = requests.post(
                url,
                headers=headers,
                data=data,
                timeout=30
            )
            
            if response.status_code in [401, 429]:
                self.logger.warning(f"Password Pusher returned status {response.status_code}")
                self.logger.info("Falling back to backup service...")
                return self.backup_secure_link(secret, ttl_days)
                
            response.raise_for_status()
            
            result = response.json()
            self.logger.info(f"Created secure link response: {result}")
            
            url = result.get('html_url')
            if not url:
                url = f"https://pwpush.com/p/{result['url_token']}/r"
                
            self.logger.info(f"Successfully created Password Pusher link: {url}")
            return url
                
        except requests.exceptions.Timeout:
            self.logger.warning("Timeout while creating Password Pusher link")
            self.logger.info("Falling back to backup service...")
            return self.backup_secure_link(secret, ttl_days)
            
        except requests.exceptions.RequestException as e:
            self.logger.warning(f"Error with Password Pusher: {str(e)}")
            self.logger.info("Falling back to backup service...")
            return self.backup_secure_link(secret, ttl_days)
            
        except (KeyError, ValueError, json.JSONDecodeError) as e:
            self.logger.warning(f"Error processing Password Pusher response: {str(e)}")
            self.logger.info("Falling back to backup service...")
            return self.backup_secure_link(secret, ttl_days)

    def backup_secure_link(self, secret: str, ttl: int = 86400) -> str:
        """
        Create a secure one-time link using onetimesecret.com service.

        Args:
            secret (str): The secret text/password to share
            ttl (int): Time to live in seconds (default 1 day, max 7 days)

        Returns:
            str: URL for the one-time secret

        Raises:
            PasswordManagerError: If link creation fails, input validation fails,
                                or service is unavailable
        """
        # multiply ttl by 86400 to convert days to seconds
        ttl *= 86400
        # Input validation
        if not secret:
            raise PasswordManagerError(
                "Cannot create secure link: secret cannot be empty",
                code=400
            )

        if not isinstance(ttl, int) or ttl <= 0:
            raise PasswordManagerError(
                "TTL must be a positive integer",
                code=400
            )

        # Ensure TTL doesn't exceed maximum
        ttl = min(ttl, self.MAX_TTL_SECONDS)

        url = 'https://onetimesecret.com/api/v1/share'
        data = {
            'secret': secret,
            'ttl': ttl
        }

        try:
            response = requests.post(
                url,
                data=data,
                timeout=30,
                headers={'Content-Type': 'application/x-www-form-urlencoded'}
            )

            # Handle specific HTTP status codes
            if response.status_code == 429:
                raise PasswordManagerError(
                    "Rate limit exceeded. Please try again later.",
                    code=429
                )

            if response.status_code == 413:
                raise PasswordManagerError(
                    "Secret exceeds maximum allowed size",
                    code=413
                )

            response.raise_for_status()

            result = response.json()
            if 'secret_key' not in result:
                raise PasswordManagerError(
                    "Invalid response from service: missing secret_key",
                    code=500
                )

            return f"https://onetimesecret.com/secret/{result['secret_key']}"

        except requests.exceptions.Timeout:
            error_msg = "Timeout while creating secure link"
            self.logger.error(error_msg)
            raise PasswordManagerError(error_msg, code=408)

        except requests.exceptions.RequestException as e:
            error_msg = f"Error creating secure link: {str(e)}"
            self.logger.error(error_msg)
            raise PasswordManagerError(
                error_msg,
                code=getattr(e.response, 'status_code', 500)
            )

        except (KeyError, ValueError, json.JSONDecodeError) as e:
            error_msg = f"Error processing service response: {str(e)}"
            self.logger.error(error_msg)
            raise PasswordManagerError(error_msg, code=500)