"""
Utility functions specific to payment requests module.
"""

import logging
import string
import random
from typing import Optional
from sqlalchemy import select
from sqlalchemy.orm import Session


def get_full_name(user_obj) -> str:
    """
    Get the full name of a user from a user object.
    
    Args:
        user_obj: User object with first_name, last_name, or full_name attributes
        
    Returns:
        Full name string or fallback identifier
    """
    if not user_obj:
        return "Unknown User"
    
    # Check for full_name attribute first
    if hasattr(user_obj, 'full_name') and user_obj.full_name:
        return user_obj.full_name
    
    # Construct from first_name and last_name
    first_name = getattr(user_obj, 'first_name', '') or ''
    last_name = getattr(user_obj, 'last_name', '') or ''
    
    if first_name and last_name:
        return f"{first_name} {last_name}".strip()
    elif first_name:
        return first_name.strip()
    elif last_name:
        return last_name.strip()
    
    # Fallback to username or email
    if hasattr(user_obj, 'username') and user_obj.username:
        return user_obj.username
    elif hasattr(user_obj, 'email') and user_obj.email:
        return user_obj.email
    
    return "Unknown User"


def get_device_details(user_agent_string: Optional[str]) -> dict:
    """
    Extract device details from user agent string.
    
    Args:
        user_agent_string: HTTP User-Agent header string
        
    Returns:
        Dictionary containing device details
    """
    if not user_agent_string:
        return {
            'browser': 'Unknown',
            'os': 'Unknown',
            'device': 'Unknown',
            'is_mobile': False,
            'is_tablet': False,
            'is_desktop': True
        }
    
    user_agent = user_agent_string.lower()
    
    # Basic browser detection
    browser = 'Unknown'
    if 'chrome' in user_agent:
        browser = 'Chrome'
    elif 'firefox' in user_agent:
        browser = 'Firefox'
    elif 'safari' in user_agent:
        browser = 'Safari'
    elif 'edge' in user_agent:
        browser = 'Edge'
    elif 'opera' in user_agent:
        browser = 'Opera'
    
    # Basic OS detection
    os_name = 'Unknown'
    if 'windows' in user_agent:
        os_name = 'Windows'
    elif 'mac' in user_agent:
        os_name = 'macOS'
    elif 'linux' in user_agent:
        os_name = 'Linux'
    elif 'android' in user_agent:
        os_name = 'Android'
    elif 'ios' in user_agent or 'iphone' in user_agent or 'ipad' in user_agent:
        os_name = 'iOS'
    
    # Device type detection
    is_mobile = any(keyword in user_agent for keyword in [
        'mobile', 'android', 'iphone', 'ipod', 'blackberry', 'windows phone'
    ])
    
    is_tablet = any(keyword in user_agent for keyword in [
        'tablet', 'ipad', 'kindle', 'silk', 'playbook'
    ])
    
    is_desktop = not (is_mobile or is_tablet)
    
    device_type = 'Desktop'
    if is_mobile:
        device_type = 'Mobile'
    elif is_tablet:
        device_type = 'Tablet'
    
    return {
        'browser': browser,
        'os': os_name,
        'device': device_type,
        'is_mobile': is_mobile,
        'is_tablet': is_tablet,
        'is_desktop': is_desktop
    }


def round_half_up(number, decimals: int = 0) -> float:
    """
    This function rounds a number half up. Eg. 18.5 will round to 19
    
    Args:
        number: Number to round
        decimals: Number of decimal places
        
    Returns:
        float: Rounded number
    """
    rounded_value = int(number * (10**decimals) + 0.5) / (10**decimals)
    if rounded_value % 1 == 0:
        rounded_value = int(rounded_value)
    return rounded_value


def get_final_amount(
    db: Session,
    payment_request_id: int,
    amount: float = 0,
    invoice_id: Optional[str] = None,
    tip: float = 0,
    shipping_amount: float = 0,
) -> float:
    """
    Calculate final amount including adjustments, tips, and fees
    
    Args:
        db: Database session
        payment_request_id: Payment request ID
        amount: Base amount
        invoice_id: Optional invoice ID
        tip: Tip amount
        shipping_amount: Shipping amount
        
    Returns:
        float: Final calculated amount
    """
    from src.apps.payment_requests.models.payment_request import PaymentRequest
    from src.apps.payment_requests.models.payment_request_adjustments import PaymentRequestAdjustments
    
    logger = logging.getLogger(__name__)
    
    try:
        
        # Get payment request from database
        stmt = select(PaymentRequest).where(PaymentRequest.id == payment_request_id)
        result = db.execute(stmt)
        payment_request = result.scalar_one_or_none()
        
        if not payment_request:
            logger.warning(f"Payment request {payment_request_id} not found")
            return amount or 0.0
        
        # Start with base amount or payment request amount
        final_amount = amount or payment_request.amount
        
        # Add shipping fee
        final_amount += payment_request.shipping_fee or 0.0
        final_amount += shipping_amount
        
        # Add tip
        final_amount += tip
        
        # Get and apply adjustments (discounts, late fees, etc.)
        stmt = select(PaymentRequestAdjustments).where(
            PaymentRequestAdjustments.payment_request_id == payment_request_id
        )
        result = db.execute(stmt)
        adjustments = result.scalars().all()
        
        for adjustment in adjustments:
            if adjustment.adjustment_type == "discount":
                if adjustment.is_percentage:
                    discount_amount = final_amount * (adjustment.amount / 100)
                else:
                    discount_amount = adjustment.amount
                final_amount -= discount_amount
            elif adjustment.adjustment_type == "fee":
                if adjustment.is_percentage:
                    fee_amount = final_amount * (adjustment.amount / 100)
                else:
                    fee_amount = adjustment.amount
                final_amount += fee_amount
            elif adjustment.adjustment_type == "late_fee":
                final_amount += adjustment.amount
        
        # Apply tax if applicable
        if payment_request.tax_percent and payment_request.tax_percent > 0:
            tax_amount = final_amount * (payment_request.tax_percent / 100)
            final_amount += tax_amount
        
        # Ensure amount is not negative
        final_amount = max(final_amount, 0.0)
        
        return round_half_up(final_amount, 2)
        
    except Exception as e:
        logger.error(f"Error calculating final amount for payment request {payment_request_id}: {e}")
        return amount or 0.0


def generate_unique_payment_request_literal(db: Session, length: int = 9) -> str:
    """
    Generate unique payment request literal
    
    Args:
        db: Database session
        length: Length of the literal
        
    Returns:
        str: Unique payment request literal
    """
    from src.apps.payment_requests.models.payment_request import PaymentRequest
    from src.apps.base.utils.functions import generate_secure_id
    
    max_attempts = 100
    for _ in range(max_attempts):
        # Generate random literal
        literal = ''.join(random.choices(string.ascii_uppercase + string.digits, k=length))
        
        # Check if it already exists
        stmt = select(PaymentRequest).where(PaymentRequest.payment_request_literal == literal)
        result = db.execute(stmt)
        existing = result.scalar_one_or_none()
        
        if not existing:
            return literal
    
    # Fallback if all attempts failed
    return generate_secure_id(length=length).upper()