from typing import Optional, List, Union
from playwright.async_api import Locator, expect
import logging

# Import the BasePage class
from base_page import BasePage

logger = logging.getLogger(__name__)


class LoginPage(BasePage):
    """
    Login Page Object Model class.
    
    This class represents the login page and provides methods to interact with
    login-specific elements and functionality.
    
    Guidelines for Page Object Model:
    1. Each page should have its own class
    2. Locators should be defined as class constants or properties
    3. Methods should represent user actions or page behaviors
    4. Use descriptive method names that reflect business operations
    5. Return appropriate objects (self for chaining, other pages for navigation)
    6. Keep locators maintainable and readable
    7. Add validation methods for page state verification
    """

    # ==================== PAGE LOCATORS ====================
    # Best Practice: Define all locators as class constants
    # Use data-testid attributes when possible for stability
    # Group related locators together
    # Use descriptive names that match the business terminology

    # Form Elements
    USERNAME_INPUT = "[data-testid='username-input']"
    PASSWORD_INPUT = "[data-testid='password-input']"
    LOGIN_BUTTON = "[data-testid='login-button']"
    REMEMBER_ME_CHECKBOX = "[data-testid='remember-me']"
    
    # Alternative locators using different strategies
    USERNAME_INPUT_ALT = "input[name='username']"
    PASSWORD_INPUT_ALT = "input[type='password']"
    LOGIN_BUTTON_ALT = "button:has-text('Login')"
    
    # Links and Navigation
    FORGOT_PASSWORD_LINK = "[data-testid='forgot-password-link']"
    SIGN_UP_LINK = "a:has-text('Sign up')"
    HOME_LINK = "[data-testid='home-link']"
    
    # Messages and Notifications
    ERROR_MESSAGE = "[data-testid='error-message']"
    SUCCESS_MESSAGE = ".alert-success"
    LOADING_SPINNER = "[data-testid='loading-spinner']"
    
    # Form Validation
    USERNAME_ERROR = "[data-testid='username-error']"
    PASSWORD_ERROR = "[data-testid='password-error']"
    FORM_ERROR = "[data-testid='form-error']"
    
    # Page Elements
    PAGE_TITLE = "h1"
    LOGIN_FORM = "[data-testid='login-form']"
    LOGO = "[data-testid='company-logo']"
    
    # Dynamic Locators (use methods for dynamic content)
    @staticmethod
    def user_profile_link(username: str) -> str:
        return f"[data-testid='user-profile-{username}']"
    
    @staticmethod
    def notification_message(message_type: str) -> str:
        return f"[data-testid='notification-{message_type}']"

    # ==================== PAGE URL CONSTANTS ====================
    PAGE_URL = "/login"
    EXPECTED_TITLE = "Login - Your App"

    def __init__(self, page, base_url: str = ""):
        """
        Initialize the Login Page.
        
        Args:
            page: Playwright page object
            base_url: Base URL of the application
        """
        super().__init__(page, base_url)
        
    # ==================== NAVIGATION METHODS ====================
    
    async def navigate_to_login_page(self) -> 'LoginPage':
        """
        Navigate to the login page.
        
        Returns:
            LoginPage instance for method chaining
        """
        await self.navigate_to(self.PAGE_URL)
        await self.wait_for_page_to_load()
        return self
    
    # ==================== PAGE VERIFICATION METHODS ====================
    
    async def verify_page_loaded(self) -> 'LoginPage':
        """
        Verify that the login page is properly loaded.
        
        Returns:
            LoginPage instance for method chaining
            
        Best Practice: Always verify page state before interactions
        """
        try:
            # Verify URL
            current_url = self.get_url()
            assert self.PAGE_URL in current_url, f"Expected URL to contain '{self.PAGE_URL}', but got '{current_url}'"
            
            # Verify page title
            title = await self.get_title()
            assert self.EXPECTED_TITLE in title, f"Expected title to contain '{self.EXPECTED_TITLE}', but got '{title}'"
            
            # Verify key elements are present
            await self.wait_for_element_visible(self.LOGIN_FORM)
            await self.wait_for_element_visible(self.USERNAME_INPUT)
            await self.wait_for_element_visible(self.PASSWORD_INPUT)
            await self.wait_for_element_visible(self.LOGIN_BUTTON)
            
            logger.info("Login page loaded successfully")
            return self
            
        except Exception as e:
            logger.error(f"Failed to verify login page: {str(e)}")
            await self.take_screenshot("login_page_verification_failed")
            raise

    async def wait_for_page_to_load(self) -> 'LoginPage':
        """Wait for login page specific elements to load."""
        await self.wait_for_element_visible(self.LOGIN_FORM)
        await self.wait_for_element_visible(self.USERNAME_INPUT)
        return self

    # ==================== FORM INTERACTION METHODS ====================
    
    async def enter_username(self, username: str) -> 'LoginPage':
        """
        Enter username in the username field.
        
        Args:
            username: Username to enter
            
        Returns:
            LoginPage instance for method chaining
        """
        logger.info(f"Entering username: {username}")
        await self.wait_for_element_visible(self.USERNAME_INPUT)
        await self.fill_text(self.USERNAME_INPUT, username)
        return self
    
    async def enter_password(self, password: str) -> 'LoginPage':
        """
        Enter password in the password field.
        
        Args:
            password: Password to enter (will be masked in logs)
            
        Returns:
            LoginPage instance for method chaining
        """
        logger.info("Entering password")  # Don't log actual password
        await self.wait_for_element_visible(self.PASSWORD_INPUT)
        await self.fill_text(self.PASSWORD_INPUT, password)
        return self
    
    async def click_login_button(self) -> 'LoginPage':
        """
        Click the login button.
        
        Returns:
            LoginPage instance for method chaining
        """
        logger.info("Clicking login button")
        await self.wait_for_element_enabled(self.LOGIN_BUTTON)
        await self.click(self.LOGIN_BUTTON)
        return self
    
    async def toggle_remember_me(self, check: bool = True) -> 'LoginPage':
        """
        Toggle the remember me checkbox.
        
        Args:
            check: True to check, False to uncheck
            
        Returns:
            LoginPage instance for method chaining
        """
        if check:
            await self.check_checkbox(self.REMEMBER_ME_CHECKBOX)
            logger.info("Checked remember me checkbox")
        else:
            await self.uncheck_checkbox(self.REMEMBER_ME_CHECKBOX)
            logger.info("Unchecked remember me checkbox")
        return self
    
    # ==================== COMBINED ACTION METHODS ====================
    # Best Practice: Provide high-level methods that combine multiple actions
    
    async def login(self, username: str, password: str, remember_me: bool = False) -> 'LoginPage':
        """
        Complete login process with username and password.
        
        Args:
            username: Username to use for login
            password: Password to use for login
            remember_me: Whether to check remember me option
            
        Returns:
            LoginPage instance for method chaining
        """
        logger.info(f"Performing login for user: {username}")
        
        await self.enter_username(username)
        await self.enter_password(password)
        
        if remember_me:
            await self.toggle_remember_me(True)
        
        await self.click_login_button()
        
        # Wait for page to process the login
        await self.wait_for_login_processing()
        
        return self
    
    async def quick_login(self, username: str, password: str) -> 'LoginPage':
        """
        Quick login method for common test scenarios.
        
        Args:
            username: Username
            password: Password
            
        Returns:
            LoginPage instance for method chaining
        """
        return await self.login(username, password, remember_me=False)
    
    # ==================== VALIDATION AND ASSERTION METHODS ====================
    
    async def verify_login_success(self, expected_redirect_url: Optional[str] = None) -> bool:
        """
        Verify that login was successful.
        
        Args:
            expected_redirect_url: Optional URL to verify redirect
            
        Returns:
            True if login successful, False otherwise
        """
        try:
            # Wait for potential success message or redirect
            await self.page.wait_for_load_state('networkidle', timeout=self.default_timeout)
            
            # Check if we're redirected away from login page
            current_url = self.get_url()
            if self.PAGE_URL not in current_url:
                logger.info("Login successful - redirected away from login page")
                
                if expected_redirect_url and expected_redirect_url not in current_url:
                    logger.warning(f"Expected redirect to {expected_redirect_url}, but got {current_url}")
                    return False
                
                return True
            
            # Check for success message if still on login page
            if await self.is_visible(self.SUCCESS_MESSAGE):
                logger.info("Login successful - success message displayed")
                return True
            
            logger.warning("Login success verification failed")
            return False
            
        except Exception as e:
            logger.error(f"Error verifying login success: {str(e)}")
            return False
    
    async def verify_login_failed(self) -> bool:
        """
        Verify that login failed with appropriate error message.
        
        Returns:
            True if login properly failed with error, False otherwise
        """
        try:
            # Wait for error message to appear
            await self.wait_for_element_visible(self.ERROR_MESSAGE, timeout=self.short_timeout)
            
            error_text = await self.get_text(self.ERROR_MESSAGE)
            logger.info(f"Login failed as expected with error: {error_text}")
            return True
            
        except Exception as e:
            logger.error(f"Error verifying login failure: {str(e)}")
            return False
    
    async def get_error_message(self) -> str:
        """
        Get the current error message text.
        
        Returns:
            Error message text or empty string if no error
        """
        try:
            if await self.is_visible(self.ERROR_MESSAGE):
                return await self.get_text(self.ERROR_MESSAGE)
            return ""
        except Exception:
            return ""
    
    async def verify_field_validation_error(self, field: str) -> bool:
        """
        Verify that a specific field has validation error.
        
        Args:
            field: Field name ('username' or 'password')
            
        Returns:
            True if validation error is present
        """
        error_locator = self.USERNAME_ERROR if field.lower() == 'username' else self.PASSWORD_ERROR
        
        try:
            await self.wait_for_element_visible(error_locator, timeout=self.short_timeout)
            return True
        except Exception:
            return False
    
    # ==================== NAVIGATION METHODS ====================
    
    async def click_forgot_password(self):
        """
        Click the forgot password link.
        
        Returns:
            Could return a ForgotPasswordPage object in a real application
        """
        await self.click(self.FORGOT_PASSWORD_LINK)
        logger.info("Navigated to forgot password page")
        # In a real application, you would return a ForgotPasswordPage object
        # return ForgotPasswordPage(self.page, self.base_url)
    
    async def click_sign_up(self):
        """
        Click the sign up link.
        
        Returns:
            Could return a SignUpPage object in a real application
        """
        await self.click(self.SIGN_UP_LINK)
        logger.info("Navigated to sign up page")
        # In a real application, you would return a SignUpPage object
        # return SignUpPage(self.page, self.base_url)
    
    # ==================== UTILITY METHODS ====================
    
    async def wait_for_login_processing(self) -> 'LoginPage':
        """
        Wait for login processing to complete.
        This could involve waiting for spinner to disappear or button to be re-enabled.
        
        Returns:
            LoginPage instance for method chaining
        """
        try:
            # Wait for loading spinner to disappear if present
            if await self.is_visible(self.LOADING_SPINNER):
                await self.wait_for_element_hidden(self.LOADING_SPINNER)
            
            # Wait for login button to be re-enabled
            await self.wait_for_element_enabled(self.LOGIN_BUTTON)
            
        except Exception as e:
            logger.warning(f"Timeout waiting for login processing: {str(e)}")
        
        return self
    
    async def is_login_button_enabled(self) -> bool:
        """Check if login button is enabled."""
        return await self.is_enabled(self.LOGIN_BUTTON)
    
    async def get_username_field_value(self) -> str:
        """Get current value in username field."""
        return await self.get_value(self.USERNAME_INPUT)
    
    async def clear_login_form(self) -> 'LoginPage':
        """Clear all form fields."""
        await self.clear_text(self.USERNAME_INPUT)
        await self.clear_text(self.PASSWORD_INPUT)
        
        # Uncheck remember me if checked
        if await self.is_checked(self.REMEMBER_ME_CHECKBOX):
            await self.uncheck_checkbox(self.REMEMBER_ME_CHECKBOX)
        
        logger.info("Login form cleared")
        return self
    
    # ==================== KEYBOARD ACTIONS ====================
    
    async def login_with_enter_key(self, username: str, password: str) -> 'LoginPage':
        """
        Login using Enter key instead of clicking button.
        
        Args:
            username: Username to enter
            password: Password to enter
            
        Returns:
            LoginPage instance for method chaining
        """
        await self.enter_username(username)
        await self.enter_password(password)
        await self.press_key("Enter")
        await self.wait_for_login_processing()
        return self
    
    # ==================== ADVANCED INTERACTIONS ====================
    
    async def login_with_tab_navigation(self, username: str, password: str) -> 'LoginPage':
        """
        Login using tab navigation (accessibility testing).
        
        Args:
            username: Username to enter
            password: Password to enter
            
        Returns:
            LoginPage instance for method chaining
        """
        # Click on username field and enter text
        await self.click(self.USERNAME_INPUT)
        await self.fill_text(self.USERNAME_INPUT, username)
        
        # Tab to password field
        await self.press_key("Tab")
        await self.fill_text(self.PASSWORD_INPUT, password)
        
        # Tab to login button and press Enter
        await self.press_key("Tab")
        await self.press_key("Enter")
        
        await self.wait_for_login_processing()
        return self
    
    # ==================== DEBUGGING AND TESTING HELPERS ====================
    
    async def debug_login_form_state(self) -> dict:
        """
        Get current state of login form for debugging.
        
        Returns:
            Dictionary with form state information
        """
        state = {
            'url': self.get_url(),
            'title': await self.get_title(),
            'username_visible': await self.is_visible(self.USERNAME_INPUT),
            'password_visible': await self.is_visible(self.PASSWORD_INPUT),
            'login_button_visible': await self.is_visible(self.LOGIN_BUTTON),
            'login_button_enabled': await self.is_enabled(self.LOGIN_BUTTON),
            'username_value': await self.get_username_field_value(),
            'remember_me_checked': await self.is_checked(self.REMEMBER_ME_CHECKBOX),
            'error_visible': await self.is_visible(self.ERROR_MESSAGE),
            'error_message': await self.get_error_message()
        }
        
        logger.info(f"Login form state: {state}")
        return state


# ==================== USAGE EXAMPLES ====================
"""
Example usage of the LoginPage class:

async def test_successful_login(page):
    # Initialize page object
    login_page = LoginPage(page, base_url="https://example.com")
    
    # Navigate and verify page
    await login_page.navigate_to_login_page()
    await login_page.verify_page_loaded()
    
    # Perform login
    await login_page.login("valid_user", "valid_password", remember_me=True)
    
    # Verify success
    success = await login_page.verify_login_success(expected_redirect_url="/dashboard")
    assert success, "Login should have been successful"

async def test_failed_login(page):
    login_page = LoginPage(page, base_url="https://example.com")
    
    await login_page.navigate_to_login_page()
    await login_page.login("invalid_user", "wrong_password")
    
    # Verify failure
    failed = await login_page.verify_login_failed()
    assert failed, "Login should have failed with error message"

async def test_form_validation(page):
    login_page = LoginPage(page, base_url="https://example.com")
    
    await login_page.navigate_to_login_page()
    await login_page.click_login_button()  # Submit empty form
    
    # Verify validation errors
    username_error = await login_page.verify_field_validation_error("username")
    password_error = await login_page.verify_field_validation_error("password")
    
    assert username_error and password_error, "Form validation should show errors for empty fields"
"""

# ==================== BEST PRACTICES SUMMARY ====================
"""
Key Best Practices Demonstrated:

1. **Locator Management**:
   - Define locators as class constants
   - Use data-testid attributes for stability
   - Provide alternative locator strategies
   - Use static methods for dynamic locators

2. **Method Organization**:
   - Group methods by functionality
   - Use descriptive method names
   - Return self for method chaining
   - Provide both atomic and composite actions

3. **Error Handling**:
   - Add try-catch blocks for critical operations
   - Log errors with context
   - Take screenshots on failures
   - Provide meaningful error messages

4. **Verification Methods**:
   - Always verify page state
   - Provide specific assertion methods
   - Check both positive and negative cases
   - Return boolean values for assertions

5. **Documentation**:
   - Document all public methods
   - Provide usage examples
   - Explain complex interactions
   - Include type hints

6. **Maintainability**:
   - Keep methods focused and single-purpose
   - Use constants for repeated values
   - Provide debugging helpers
   - Structure code logically

7. **Scalability**:
   - Extend from base class properly
   - Return appropriate page objects
   - Support method chaining
   - Make methods reusable
"""