from fastapi import APIRouter, Depends, HTTPException, status, Query, Request
from sqlalchemy.orm import Session, selectinload
from sqlalchemy import func
from typing import Optional, List, Union
from datetime import datetime
from pydantic import BaseModel, EmailStr, field_validator

from ...shared.config.database import get_db
from ...security.middleware.auth import get_current_user, authorize_roles
from ...auth.models.user import User
from ..models.email_campaign import (
    Campaign, CampaignStatus, CampaignType,
    CampaignSegment, EmailTemplate, CampaignEmail, EmailStatus,
    DripSequence, DripSequenceStep, Unsubscribe, NewsletterSubscriber
)
from ..services.email_campaign_service import email_campaign_service

router = APIRouter(prefix="/email-campaigns", tags=["Email Campaigns"])

# Pydantic Models
class CampaignCreate(BaseModel):
    name: str
    subject: str
    html_content: str
    text_content: Optional[str] = None
    campaign_type: str = "newsletter"
    segment_id: Optional[Union[int, str]] = None
    scheduled_at: Optional[datetime] = None
    template_id: Optional[Union[int, str]] = None
    from_name: Optional[str] = None
    from_email: Optional[str] = None
    reply_to_email: Optional[str] = None
    track_opens: bool = True
    track_clicks: bool = True
    recipient_type: Optional[str] = 'users'  # 'users', 'subscribers', 'both'
    
    @field_validator('segment_id', 'template_id', mode='before')
    @classmethod
    def parse_int_or_none(cls, v):
        if v is None or v == '' or v == 'undefined' or (isinstance(v, str) and v.strip() == ''):
            return None
        if isinstance(v, str):
            try:
                return int(v)
            except (ValueError, TypeError):
                return None
        if isinstance(v, int):
            return v
        return None

class CampaignUpdate(BaseModel):
    name: Optional[str] = None
    subject: Optional[str] = None
    html_content: Optional[str] = None
    text_content: Optional[str] = None
    segment_id: Optional[Union[int, str]] = None
    scheduled_at: Optional[datetime] = None
    status: Optional[str] = None
    
    @field_validator('segment_id', mode='before')
    @classmethod
    def parse_int_or_none(cls, v):
        if v is None or v == '' or v == 'undefined' or (isinstance(v, str) and v.strip() == ''):
            return None
        if isinstance(v, str):
            try:
                return int(v)
            except (ValueError, TypeError):
                return None
        if isinstance(v, int):
            return v
        return None

class SegmentCreate(BaseModel):
    name: str
    description: Optional[str] = None
    criteria: dict

class TemplateCreate(BaseModel):
    name: str
    subject: str
    html_content: str
    text_content: Optional[str] = None
    category: Optional[str] = None
    variables: Optional[List[str]] = None

class DripSequenceCreate(BaseModel):
    name: str
    description: Optional[str] = None
    trigger_event: Optional[str] = None

class DripStepCreate(BaseModel):
    subject: str
    html_content: str
    text_content: Optional[str] = None
    delay_days: int = 0
    delay_hours: int = 0
    template_id: Optional[Union[int, str]] = None
    
    @field_validator('template_id', mode='before')
    @classmethod
    def parse_int_or_none(cls, v):
        if v is None or v == '' or v == 'undefined' or (isinstance(v, str) and v.strip() == ''):
            return None
        if isinstance(v, str):
            try:
                return int(v)
            except (ValueError, TypeError):
                return None
        if isinstance(v, int):
            return v
        return None

# Campaign Routes
@router.get("")
async def get_campaigns(
    status_filter: Optional[str] = Query(None, alias='status'),
    campaign_type: Optional[str] = Query(None),
    limit: int = Query(50, ge=1, le=100),
    offset: int = Query(0, ge=0),
    current_user: User = Depends(authorize_roles("admin")),
    db: Session = Depends(get_db)
):
    """Get all email campaigns"""
    query = db.query(Campaign)
    
    if status_filter:
        try:
            status_enum = CampaignStatus(status_filter)
            query = query.filter(Campaign.status == status_enum)
        except ValueError:
            pass
    
    if campaign_type:
        try:
            type_enum = CampaignType(campaign_type)
            query = query.filter(Campaign.campaign_type == type_enum)
        except ValueError:
            pass
    
    campaigns = query.order_by(Campaign.created_at.desc()).offset(offset).limit(limit).all()
    
    return [{
        "id": c.id,
        "name": c.name,
        "subject": c.subject,
        "campaign_type": c.campaign_type.value,
        "status": c.status.value,
        "total_recipients": c.total_recipients,
        "total_sent": c.total_sent,
        "total_opened": c.total_opened,
        "total_clicked": c.total_clicked,
        "open_rate": float(c.open_rate) if c.open_rate else None,
        "click_rate": float(c.click_rate) if c.click_rate else None,
        "scheduled_at": c.scheduled_at.isoformat() if c.scheduled_at else None,
        "sent_at": c.sent_at.isoformat() if c.sent_at else None,
        "created_at": c.created_at.isoformat() if c.created_at else None
    } for c in campaigns]

# Segment Routes (must be before /{campaign_id} to avoid route conflicts)
@router.get("/segments")
async def get_segments(
    current_user: User = Depends(authorize_roles("admin")),
    db: Session = Depends(get_db)
):
    """Get all campaign segments"""
    try:
        segments = db.query(CampaignSegment).filter(CampaignSegment.is_active == True).all()
        return [{
            "id": s.id,
            "name": s.name,
            "description": s.description,
            "criteria": s.criteria,
            "estimated_count": s.estimated_count,
            "created_at": s.created_at.isoformat() if s.created_at else None
        } for s in segments]
    except HTTPException:
        raise
    except Exception as e:
        from ...shared.config.logging_config import get_logger
        logger = get_logger(__name__)
        logger.error(f"Error fetching segments: {str(e)}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"Failed to fetch segments: {str(e)}")

@router.post("/segments")
async def create_segment(
    data: SegmentCreate,
    current_user: User = Depends(authorize_roles("admin")),
    db: Session = Depends(get_db)
):
    """Create a new campaign segment"""
    try:
        segment = email_campaign_service.create_segment(
            db=db,
            name=data.name,
            criteria=data.criteria,
            description=data.description,
            created_by=current_user.id
        )
        return {"status": "success", "segment_id": segment.id, "estimated_count": segment.estimated_count}
    except HTTPException:
        raise
    except Exception as e:
        from ...shared.config.logging_config import get_logger
        logger = get_logger(__name__)
        logger.error(f"Error creating segment: {str(e)}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"Failed to create segment: {str(e)}")

# Template Routes (must be before /{campaign_id} to avoid route conflicts)
@router.get("/templates")
async def get_templates(
    category: Optional[str] = Query(None, description="Filter by template category"),
    current_user: User = Depends(authorize_roles("admin")),
    db: Session = Depends(get_db)
):
    """Get all email templates"""
    try:
        query = db.query(EmailTemplate).filter(EmailTemplate.is_active == True)
        if category:
            query = query.filter(EmailTemplate.category == category)
        
        templates = query.all()
        result = [{
            "id": t.id,
            "name": t.name,
            "subject": t.subject,
            "category": t.category,
            "variables": t.variables,
            "created_at": t.created_at.isoformat() if t.created_at else None
        } for t in templates]
        return result
    except HTTPException:
        raise
    except Exception as e:
        from ...shared.config.logging_config import get_logger
        logger = get_logger(__name__)
        logger.error(f"Error fetching templates: {str(e)}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"Failed to fetch templates: {str(e)}")

@router.post("/templates")
async def create_template(
    data: TemplateCreate,
    current_user: User = Depends(authorize_roles("admin")),
    db: Session = Depends(get_db)
):
    """Create a new email template"""
    try:
        template = EmailTemplate(
            name=data.name,
            subject=data.subject,
            html_content=data.html_content,
            text_content=data.text_content,
            category=data.category,
            variables=data.variables,
            created_by=current_user.id
        )
        db.add(template)
        db.commit()
        db.refresh(template)
        return {"status": "success", "template_id": template.id}
    except HTTPException:
        raise
    except Exception as e:
        from ...shared.config.logging_config import get_logger
        logger = get_logger(__name__)
        logger.error(f"Error creating template: {str(e)}", exc_info=True)
        db.rollback()
        raise HTTPException(status_code=500, detail=f"Failed to create template: {str(e)}")

# Drip Sequence Routes (must be before /{campaign_id} to avoid route conflicts)
@router.get("/drip-sequences")
async def get_drip_sequences(
    current_user: User = Depends(authorize_roles("admin")),
    db: Session = Depends(get_db)
):
    """Get all drip sequences"""
    try:
        # Use eager loading to avoid lazy loading issues
        sequences = db.query(DripSequence).options(
            selectinload(DripSequence.steps)
        ).filter(DripSequence.is_active == True).all()
        
        return [{
            "id": s.id,
            "name": s.name,
            "description": s.description,
            "trigger_event": s.trigger_event,
            "step_count": len(s.steps) if s.steps else 0,
            "created_at": s.created_at.isoformat() if s.created_at else None
        } for s in sequences]
    except HTTPException:
        raise
    except Exception as e:
        from ...shared.config.logging_config import get_logger
        logger = get_logger(__name__)
        logger.error(f"Error fetching drip sequences: {str(e)}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"Failed to fetch drip sequences: {str(e)}")

@router.post("/drip-sequences")
async def create_drip_sequence(
    data: DripSequenceCreate,
    current_user: User = Depends(authorize_roles("admin")),
    db: Session = Depends(get_db)
):
    """Create a new drip sequence"""
    try:
        sequence = email_campaign_service.create_drip_sequence(
            db=db,
            name=data.name,
            description=data.description,
            trigger_event=data.trigger_event,
            created_by=current_user.id
        )
        return {"status": "success", "sequence_id": sequence.id}
    except HTTPException:
        raise
    except Exception as e:
        from ...shared.config.logging_config import get_logger
        logger = get_logger(__name__)
        logger.error(f"Error creating drip sequence: {str(e)}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"Failed to create drip sequence: {str(e)}")

@router.post("/drip-sequences/{sequence_id}/steps")
async def add_drip_step(
    sequence_id: int,
    data: DripStepCreate,
    current_user: User = Depends(authorize_roles("admin")),
    db: Session = Depends(get_db)
):
    """Add a step to a drip sequence"""
    try:
        # Ensure template_id is integer or None
        template_id = int(data.template_id) if data.template_id is not None else None
        
        step = email_campaign_service.add_drip_step(
            db=db,
            sequence_id=sequence_id,
            subject=data.subject,
            html_content=data.html_content,
            text_content=data.text_content,
            delay_days=data.delay_days,
            delay_hours=data.delay_hours,
            template_id=template_id
        )
        return {"status": "success", "step_id": step.id}
    except HTTPException:
        raise
    except Exception as e:
        from ...shared.config.logging_config import get_logger
        logger = get_logger(__name__)
        logger.error(f"Error adding drip step: {str(e)}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"Failed to add drip step: {str(e)}")

@router.get("/{campaign_id}")
async def get_campaign(
    campaign_id: int,
    current_user: User = Depends(authorize_roles("admin")),
    db: Session = Depends(get_db)
):
    """Get a specific campaign"""
    campaign = db.query(Campaign).filter(Campaign.id == campaign_id).first()
    if not campaign:
        raise HTTPException(status_code=404, detail="Campaign not found")
    
    return {
        "id": campaign.id,
        "name": campaign.name,
        "subject": campaign.subject,
        "html_content": campaign.html_content,
        "text_content": campaign.text_content,
        "campaign_type": campaign.campaign_type.value,
        "status": campaign.status.value,
        "segment_id": campaign.segment_id,
        "scheduled_at": campaign.scheduled_at.isoformat() if campaign.scheduled_at else None,
        "total_recipients": campaign.total_recipients,
        "total_sent": campaign.total_sent,
        "total_delivered": campaign.total_delivered,
        "total_opened": campaign.total_opened,
        "total_clicked": campaign.total_clicked,
        "total_bounced": campaign.total_bounced,
        "open_rate": float(campaign.open_rate) if campaign.open_rate else None,
        "click_rate": float(campaign.click_rate) if campaign.click_rate else None,
        "created_at": campaign.created_at.isoformat() if campaign.created_at else None
    }

@router.post("")
async def create_campaign(
    data: CampaignCreate,
    current_user: User = Depends(authorize_roles("admin")),
    db: Session = Depends(get_db)
):
    """Create a new email campaign"""
    try:
        campaign_type = CampaignType(data.campaign_type)
    except ValueError:
        raise HTTPException(status_code=400, detail="Invalid campaign type")
    
    campaign = email_campaign_service.create_campaign(
        db=db,
        name=data.name,
        subject=data.subject,
        html_content=data.html_content,
        text_content=data.text_content,
        campaign_type=campaign_type,
        segment_id=data.segment_id,
        scheduled_at=data.scheduled_at,
        template_id=data.template_id,
        created_by=current_user.id,
        from_name=data.from_name,
        from_email=data.from_email,
        reply_to_email=data.reply_to_email,
        track_opens=data.track_opens,
        track_clicks=data.track_clicks,
        recipient_type=data.recipient_type or 'users'
    )
    
    return {"status": "success", "campaign_id": campaign.id}

@router.put("/{campaign_id}")
async def update_campaign(
    campaign_id: int,
    data: CampaignUpdate,
    current_user: User = Depends(authorize_roles("admin")),
    db: Session = Depends(get_db)
):
    """Update a campaign"""
    campaign = db.query(Campaign).filter(Campaign.id == campaign_id).first()
    if not campaign:
        raise HTTPException(status_code=404, detail="Campaign not found")
    
    if data.name:
        campaign.name = data.name
    if data.subject:
        campaign.subject = data.subject
    if data.html_content:
        campaign.html_content = data.html_content
    if data.text_content is not None:
        campaign.text_content = data.text_content
    if data.segment_id is not None:
        campaign.segment_id = int(data.segment_id) if isinstance(data.segment_id, str) else data.segment_id
    if data.scheduled_at is not None:
        campaign.scheduled_at = data.scheduled_at
    if data.status:
        try:
            campaign.status = CampaignStatus(data.status)
        except ValueError:
            raise HTTPException(status_code=400, detail="Invalid status")
    
    db.commit()
    db.refresh(campaign)
    
    return {"status": "success", "message": "Campaign updated"}

@router.post("/{campaign_id}/send")
async def send_campaign(
    campaign_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(authorize_roles("admin"))
):
    """Send an email campaign"""
    try:
        result = email_campaign_service.send_campaign(db=db, campaign_id=campaign_id)
        return {"status": "success", "result": result}
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Failed to send campaign: {str(e)}")

@router.get("/{campaign_id}/analytics")
async def get_campaign_analytics(
    campaign_id: int,
    current_user: User = Depends(authorize_roles("admin")),
    db: Session = Depends(get_db)
):
    """Get campaign analytics"""
    campaign = db.query(Campaign).filter(Campaign.id == campaign_id).first()
    if not campaign:
        raise HTTPException(status_code=404, detail="Campaign not found")
    
    # Get email status breakdown
    emails = db.query(CampaignEmail).filter(CampaignEmail.campaign_id == campaign_id).all()
    
    status_breakdown = {}
    for status in EmailStatus:
        status_breakdown[status.value] = len([e for e in emails if e.status == status])
    
    return {
        "campaign_id": campaign.id,
        "total_recipients": campaign.total_recipients,
        "total_sent": campaign.total_sent,
        "total_delivered": campaign.total_delivered,
        "total_opened": campaign.total_opened,
        "total_clicked": campaign.total_clicked,
        "total_bounced": campaign.total_bounced,
        "total_unsubscribed": campaign.total_unsubscribed,
        "open_rate": float(campaign.open_rate) if campaign.open_rate else 0,
        "click_rate": float(campaign.click_rate) if campaign.click_rate else 0,
        "bounce_rate": float(campaign.bounce_rate) if campaign.bounce_rate else 0,
        "status_breakdown": status_breakdown
    }

# Tracking Routes (public endpoints for email tracking)
@router.get("/track/open/{campaign_email_id}")
async def track_email_open(
    campaign_email_id: int,
    db: Session = Depends(get_db)
):
    """Track email open (called by tracking pixel)"""
    email_campaign_service.track_email_open(db=db, campaign_email_id=campaign_email_id)
    # Return 1x1 transparent pixel (GIF)
    from fastapi.responses import Response
    # 1x1 transparent GIF
    pixel = b'\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\xff\xff\xff\x00\x00\x00\x21\xf9\x04\x01\x00\x00\x00\x00\x2c\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x04\x01\x00\x3b'
    return Response(content=pixel, media_type="image/gif")

@router.get("/track/click/{campaign_email_id}")
async def track_email_click(
    campaign_email_id: int,
    url: str = Query(...),
    request: Request = None,
    db: Session = Depends(get_db)
):
    """Track email click"""
    ip_address = request.client.host if request and request.client else None
    user_agent = request.headers.get("User-Agent") if request else None
    
    email_campaign_service.track_email_click(
        db=db,
        campaign_email_id=campaign_email_id,
        url=url,
        ip_address=ip_address,
        user_agent=user_agent
    )
    
    # Redirect to the actual URL
    from fastapi.responses import RedirectResponse
    return RedirectResponse(url=url)

# Newsletter Subscription Routes
class NewsletterSubscribeRequest(BaseModel):
    email: EmailStr
    name: Optional[str] = None

@router.post("/newsletter/subscribe")
async def subscribe_to_newsletter(
    request: NewsletterSubscribeRequest,
    db: Session = Depends(get_db)
):
    """Subscribe to newsletter"""
    email = request.email
    name = request.name
    
    # Check if already unsubscribed
    existing_unsubscribe = db.query(Unsubscribe).filter(
        Unsubscribe.email == email,
        Unsubscribe.unsubscribe_all == True
    ).first()
    
    if existing_unsubscribe:
        # Remove unsubscribe record to re-subscribe
        db.delete(existing_unsubscribe)
        db.commit()
    
    # Check if user exists
    user = db.query(User).filter(User.email == email).first()
    
    # Check if already subscribed
    existing_subscriber = db.query(NewsletterSubscriber).filter(
        NewsletterSubscriber.email == email
    ).first()
    
    if existing_subscriber:
        # Re-activate if was deactivated
        if not existing_subscriber.is_active:
            existing_subscriber.is_active = True
            existing_subscriber.subscribed_at = datetime.utcnow()
        if name and not existing_subscriber.name:
            existing_subscriber.name = name
        if user and not existing_subscriber.user_id:
            existing_subscriber.user_id = user.id
        db.commit()
    else:
        # Create new subscriber record
        subscriber = NewsletterSubscriber(
            email=email,
            user_id=user.id if user else None,
            name=name or (user.full_name if user else None),
            is_active=True
        )
        db.add(subscriber)
        db.commit()
    
    # Find or create newsletter segment
    newsletter_segment = db.query(CampaignSegment).filter(
        CampaignSegment.name == "Newsletter Subscribers"
    ).first()
    
    if not newsletter_segment:
        # Create newsletter segment if it doesn't exist
        newsletter_segment = CampaignSegment(
            name="Newsletter Subscribers",
            description="Users who have subscribed to the newsletter",
            criteria={"subscribed": True}
        )
        db.add(newsletter_segment)
        db.commit()
        db.refresh(newsletter_segment)
    
    # Update segment estimated count - count active subscribers
    subscriber_count = db.query(func.count(NewsletterSubscriber.id)).filter(
        NewsletterSubscriber.is_active == True
    ).scalar() or 0
    
    newsletter_segment.estimated_count = subscriber_count
    newsletter_segment.last_calculated_at = datetime.utcnow()
    db.commit()
    
    return {
        "status": "success",
        "message": "Successfully subscribed to newsletter",
        "email": email
    }

@router.get("/newsletter/subscribers")
async def get_newsletter_subscribers(
    current_user: User = Depends(authorize_roles("admin")),
    db: Session = Depends(get_db),
    page: int = Query(1, ge=1),
    limit: int = Query(50, ge=1, le=100)
):
    """Get list of newsletter subscribers"""
    try:
        # Get all active newsletter subscribers
        subscribers_query = db.query(NewsletterSubscriber).filter(
            NewsletterSubscriber.is_active == True
        ).order_by(NewsletterSubscriber.subscribed_at.desc())
        
        # Get total count
        total = subscribers_query.count()
        
        # Pagination
        offset = (page - 1) * limit
        paginated_subscribers = subscribers_query.offset(offset).limit(limit).all()
        
        # Format subscribers
        subscribers_list = []
        for subscriber in paginated_subscribers:
            subscribers_list.append({
                "email": subscriber.email,
                "user_id": subscriber.user_id,
                "name": subscriber.name or (subscriber.user.full_name if subscriber.user else None),
                "type": "user" if subscriber.user_id else "guest",
                "subscribed_at": subscriber.subscribed_at.isoformat() if subscriber.subscribed_at else None
            })
        
        return {
            "status": "success",
            "data": {
                "subscribers": subscribers_list,
                "total": total,
                "page": page,
                "limit": limit,
                "total_pages": (total + limit - 1) // limit if limit > 0 else 1
            }
        }
    except Exception as e:
        from ...shared.config.logging_config import get_logger
        logger = get_logger(__name__)
        logger.error(f"Error fetching newsletter subscribers: {str(e)}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"Failed to fetch subscribers: {str(e)}")

# Unsubscribe Routes
@router.post("/unsubscribe")
async def unsubscribe(
    email: EmailStr = Query(...),
    campaign_id: Optional[Union[int, str]] = Query(None),
    unsubscribe_all: bool = Query(False),
    reason: Optional[str] = None,
    db: Session = Depends(get_db)
):
    """Unsubscribe from email campaigns"""
    # Parse campaign_id if it's a string
    parsed_campaign_id = None
    if campaign_id is not None and campaign_id != '' and campaign_id != 'undefined':
        try:
            parsed_campaign_id = int(campaign_id) if isinstance(campaign_id, str) else campaign_id
        except (ValueError, TypeError):
            parsed_campaign_id = None
    
    user = db.query(User).filter(User.email == email).first()
    
    # Mark newsletter subscriber as inactive if unsubscribing from all
    if unsubscribe_all:
        subscriber = db.query(NewsletterSubscriber).filter(
            NewsletterSubscriber.email == email
        ).first()
        if subscriber:
            subscriber.is_active = False
            # Update segment count
            newsletter_segment = db.query(CampaignSegment).filter(
                CampaignSegment.name == "Newsletter Subscribers"
            ).first()
            if newsletter_segment:
                subscriber_count = db.query(func.count(NewsletterSubscriber.id)).filter(
                    NewsletterSubscriber.is_active == True
                ).scalar() or 0
                newsletter_segment.estimated_count = subscriber_count
                newsletter_segment.last_calculated_at = datetime.utcnow()
    
    unsubscribe_record = Unsubscribe(
        email=email,
        user_id=user.id if user else None,
        campaign_id=parsed_campaign_id,
        unsubscribe_all=unsubscribe_all,
        reason=reason
    )
    db.add(unsubscribe_record)
    db.commit()
    
    return {"status": "success", "message": "Successfully unsubscribed"}

@router.post("/drip-sequences/process")
async def process_drip_sequences(
    current_user: User = Depends(authorize_roles("admin")),
    db: Session = Depends(get_db)
):
    """Manually trigger drip sequence processing"""
    try:
        email_campaign_service.process_drip_sequences(db=db)
        return {"status": "success", "message": "Drip sequences processed"}
    except HTTPException:
        raise
    except Exception as e:
        from ...shared.config.logging_config import get_logger
        logger = get_logger(__name__)
        logger.error(f"Error processing drip sequences: {str(e)}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"Failed to process drip sequences: {str(e)}")

