import logging
from datetime import time

from zoneinfo import ZoneInfo
from django.db import transaction, IntegrityError, models
from django.db.models import Q
from django.utils import timezone
from urllib.parse import urlparse
from rest_framework import status
from rest_framework.response import Response

from apps.core.models import User
from apps.companies.constants import CompanyType
from apps.companies.models import (
    Company, OfficeHours,
    Holiday, SalesTiming,
    CompanyBotSettings,
    CompanyHistory, Service,
)
from apps.userprofile.services import ProfileServices
from utils.timezone_utils import get_company_time

logger = logging.getLogger(__name__)


def normalize_website(url: str) -> str:
    """
    Normalize a website URL into a consistent comparable format.

    This function ensures all website URLs are stored and compared
    in a uniform way by removing protocol, 'www', trailing slashes,
    and converting to lowercase.

    Steps performed:
    1. Adds 'http://' if the URL has no scheme (so it can be parsed).
    2. Extracts the domain part (netloc) only.
    3. Removes 'www.' prefix if present.
    4. Converts to lowercase.
    5. Removes any trailing slashes.

    Example:
        >>> normalize_website("https://www.ABC.com/")
        'abc.com'
        >>> normalize_website("abc.com")
        'abc.com'
        >>> normalize_website("http://abc.com:8000/test")
        'abc.com:8000'

    Args:
        url (str): The website URL to normalize.

    Returns:
        str: Normalized domain (e.g. "abc.com") or the original value if empty/None.
    """
    if not url:
        return url

    parsed = urlparse(url if '://' in url else f'http://{url}')
    netloc = parsed.netloc.lower()
    if netloc.startswith('www.'):
        netloc = netloc[4:]
    return netloc.rstrip('/')


def check_sales_time_by_phone(phone_number):
    """
    Check if it's sales time based on phone number
    """
    try:
        # Get bot by phone number
        bot = CompanyBotSettings.objects.get(phone_number=phone_number, is_active=True)
        company = bot.company

        current_time = get_company_time(company)

        timezone_offset = int(current_time.utcoffset().total_seconds() / 3600)

        is_holiday = False
        today = current_time.date()

        holiday = Holiday.objects.filter(
            company=company,
            bots=bot,
            start_date__lte=today,
            end_date__gte=today,
            is_active=True
        ).first()

        if holiday:
            is_holiday = True

        sales_timing = SalesTiming.objects.filter(company=company, bot='hazel', is_active=True).first()

        if sales_timing:
            is_sales_time = sales_timing.start_time <= current_time.time() <= sales_timing.end_time
        else:
            is_sales_time = False

        # Get company users with phone numbers for advisor data
        company_users = User.objects.filter(
            companies=company,
            is_active=True
        ).select_related('profile')

        advisor_list = []
        for user in company_users:
            if hasattr(user, 'profile') and user.profile.phone_number:
                advisor_name = f"{user.profile.first_name} {user.profile.last_name}".strip()
                advisor_list.append(f"{advisor_name} have id {user.id}")

        advisor_data = ", ".join(advisor_list) if advisor_list else "No advisor data"

        return {
            'is_office_hours': is_sales_time,
            'is_holiday': is_holiday,
            'holiday_message': holiday.message if holiday else 'No holiday today.',
            'timezone_offset': timezone_offset,
            'company_name': company.name,
            'advisor_data': advisor_data,
        }

    except CompanyBotSettings.DoesNotExist:
        return {
            'is_office_hours': False,
            'is_holiday': False,
            'holiday_message': '',
            'timezone_offset': -7,
            'advisor_data': 'No advisor data',
        }
    except SalesTiming.DoesNotExist:
        return {
            'is_office_hours': False,
            'timezone_offset': -7,
            'is_holiday': False,
            'holiday_message': '',
            'advisor_data': 'No advisor data',
        }
    except Exception as e:
        logger.warning(f"check_sales_time_by_phone {e}")
        return {
            'is_office_hours': False,
            'timezone_offset': -7,
            'is_holiday': False,
            'holiday_message': '',
            'advisor_data': 'No advisor data',
        }


def apply_holidays_to_all_companies(request):
    """Apply current company's holidays to all user's companies with bot relationships"""
    user = request.user

    if not user.active_company:
        return Response({'error': 'No active company'}, status=status.HTTP_400_BAD_REQUEST)

    # Require explicit confirmation
    if not request.data.get('confirm', False):
        return Response({'error': 'You must confirm to apply to all companies'}, status=status.HTTP_400_BAD_REQUEST)

    # Other companies
    user_companies = user.companies.filter(is_active=True).exclude(id=user.active_company.id)
    if not user_companies.exists():
        return Response({'error': 'No other companies found'}, status=status.HTTP_400_BAD_REQUEST)

    # Permission filtering
    allowed_companies = []
    denied = []
    for company in user_companies:
        if user.has_company_permission('edit_company', company):
            allowed_companies.append(company)
        else:
            denied.append(company.name)

    if not allowed_companies:
        return Response({
            'error': 'No permission to edit any company',
            'denied_companies': denied,
        }, status=status.HTTP_403_FORBIDDEN)

    # Get source holidays with bot relationships
    current_holidays = Holiday.objects.filter(company=user.active_company).prefetch_related('bots')
    if not current_holidays.exists():
        return Response({'error': 'No holidays found in current company'}, status=status.HTTP_400_BAD_REQUEST)

    # Validate holidays
    validation_error = validate_holidays(current_holidays)
    if validation_error:
        return Response({'error': validation_error}, status=status.HTTP_400_BAD_REQUEST)

    try:
        with transaction.atomic():
            Holiday.objects.filter(company__in=allowed_companies).delete()

            source_bots = {
                bot.bot_name: bot
                for bot in CompanyBotSettings.objects.filter(company=user.active_company)
            }

            created_holidays = []

            for company in allowed_companies:

                target_bots = {
                    bot.bot_name: bot
                    for bot in CompanyBotSettings.objects.filter(company=company)
                }

                for bot_name, target_bot in target_bots.items():
                    if bot_name in source_bots:
                        source_bot = source_bots[bot_name]

                        target_bot.is_active = source_bot.is_active
                        target_bot.save()

                for holiday in current_holidays:
                    new_holiday = Holiday.objects.create(
                        company=company,
                        name=holiday.name,
                        message=holiday.message,
                        start_date=holiday.start_date,
                        end_date=holiday.end_date,
                        is_active=holiday.is_active
                    )

                    matching_bots = [
                        target_bots[source_bot.bot_name]
                        for source_bot in holiday.bots.all()
                        if source_bot.bot_name in target_bots
                    ]

                    if matching_bots:
                        new_holiday.bots.set(matching_bots)

                    created_holidays.append(new_holiday)

            # 3. History logs
            history_logs = []
            count = current_holidays.count()
            for company in allowed_companies:
                history_logs.append(CompanyHistory(
                    company=company,
                    updated_by=user,
                    action='bulk_replace',
                    model_name='Holiday',
                    object_id=None,
                    details=f"Replaced {count} holidays from {user.active_company.name} → {company.name}"
                ))

            CompanyHistory.objects.bulk_create(history_logs, batch_size=100)

        # Success response
        response_data = {
            'message': f'Holidays applied to {len(allowed_companies)} companies',
            'applied_count': len(allowed_companies),
            'total_companies': user_companies.count(),
            'skipped_companies': denied if denied else None,
            'source_company': user.active_company.name,
            'items_count': current_holidays.count()
        }

        return Response(response_data, status=status.HTTP_200_OK if not denied else status.HTTP_206_PARTIAL_CONTENT)

    except Exception as e:
        return Response({'error': f'Operation failed: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


def get_company_working_info(phone_number):
    """
    Check if company is currently in working hours based on phone number

    Args:
        phone_number (str): Company phone number

    Returns:
        dict: {
            'is_office_hours': bool,  # false if holiday OR outside working hours, true if NOT holiday AND within working hours
            'is_holiday': bool,  # true if today is a holiday, false otherwise
            'holiday_message': str,  # Holiday message if applicable, None otherwise
            'timezone_offset': int,  # UTC offset like -7
            'transfer_instructions': str  # Transfer instructions for the company
        }
    """

    try:
        # Get company by phone number
        company_bot_settings = CompanyBotSettings.objects.get(
            phone_number=phone_number,
            is_active=True
        )
        company = company_bot_settings.company
    except Company.DoesNotExist:
        return {
            'company_name': "no company configures",
            'is_office_hours': False,  # Company not found = off hours
            'is_holiday': False,
            'holiday_message': 'No holiday today.',
            'timezone_offset': 0,
            'transfer_instructions': "Company not found",
            'advisor_data': "No advisor data"
        }

    # Get company timezone
    company_timezone = ZoneInfo(company.timezone)

    # Get current time in company timezone
    current_time_utc = timezone.now()
    current_time_company = get_company_time(company)

    logger.info("================================= COMPANY INFO ===============================")
    logger.info(f"Current time UTC: {current_time_utc}")
    logger.info(f"Current company time: {current_time_company}")
    logger.info(f"Company timezone: {company_timezone}")

    # Calculate timezone offset
    timezone_offset = current_time_company.utcoffset().total_seconds() / 3600

    # Get current day name
    current_day = current_time_company.strftime('%A').lower()

    # Check if today is a holiday
    is_holiday = False
    today = current_time_company.date()

    logger.info(f"Today date in timezone: {today}")

    holiday = Holiday.objects.filter(
        company=company,
        bots=company_bot_settings,
        start_date__lte=current_time_company,
        end_date__gte=current_time_company,
        is_active=True
    ).first()

    logger.info(f"Holiday : {holiday}")

    if holiday:
        is_holiday = True

    # Get office hours for current day
    try:
        office_hours = OfficeHours.objects.get(
            company=company,
            day=current_day
        )

        logger.info(f"Office hours: {office_hours}")
        # Check if office is open today
        if not office_hours.is_open:
            is_office_hours = False
        else:
            # Check if current time is within working hours
            current_time_only = current_time_company.time()
            is_within_hours = office_hours.start_time <= current_time_only <= office_hours.end_time
            logger.info(f"company id: {company.id}")
            logger.info(f"company name: {company.name}")
            logger.info(f"current day: {current_day}")
            logger.info(f"office_hours.start_time: {office_hours.start_time}")
            logger.info(f"office_hours.end_time: {office_hours.end_time}")
            logger.info(f"current_time_only: {current_time_only}")
            logger.info(f"is_within_hours: {is_within_hours}")

            # Check weather company working hours are ended or not.
            # is_office_hours will be true if the call will be during office hours and there will be no holidays.
            is_office_hours = not is_holiday and is_within_hours
    except OfficeHours.DoesNotExist:
        # No office hours set for this day
        is_office_hours = False

    # Build transfer instructions
    transfer_instructions = ""

    # Get company users with phone numbers
    company_users = User.objects.filter(
        companies=company,
        is_active=True,
        is_available=True
    ).select_related('profile')

    # First statement: Loop through users with phone numbers
    user_instructions = []
    for user in company_users:
        if hasattr(user, 'profile') and user.profile.phone_number:
            advisor_name = ProfileServices.resolve_display_name(user=user)
            advisor_phone = user.profile.phone_number
            user_instructions.append(
                f"If the user wants to reach, speak or talk to {advisor_name} then transfer to {advisor_phone}; "
            )

    # Add user instructions to transfer_instructions
    if user_instructions:
        transfer_instructions += " ".join(user_instructions) + " "

    # Second statement: BDC number
    if company.bdc_number:
        transfer_instructions += (
            f"If the user wants to schedule a meeting then transfer the call to BDC {company.bdc_number} "
            f"and say 'I'll transfer you to our BDC'; "
        )

    # Third statement: Default message
    transfer_instructions += (
        "If the user asks for an advisor who is not in the list, say 'I'm sorry, we don't have an advisor by that name at our company. Would you like to connect with someone else?'"
    )

    advisor_list = []
    for user in company_users:
        if hasattr(user, 'profile') and user.profile.phone_number:
            advisor_name = f"{user.profile.first_name} {user.profile.last_name}".strip()
            advisor_list.append(f"{advisor_name} have id {user.id}")

    advisor_data = ", ".join(advisor_list)

    services = get_company_services_for_bot(company)

    return {
        'company_name': company.name,
        'is_office_hours': is_office_hours,
        'holiday_message': holiday.message if holiday else 'No holiday today.',
        'is_holiday': is_holiday,
        'timezone_offset': int(timezone_offset),
        'transfer_instructions': (
            transfer_instructions
            if is_office_hours and not is_holiday
            else "Cannot transfer calls in off hours or holidays."
                 " Please record a message for the advisor instead."
        ),
        'advisor_data': advisor_data,
        'service_costs': services,
    }


db_shell_testing = """

from apps.companies.utils import get_rabeeca_ai_context
get_rabeeca_ai_context('+15673471975')

from apps.companies.utils import get_rabeeca_working_info
get_rabeeca_working_info('+15673471975')

from apps.companies.utils import get_company_ai_context
get_company_ai_context('+16813956085')

from apps.companies.utils import get_company_working_info
get_company_working_info('+16813956085')

from apps.companies.utils import get_company_services_for_bot
from apps.companies.models import Company
get_company_services_for_bot(Company.objects.get(id=53))

"""


def get_company_services_for_bot(company):
    services = Service.objects.filter(company=company, is_active=True)

    if not services.exists():
        return "Currently, the company has no service to provide on call. You need to visit us."

    today = timezone.now().date()
    service_lines = []

    for service in services:
        service_name = service.name

        price = service.prices.filter(
            company=company,
            is_active=True
        ).filter(
            Q(effective_from__lte=today) | Q(effective_from__isnull=True),
        ).filter(
            Q(effective_to__gte=today) | Q(effective_to__isnull=True),
        ).order_by("-effective_from").first()

        if price:
            base_price = f"${format_price(price.base_price)}"
            labor_rate = (
                f"${format_price(price.labor_rate_per_hour)}"
                if price.labor_rate_per_hour else None
            )
            estimated_time = format_minutes(price.estimated_hours)

            if labor_rate:

                line = (
                    f"The base price for the {service_name} "
                    f"service starts at {base_price} dollar. "
                    f"Only if the customer asks about the labor rate "
                    f"for {service_name}, tell them: "
                    f"The labor rate for {service_name} is {labor_rate} dollar per hour "
                    f"Estimated time for {service_name} is {estimated_time} "
                )
            else:
                line = f"{service_name} Service starting at {base_price}"
        else:
            line = f"{service_name} Service is available "

        service_lines.append(line)

    services_text = ". ".join(service_lines) + "."

    return f"Our company provides the following services: {services_text}"


def format_minutes(minutes) -> str:
    if not minutes:
        return "0 minutes"

    minutes = int(round(minutes))

    hours, remaining_minutes = divmod(minutes, 60)

    if hours and remaining_minutes:
        return f"{hours} hour{'s' if hours > 1 else ''} {remaining_minutes} minute{'s' if remaining_minutes > 1 else ''}"
    elif hours:
        return f"{hours} hour{'s' if hours > 1 else ''}"
    else:
        return f"{remaining_minutes} minute{'s' if remaining_minutes > 1 else ''}"


def format_price(value):
    if value is None:
        return None

    return int(value) if value == value.to_integral() else value


def get_pac_working_info(phone_number):
    """
    Get AI-friendly routing table with dealerships, departments, and phone numbers for a company
    
    Args:
        phone_number (str): Company phone number
        
    Returns:
        dict: {
            'routing_table': [
                {
                    'dealership_name': str,
                    'dealership_phone': str,
                    'departments': [
                        {
                            'department_name': str,
                            'phone_number': str
                        }
                    ]
                }
            ]
        }
    """
    try:
        company_bot_settings = CompanyBotSettings.objects.get(
            phone_number=phone_number,
            is_active=True
        )
        company = company_bot_settings.company

        company_tz = ZoneInfo(company.timezone)
        current_time_company = timezone.now().astimezone(company_tz)

        current_day = current_time_company.strftime('%A').lower()
        current_time_only = current_time_company.time()

        if current_day == 'sunday':
            start_time = time(9, 0)
            end_time = time(21, 0)
        else:  # Monday to Saturday
            start_time = time(7, 0)
            end_time = time(21, 0)

        is_office_hours =  start_time <= current_time_only <= end_time

        dealerships = Company.objects.filter(
            company_type=CompanyType.DEALERSHIP,
        )

        routing_table = []
        transfer_instructions = ""

        for dealership in dealerships:
            dealership_phone = (
                    dealership.contact_phone
                    if dealership.contact_phone
                    else None
                )
            # dealership_data = {
            #     'dealership_name': dealership.name,
            #     'dealership_phone': dealership_phone,
            # }
            transfer_instructions += f"If the user wants to talk, speak, transfer or reach to {dealership.name} then transfer to {dealership_phone}; "

            # routing_table.append(dealership_data)

        return {
            'is_office_hours': is_office_hours,
            'company_name': company.name,
            'transfer_instructions': transfer_instructions
        }

    except CompanyBotSettings.DoesNotExist:
        return {
            'is_office_hours': True,
            'company_name': 'Dealer Pulse',
            'error': 'Company bot settings not found for the given phone number'
        }
    except Exception as e:
        logger.error(f"Error in get_pac_working_info: {str(e)}")
        return {
            'is_office_hours': True,
            'company_name': 'Dealer Pulse',
            'error': str(e)
        }


def log_company_change(company, user, action, model_name, object_id, details):
    """Helper function to log company changes"""
    return CompanyHistory.objects.create(
        company=company,
        updated_by=user,
        action=action,
        model_name=model_name,
        object_id=object_id,
        details=details
    )


def validate_holidays(holidays):
    """Validate holidays data"""
    invalid_holidays = holidays.filter(
        models.Q(start_date__isnull=True) | models.Q(end_date__isnull=True)
    )
    if invalid_holidays.exists():
        return 'Invalid holidays: some holidays are missing start/end date'

    # Check for invalid date ranges
    for holiday in holidays:
        if holiday.start_date and holiday.end_date and holiday.start_date > holiday.end_date:
            return f'Invalid holiday "{holiday.name}": start date must be before end date'

    return None


def validate_office_hours(office_hours):
    """Validate office hours data"""
    invalid_hours = office_hours.filter(
        is_open=True
    ).filter(
        models.Q(start_time__isnull=True) | models.Q(end_time__isnull=True)
    )
    if invalid_hours.exists():
        return 'Invalid office hours: some open days are missing start/end time'
    return None


def validate_sales_timings(sales_timings):
    """Validate sales timings data"""
    invalid_timings = sales_timings.filter(
        models.Q(start_time__isnull=True) | models.Q(end_time__isnull=True)
    )
    if invalid_timings.exists():
        return 'Invalid sales timings: some timings are missing start/end time'
    return None


def apply_to_all_companies_bulk(request, model_class, model_name, validation_func=None):
    """
    Generic utility to apply current company's data to all user's companies

    Args:
        request: Django request object
        model_class: The model class (OfficeHours, SalesTiming, etc.)
        model_name: String name for logging (e.g., 'OfficeHours', 'SalesTiming')
        validation_func: Optional custom validation function

    Returns:
        Response object
    """
    user = request.user

    if not user.active_company:
        return Response({'error': 'No active company'}, status=status.HTTP_400_BAD_REQUEST)

    # Require explicit confirmation
    if not request.data.get('confirm', False):
        return Response({'error': 'You must confirm to apply to all companies'}, status=status.HTTP_400_BAD_REQUEST)

    # Other companies
    user_companies = user.companies.filter(is_active=True).exclude(id=user.active_company.id)
    if not user_companies.exists():
        return Response({'error': 'No other companies found'}, status=status.HTTP_400_BAD_REQUEST)

    # Permission filtering
    allowed_companies = []
    denied = []
    for company in user_companies:
        if user.has_company_permission('edit_company', company):
            allowed_companies.append(company)
        else:
            denied.append(company.name)

    if not allowed_companies:
        return Response({
            'error': 'No permission to edit any company',
            'denied_companies': denied,
        }, status=status.HTTP_403_FORBIDDEN)

    # Get source data
    current_data = model_class.objects.filter(company=user.active_company)
    if not current_data.exists():
        return Response({'error': f'No {model_name.lower()} found in current company'},
                        status=status.HTTP_400_BAD_REQUEST)

    # Custom validation if provided
    if validation_func:
        validation_error = validation_func(current_data)
        if validation_error:
            return Response({'error': validation_error}, status=status.HTTP_400_BAD_REQUEST)

    try:
        with transaction.atomic():
            # 1. Delete old data for allowed companies
            model_class.objects.filter(company__in=allowed_companies).delete()

            # 2. Prepare bulk create list
            new_records = []
            for company in allowed_companies:
                for item in current_data:
                    # Create new record with company
                    new_record = model_class(
                        company=company,
                        **{field.name: getattr(item, field.name) for field in model_class._meta.fields
                           if field.name != 'id' and field.name != 'company' and not field.auto_created}
                    )
                    new_records.append(new_record)

            # Create records
            created_records = model_class.objects.bulk_create(new_records, batch_size=500)

            # 3. History logs (one per company)
            history_logs = []
            count = current_data.count()
            for company in allowed_companies:
                history_logs.append(CompanyHistory(
                    company=company,
                    updated_by=user,
                    action='bulk_replace',
                    model_name=model_name,
                    object_id=None,  # Null for bulk operations
                    details=f"Replaced {count} {model_name.lower()} from {user.active_company.name} → {company.name}"
                ))

            CompanyHistory.objects.bulk_create(history_logs, batch_size=100)

        # Success response
        response_data = {
            'message': f'{model_name} applied to {len(allowed_companies)} companies',
            'applied_count': len(allowed_companies),
            'total_companies': user_companies.count(),
            'skipped_companies': denied if denied else None,
            'source_company': user.active_company.name,
            'items_count': current_data.count()
        }

        return Response(response_data, status=status.HTTP_200_OK if not denied else status.HTTP_206_PARTIAL_CONTENT)

    except IntegrityError as e:
        error_msg = str(e)
        if 'UNIQUE constraint failed' in error_msg:
            return Response({'error': f'Duplicate {model_name.lower()} found. Please try again.'},
                            status=status.HTTP_400_BAD_REQUEST)
        elif 'FOREIGN KEY constraint failed' in error_msg:
            return Response({'error': 'Invalid company reference'}, status=status.HTTP_400_BAD_REQUEST)
        else:
            return Response({'error': f'Data constraint violation: {error_msg}'}, status=status.HTTP_400_BAD_REQUEST)
    except PermissionError:
        return Response({'error': 'Permission denied'}, status=status.HTTP_403_FORBIDDEN)
    except Exception as e:
        return Response({'error': f'Operation failed: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
