"""
Email Template Tag System

Defines the allowlist of template tags and validation logic.
Tags use {{ tag_name }} syntax in subject and body content.
"""
import re
from typing import List, Dict, Set, Tuple
from .email_types import EmailType


# =============================================================================
# Tag Definitions
# =============================================================================

# Base tags available for ALL email types
BASE_TAGS: Dict[str, str] = {
    # Business info
    'business_name': 'Business name',
    'business_email': 'Business email address',
    'business_phone': 'Business phone number',
    'business_address': 'Business address',
    'business_logo_url': 'URL to business logo image',
    'business_website_url': 'Business website URL',

    # Customer info
    'customer_name': 'Customer full name',
    'customer_first_name': 'Customer first name',
    'customer_last_name': 'Customer last name',
    'customer_email': 'Customer email address',
    'customer_phone': 'Customer phone number',

    # Date/time
    'current_date': 'Current date (formatted)',
    'current_year': 'Current year',
}

# Appointment-related tags
APPOINTMENT_TAGS: Dict[str, str] = {
    'appointment_date': 'Appointment date (e.g., December 15, 2024)',
    'appointment_time': 'Appointment time (e.g., 2:00 PM)',
    'appointment_datetime': 'Full date and time',
    'appointment_duration': 'Duration (e.g., 1 hour)',
    'service_name': 'Name of the booked service',
    'service_description': 'Service description',
    'staff_name': 'Assigned staff member name',
    'location_name': 'Location/venue name',
    'location_address': 'Location address',

    # Action links
    'manage_appointment_link': 'Link to manage appointment',
    'reschedule_link': 'Link to reschedule',
    'cancel_link': 'Link to cancel',
    'add_to_calendar_link': 'Add to calendar link (ICS)',
}

# Contract-related tags
CONTRACT_TAGS: Dict[str, str] = {
    'contract_title': 'Contract title/name',
    'contract_description': 'Contract description',
    'signing_link': 'Link to sign the contract',
    'contract_expires_at': 'Contract expiration date',
    'days_until_expiry': 'Days until contract expires',
    'signed_at': 'Date/time contract was signed',
}

# Payment-related tags
PAYMENT_TAGS: Dict[str, str] = {
    'amount_due': 'Amount due (formatted with currency)',
    'amount_paid': 'Amount paid (formatted with currency)',
    'currency': 'Currency code (e.g., USD)',
    'invoice_number': 'Invoice number/ID',
    'receipt_link': 'Link to view receipt',
    'payment_link': 'Link to make payment',
    'payment_due_date': 'Payment due date',
}

# Ticket/support-related tags
TICKET_TAGS: Dict[str, str] = {
    'ticket_id': 'Ticket ID/number',
    'ticket_subject': 'Ticket subject line',
    'ticket_status': 'Current ticket status',
    'ticket_priority': 'Ticket priority level',
    'ticket_message': 'Original ticket message',
    'reply_message': 'Reply content',
    'ticket_link': 'Link to view ticket',
    'assignee_name': 'Name of assigned staff',
}


# =============================================================================
# Tag Mapping by Email Type
# =============================================================================

def get_tags_for_category(category: str) -> Dict[str, str]:
    """Get additional tags for a category."""
    category_tags = {
        'appointment': APPOINTMENT_TAGS,
        'contract': CONTRACT_TAGS,
        'payment': PAYMENT_TAGS,
        'ticket': TICKET_TAGS,
        'welcome': {},  # Only base tags
    }
    return category_tags.get(category, {})


def get_allowed_tags_for_email_type(email_type: EmailType) -> Dict[str, str]:
    """
    Get all allowed tags for an email type.

    Returns:
        Dictionary of tag_name -> description
    """
    category = EmailType.get_category(email_type)
    category_tags = get_tags_for_category(category)

    # Combine base tags with category-specific tags
    allowed = {**BASE_TAGS, **category_tags}

    # Some email types need additional tags from other categories
    if email_type == EmailType.APPOINTMENT_CONFIRMATION:
        # Confirmation might include payment info if deposits are used
        allowed['deposit_amount'] = 'Deposit amount if applicable'

    if email_type == EmailType.THANK_YOU:
        # Follow-up might include payment receipt info
        allowed.update({
            'total_paid': 'Total amount paid',
            'review_link': 'Link to leave a review',
        })

    return allowed


def get_allowed_tag_names(email_type: EmailType) -> Set[str]:
    """Get just the tag names (without descriptions)."""
    return set(get_allowed_tags_for_email_type(email_type).keys())


# =============================================================================
# Tag Extraction and Validation
# =============================================================================

# Regex to find {{ tag_name }} patterns
TAG_PATTERN = re.compile(r'\{\{\s*(\w+)\s*\}\}')


def extract_tags(text: str) -> Set[str]:
    """
    Extract all tag names from a text string.

    Args:
        text: Text containing {{ tag_name }} patterns

    Returns:
        Set of tag names found
    """
    if not text:
        return set()
    return set(TAG_PATTERN.findall(text))


def extract_tags_from_puck_data(puck_data: dict) -> Set[str]:
    """
    Extract all tags from Puck component data.

    Searches through all text content in components.

    Args:
        puck_data: Puck data structure with 'content' array

    Returns:
        Set of tag names found
    """
    tags = set()

    def extract_from_value(value):
        """Recursively extract tags from any value."""
        if isinstance(value, str):
            tags.update(extract_tags(value))
        elif isinstance(value, dict):
            for v in value.values():
                extract_from_value(v)
        elif isinstance(value, list):
            for item in value:
                extract_from_value(item)

    extract_from_value(puck_data)
    return tags


def validate_tags(
    subject: str,
    puck_data_or_body: str | dict,
    email_type: EmailType
) -> List[str]:
    """
    Validate that only allowed tags are used.

    Args:
        subject: Subject line template
        puck_data_or_body: Either Puck data dict or plain text body
        email_type: The email type being validated

    Returns:
        List of error messages (empty if valid)
    """
    errors = []

    # Get allowed tags
    allowed_tags = get_allowed_tag_names(email_type)

    # Extract tags from subject
    subject_tags = extract_tags(subject)

    # Extract tags from body
    if isinstance(puck_data_or_body, dict):
        body_tags = extract_tags_from_puck_data(puck_data_or_body)
    else:
        body_tags = extract_tags(puck_data_or_body)

    # Find invalid tags
    all_used_tags = subject_tags | body_tags
    invalid_tags = all_used_tags - allowed_tags

    for tag in invalid_tags:
        errors.append(
            f"Invalid tag '{{{{ {tag} }}}}' is not allowed for {email_type.value} emails. "
            f"Allowed tags: {', '.join(sorted(allowed_tags))}"
        )

    return errors


def get_tag_info_for_email_type(email_type: EmailType) -> List[Dict[str, str]]:
    """
    Get tag information for UI display.

    Returns list of dicts with 'name', 'description', and 'category'.
    """
    result = []
    allowed = get_allowed_tags_for_email_type(email_type)

    # Categorize tags
    for tag, description in sorted(allowed.items()):
        if tag in BASE_TAGS:
            category = 'Business & Customer'
        elif tag in APPOINTMENT_TAGS:
            category = 'Appointment'
        elif tag in CONTRACT_TAGS:
            category = 'Contract'
        elif tag in PAYMENT_TAGS:
            category = 'Payment'
        elif tag in TICKET_TAGS:
            category = 'Support Ticket'
        else:
            category = 'Other'

        result.append({
            'name': tag,
            'description': description,
            'category': category,
            'syntax': f'{{{{ {tag} }}}}',
        })

    return result


def get_all_tag_info() -> List[Dict[str, str]]:
    """
    Get ALL available tags for custom email templates.

    Custom templates (used by plugins) have access to all tags
    since they may be triggered by any context.

    Returns list of dicts with 'name', 'description', and 'category'.
    """
    result = []

    # Collect all tags with their categories
    tag_categories = [
        (BASE_TAGS, 'Business & Customer'),
        (APPOINTMENT_TAGS, 'Appointment'),
        (CONTRACT_TAGS, 'Contract'),
        (PAYMENT_TAGS, 'Payment'),
        (TICKET_TAGS, 'Support Ticket'),
    ]

    seen_tags = set()

    for tag_dict, category in tag_categories:
        for tag, description in sorted(tag_dict.items()):
            if tag not in seen_tags:
                seen_tags.add(tag)
                result.append({
                    'name': tag,
                    'description': description,
                    'category': category,
                    'syntax': f'{{{{ {tag} }}}}',
                })

    # Sort by category then name
    result.sort(key=lambda x: (x['category'], x['name']))

    return result
