"""
Example: Smart Client Re-engagement Automation

This demonstrates a real-world automation that businesses would love:
Automatically win back customers who haven't booked in a while.
"""

from typing import Any, Dict
from django.utils import timezone
from django.core.mail import send_mail
from django.conf import settings
from django.db.models import Max, Count, Q
from datetime import timedelta
import logging

from .plugins import BasePlugin, register_plugin, PluginExecutionError

logger = logging.getLogger(__name__)


@register_plugin
class ClientReengagementPlugin(BasePlugin):
    """
    Automatically re-engage customers who haven't booked recently.

    This plugin:
    1. Finds customers who haven't booked in X days
    2. Sends personalized re-engagement emails
    3. Offers optional discount codes
    4. Tracks engagement metrics
    """

    name = "client_reengagement"
    display_name = "Client Re-engagement Campaign"
    description = "Automatically reach out to customers who haven't booked recently with personalized offers"
    category = "marketing"

    config_schema = {
        'days_inactive': {
            'type': 'integer',
            'required': False,
            'default': 60,
            'description': 'Target customers who haven\'t booked in this many days (default: 60)',
        },
        'email_subject': {
            'type': 'string',
            'required': False,
            'default': 'We Miss You! Come Back Soon',
            'description': 'Email subject line',
        },
        'email_message': {
            'type': 'text',
            'required': False,
            'default': '''Hi {customer_name},

We noticed it's been a while since your last visit on {last_visit_date}. We'd love to see you again!

As a valued customer, we're offering you {discount}% off your next appointment.

Book now and use code: {promo_code}

Looking forward to seeing you soon!
{business_name}''',
            'description': 'Email message template (variables: customer_name, last_visit_date, business_name, discount, promo_code)',
        },
        'discount_percentage': {
            'type': 'integer',
            'required': False,
            'default': 15,
            'description': 'Discount percentage to offer (default: 15%)',
        },
        'promo_code_prefix': {
            'type': 'string',
            'required': False,
            'default': 'COMEBACK',
            'description': 'Prefix for generated promo codes (default: COMEBACK)',
        },
        'max_customers_per_run': {
            'type': 'integer',
            'required': False,
            'default': 50,
            'description': 'Maximum customers to contact per execution (prevents spam)',
        },
        'exclude_already_contacted': {
            'type': 'boolean',
            'required': False,
            'default': True,
            'description': 'Skip customers who were already contacted by this campaign',
        },
        'minimum_past_visits': {
            'type': 'integer',
            'required': False,
            'default': 1,
            'description': 'Only target customers with at least this many past visits',
        },
    }

    def execute(self, context: Dict[str, Any]) -> Dict[str, Any]:
        """Execute the re-engagement campaign"""

        from .models import Event
        from smoothschedule.users.models import User

        business = context.get('business')
        if not business:
            raise PluginExecutionError("No business context available")

        # Get configuration
        days_inactive = self.config.get('days_inactive', 60)
        max_customers = self.config.get('max_customers_per_run', 50)
        min_visits = self.config.get('minimum_past_visits', 1)

        # Calculate cutoff date
        cutoff_date = timezone.now() - timedelta(days=days_inactive)

        # Find inactive customers
        inactive_customers = self._find_inactive_customers(
            cutoff_date=cutoff_date,
            min_visits=min_visits,
            max_count=max_customers
        )

        if not inactive_customers:
            return {
                'success': True,
                'message': 'No inactive customers found',
                'data': {
                    'customers_contacted': 0,
                    'emails_sent': 0,
                }
            }

        # Send re-engagement emails
        results = {
            'customers_contacted': 0,
            'emails_sent': 0,
            'emails_failed': 0,
            'customers': [],
        }

        for customer_data in inactive_customers:
            try:
                success = self._send_reengagement_email(
                    customer_data=customer_data,
                    business=business
                )

                if success:
                    results['emails_sent'] += 1
                    results['customers_contacted'] += 1
                    results['customers'].append({
                        'email': customer_data['email'],
                        'last_visit': customer_data['last_visit'].isoformat(),
                        'days_since_visit': customer_data['days_since_visit'],
                    })
                else:
                    results['emails_failed'] += 1

            except Exception as e:
                logger.error(f"Failed to send re-engagement email to {customer_data['email']}: {e}")
                results['emails_failed'] += 1

        return {
            'success': True,
            'message': f"Contacted {results['customers_contacted']} inactive customers",
            'data': results
        }

    def _find_inactive_customers(self, cutoff_date, min_visits, max_count):
        """Find customers who haven't booked recently"""
        from .models import Event, Participant
        from smoothschedule.users.models import User
        from django.contrib.contenttypes.models import ContentType

        # Get customer content type
        customer_ct = ContentType.objects.get_for_model(User)

        # Find customers with their last visit date
        # This query finds all customers who participated in events
        customer_participants = Participant.objects.filter(
            role=Participant.Role.CUSTOMER,
            content_type=customer_ct,
        ).values('object_id').annotate(
            last_event_date=Max('event__end_time'),
            total_visits=Count('event', filter=Q(event__status__in=['COMPLETED', 'PAID']))
        ).filter(
            last_event_date__lt=cutoff_date,  # Last visit before cutoff
            total_visits__gte=min_visits,  # Minimum number of visits
        ).order_by('last_event_date')[:max_count]

        # Get customer details
        inactive_customers = []
        for participant_data in customer_participants:
            try:
                customer = User.objects.get(id=participant_data['object_id'])

                # Skip if no email
                if not customer.email:
                    continue

                days_since_visit = (timezone.now() - participant_data['last_event_date']).days

                inactive_customers.append({
                    'id': customer.id,
                    'email': customer.email,
                    'name': customer.get_full_name() or customer.username,
                    'last_visit': participant_data['last_event_date'],
                    'days_since_visit': days_since_visit,
                    'total_visits': participant_data['total_visits'],
                })
            except User.DoesNotExist:
                continue

        return inactive_customers

    def _send_reengagement_email(self, customer_data, business):
        """Send personalized re-engagement email to a customer"""

        # Generate promo code
        promo_prefix = self.config.get('promo_code_prefix', 'COMEBACK')
        promo_code = f"{promo_prefix}{customer_data['id']}"

        # Format email message
        email_template = self.config.get('email_message', self.config_schema['email_message']['default'])

        email_body = email_template.format(
            customer_name=customer_data['name'],
            last_visit_date=customer_data['last_visit'].strftime('%B %d, %Y'),
            business_name=business.name if business else 'Our Business',
            discount=self.config.get('discount_percentage', 15),
            promo_code=promo_code,
        )

        subject = self.config.get('email_subject', 'We Miss You! Come Back Soon')

        try:
            send_mail(
                subject=subject,
                message=email_body,
                from_email=settings.DEFAULT_FROM_EMAIL,
                recipient_list=[customer_data['email']],
                fail_silently=False,
            )

            logger.info(f"Sent re-engagement email to {customer_data['email']} (promo: {promo_code})")
            return True

        except Exception as e:
            logger.error(f"Failed to send email to {customer_data['email']}: {e}")
            return False


@register_plugin
class LowShowRateAlertPlugin(BasePlugin):
    """
    Alert business owners when no-show rate is unusually high.

    Helps businesses identify issues early and take corrective action.
    """

    name = "low_show_rate_alert"
    display_name = "No-Show Rate Alert"
    description = "Alert when customer no-show rate exceeds threshold"
    category = "monitoring"

    config_schema = {
        'threshold_percentage': {
            'type': 'integer',
            'required': False,
            'default': 20,
            'description': 'Alert if no-show rate exceeds this percentage (default: 20%)',
        },
        'lookback_days': {
            'type': 'integer',
            'required': False,
            'default': 7,
            'description': 'Analyze appointments from the last N days (default: 7)',
        },
        'alert_emails': {
            'type': 'list',
            'required': True,
            'description': 'Email addresses to notify',
        },
        'min_appointments': {
            'type': 'integer',
            'required': False,
            'default': 10,
            'description': 'Minimum appointments needed before alerting (avoid false positives)',
        },
    }

    def execute(self, context: Dict[str, Any]) -> Dict[str, Any]:
        """Check no-show rate and alert if too high"""

        from .models import Event

        lookback_days = self.config.get('lookback_days', 7)
        threshold = self.config.get('threshold_percentage', 20)
        min_appointments = self.config.get('min_appointments', 10)

        # Calculate date range
        start_date = timezone.now() - timedelta(days=lookback_days)

        # Get appointment statistics
        total_appointments = Event.objects.filter(
            start_time__gte=start_date,
            start_time__lt=timezone.now(),
        ).count()

        if total_appointments < min_appointments:
            return {
                'success': True,
                'message': f'Not enough data ({total_appointments} appointments, need {min_appointments})',
                'data': {'appointments': total_appointments}
            }

        canceled_count = Event.objects.filter(
            start_time__gte=start_date,
            start_time__lt=timezone.now(),
            status='CANCELED',
        ).count()

        no_show_rate = (canceled_count / total_appointments) * 100

        if no_show_rate >= threshold:
            # Send alert
            business = context.get('business')
            self._send_alert(
                business=business,
                no_show_rate=no_show_rate,
                canceled_count=canceled_count,
                total_appointments=total_appointments,
                lookback_days=lookback_days
            )

            return {
                'success': True,
                'message': f'ALERT: No-show rate is {no_show_rate:.1f}% (threshold: {threshold}%)',
                'data': {
                    'no_show_rate': round(no_show_rate, 2),
                    'canceled': canceled_count,
                    'total': total_appointments,
                    'alert_sent': True,
                }
            }
        else:
            return {
                'success': True,
                'message': f'No-show rate is healthy: {no_show_rate:.1f}%',
                'data': {
                    'no_show_rate': round(no_show_rate, 2),
                    'canceled': canceled_count,
                    'total': total_appointments,
                    'alert_sent': False,
                }
            }

    def _send_alert(self, business, no_show_rate, canceled_count, total_appointments, lookback_days):
        """Send alert email to business owners"""

        alert_emails = self.config.get('alert_emails', [])

        subject = f"⚠️ High No-Show Rate Alert - {business.name if business else 'Your Business'}"

        message = f"""
Alert: Your no-show rate is unusually high!

No-Show Rate: {no_show_rate:.1f}%
Period: Last {lookback_days} days
Canceled Appointments: {canceled_count} out of {total_appointments}

Recommended Actions:
1. Review your confirmation process
2. Send appointment reminders 24 hours before
3. Implement a cancellation policy
4. Follow up with customers who no-showed

This is an automated alert from your scheduling system.
"""

        try:
            send_mail(
                subject=subject,
                message=message,
                from_email=settings.DEFAULT_FROM_EMAIL,
                recipient_list=alert_emails,
                fail_silently=False,
            )
            logger.info(f"Sent no-show alert to {len(alert_emails)} recipient(s)")
        except Exception as e:
            logger.error(f"Failed to send no-show alert: {e}")


@register_plugin
class PeakHoursAnalyzerPlugin(BasePlugin):
    """
    Analyze booking patterns to identify peak hours and make staffing recommendations.
    """

    name = "peak_hours_analyzer"
    display_name = "Peak Hours Analyzer"
    description = "Analyze booking patterns and recommend optimal staffing"
    category = "analytics"

    config_schema = {
        'analysis_days': {
            'type': 'integer',
            'required': False,
            'default': 30,
            'description': 'Analyze data from the last N days (default: 30)',
        },
        'report_emails': {
            'type': 'list',
            'required': True,
            'description': 'Email addresses to receive the analysis report',
        },
        'group_by': {
            'type': 'choice',
            'choices': ['hour', 'day_of_week', 'both'],
            'required': False,
            'default': 'both',
            'description': 'How to group the analysis',
        },
    }

    def execute(self, context: Dict[str, Any]) -> Dict[str, Any]:
        """Analyze booking patterns and send report"""

        from .models import Event
        from collections import defaultdict

        analysis_days = self.config.get('analysis_days', 30)
        start_date = timezone.now() - timedelta(days=analysis_days)

        # Get all appointments in the period
        events = Event.objects.filter(
            start_time__gte=start_date,
            status__in=['COMPLETED', 'PAID', 'SCHEDULED']
        ).values_list('start_time', flat=True)

        # Analyze by hour
        hourly_counts = defaultdict(int)
        weekday_counts = defaultdict(int)

        for event_time in events:
            hour = event_time.hour
            weekday = event_time.strftime('%A')

            hourly_counts[hour] += 1
            weekday_counts[weekday] += 1

        # Find peak hours
        peak_hours = sorted(hourly_counts.items(), key=lambda x: x[1], reverse=True)[:5]
        peak_days = sorted(weekday_counts.items(), key=lambda x: x[1], reverse=True)[:3]

        # Generate report
        report = self._generate_report(
            peak_hours=peak_hours,
            peak_days=peak_days,
            total_appointments=len(events),
            analysis_days=analysis_days,
            business=context.get('business')
        )

        # Send report
        self._send_report(report)

        return {
            'success': True,
            'message': 'Peak hours analysis completed',
            'data': {
                'peak_hours': [f"{h}:00" for h, _ in peak_hours],
                'peak_days': [d for d, _ in peak_days],
                'total_analyzed': len(events),
            }
        }

    def _generate_report(self, peak_hours, peak_days, total_appointments, analysis_days, business):
        """Generate human-readable report"""

        report = f"""
Peak Hours Analysis Report
Business: {business.name if business else 'Your Business'}
Period: Last {analysis_days} days
Total Appointments: {total_appointments}

TOP 5 BUSIEST HOURS:
"""
        for hour, count in peak_hours:
            percentage = (count / total_appointments * 100) if total_appointments > 0 else 0
            time_label = f"{hour}:00 - {hour+1}:00"
            report += f"  {time_label}: {count} appointments ({percentage:.1f}%)\n"

        report += f"\nTOP 3 BUSIEST DAYS:\n"
        for day, count in peak_days:
            percentage = (count / total_appointments * 100) if total_appointments > 0 else 0
            report += f"  {day}: {count} appointments ({percentage:.1f}%)\n"

        report += f"""

RECOMMENDATIONS:
• Ensure adequate staffing during peak hours
• Consider offering promotions during slower periods
• Use this data to optimize your schedule
• Review this analysis monthly to spot trends

This is an automated report from your scheduling system.
"""
        return report

    def _send_report(self, report):
        """Send the analysis report via email"""

        recipients = self.config.get('report_emails', [])

        try:
            send_mail(
                subject="📊 Peak Hours Analysis Report",
                message=report,
                from_email=settings.DEFAULT_FROM_EMAIL,
                recipient_list=recipients,
                fail_silently=False,
            )
            logger.info(f"Sent peak hours report to {len(recipients)} recipient(s)")
        except Exception as e:
            logger.error(f"Failed to send peak hours report: {e}")
