from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify, send_file
from flask_login import login_required, current_user
from models import db, Conversation, User, Message, MessageAttachment, DocuPulseSettings
from forms import ConversationForm
from routes.auth import require_password_change
from utils import log_event, create_notification, get_unread_count
import os
from werkzeug.utils import secure_filename
from datetime import datetime

conversations_bp = Blueprint('conversations', __name__, url_prefix='/conversations')

# Configure upload settings
UPLOAD_FOLDER = os.environ.get('UPLOAD_FOLDER', 'uploads')
ALLOWED_EXTENSIONS = {
    # Documents
    'pdf', 'docx', 'doc', 'txt', 'rtf', 'odt', 'md', 'csv',
    # Spreadsheets
    'xlsx', 'xls', 'ods', 'xlsm',
    # Presentations
    'pptx', 'ppt', 'odp',
    # Images
    'jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp', 'tiff',
    # Archives
    'zip', 'rar', '7z', 'tar', 'gz',
    # Code/Text
    'py', 'js', 'html', 'css', 'json', 'xml', 'sql', 'sh', 'bat',
    # Audio
    'mp3', 'wav', 'ogg', 'm4a', 'flac',
    # Video
    'mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm',
    # CAD/Design
    'dwg', 'dxf', 'ai', 'psd', 'eps', 'indd',
    # Other
    'eml', 'msg', 'vcf', 'ics'
}
MAX_FILE_SIZE = 10 * 1024 * 1024  # 10MB

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def get_file_extension(filename):
    return filename.rsplit('.', 1)[1].lower() if '.' in filename else ''

@conversations_bp.route('/')
@login_required
@require_password_change
def conversations():
    search = request.args.get('search', '').strip()
    if current_user.is_admin:
        query = Conversation.query
    else:
        query = Conversation.query.filter(Conversation.members.any(id=current_user.id))
    if search:
        query = query.filter(Conversation.name.ilike(f'%{search}%'))
    conversations = query.order_by(Conversation.created_at.desc()).all()
    unread_count = get_unread_count(current_user.id)
    usage_stats = DocuPulseSettings.get_usage_stats()
    return render_template('conversations/conversations.html', conversations=conversations, search=search, unread_notifications=unread_count, usage_stats=usage_stats)

@conversations_bp.route('/create', methods=['GET', 'POST'])
@login_required
@require_password_change
def create_conversation():
    if not (current_user.is_admin or current_user.is_manager):
        flash('Only administrators and managers can create conversations.', 'error')
        return redirect(url_for('conversations.conversations'))
    
    form = ConversationForm()
    if form.validate_on_submit():
        conversation = Conversation(
            name=form.name.data,
            description=form.description.data,
            created_by=current_user.id
        )
        
        # Add creator as a member
        conversation.members.append(current_user)
        creator_id = current_user.id
        # Add selected members, skipping the creator if present
        for user_id in form.members.data:
            if int(user_id) != creator_id:
                user = User.query.get(user_id)
                if user and user not in conversation.members:
                    conversation.members.append(user)
        
        db.session.add(conversation)
        db.session.commit()

        # Create notifications for all members except the creator
        for member in conversation.members:
            if member.id != current_user.id:
                create_notification(
                    notif_type='conversation_invite',
                    user_id=member.id,
                    sender_id=current_user.id,
                    details={
                        'message': f'You have been added to conversation "{conversation.name}"',
                        'conversation_id': conversation.id,
                        'conversation_name': conversation.name,
                        'invited_by': f"{current_user.username} {current_user.last_name}",
                        'timestamp': datetime.utcnow().isoformat()
                    }
                )

        # Log conversation creation
        log_event(
            event_type='conversation_create',
            details={
                'conversation_id': conversation.id,
                'created_by': current_user.id,
                'created_by_name': f"{current_user.username} {current_user.last_name}",
                'name': conversation.name,
                'description': conversation.description,
                'member_count': len(conversation.members),
                'member_ids': [member.id for member in conversation.members]
            }
        )
        db.session.commit()
        
        flash('Conversation created successfully!', 'success')
        return redirect(url_for('conversations.conversations'))
    unread_count = get_unread_count(current_user.id)
    return render_template('conversations/create_conversation.html', form=form, unread_notifications=unread_count)

@conversations_bp.route('/<int:conversation_id>')
@login_required
@require_password_change
def conversation(conversation_id):
    conversation = Conversation.query.get_or_404(conversation_id)
    # Check if user is a member
    if not current_user.is_admin and current_user not in conversation.members:
        flash('You do not have access to this conversation.', 'error')
        return redirect(url_for('conversations.conversations'))
    
    # Log conversation open
    log_event(
        event_type='conversation_open',
        details={
            'conversation_id': conversation.id,
            'conversation_name': conversation.name,
            'user_id': current_user.id,
            'user_name': f"{current_user.username} {current_user.last_name}",
            'message_count': Message.query.filter_by(conversation_id=conversation_id).count()
        }
    )
    db.session.commit()
    
    # Query messages directly using the Message model
    messages = Message.query.filter_by(conversation_id=conversation_id).order_by(Message.created_at.asc()).all()
    
    # Get all users for member selection (needed for admin and manager)
    all_users = User.query.all() if (current_user.is_admin or current_user.is_manager) else None
    
    unread_count = get_unread_count(current_user.id)
    return render_template('conversations/conversation.html', 
                         conversation=conversation, 
                         messages=messages,
                         all_users=all_users,
                         unread_notifications=unread_count)

@conversations_bp.route('/<int:conversation_id>/members')
@login_required
@require_password_change
def conversation_members(conversation_id):
    conversation = Conversation.query.get_or_404(conversation_id)
    if not current_user.is_admin and current_user not in conversation.members:
        flash('You do not have access to this conversation.', 'error')
        return redirect(url_for('conversations.conversations'))
    
    if not (current_user.is_admin or current_user.is_manager):
        flash('Only administrators and managers can manage conversation members.', 'error')
        return redirect(url_for('conversations.conversation', conversation_id=conversation_id))
    
    available_users = User.query.filter(~User.id.in_([m.id for m in conversation.members])).all()
    unread_count = get_unread_count(current_user.id)
    return render_template('conversations/conversation_members.html', 
                         conversation=conversation, 
                         available_users=available_users,
                         unread_notifications=unread_count)

@conversations_bp.route('/<int:conversation_id>/members/add', methods=['POST'])
@login_required
@require_password_change
def add_member(conversation_id):
    conversation = Conversation.query.get_or_404(conversation_id)
    if not current_user.is_admin:
        flash('Only administrators can manage conversation members.', 'error')
        return redirect(url_for('conversations.conversation', conversation_id=conversation_id))
    
    user_id = request.form.get('user_id')
    if not user_id:
        flash('Please select a user to add.', 'error')
        return redirect(url_for('conversations.conversation_members', conversation_id=conversation_id))
    
    user = User.query.get_or_404(user_id)
    if user in conversation.members:
        flash('User is already a member of this conversation.', 'error')
    else:
        conversation.members.append(user)
        db.session.commit()

        # Create notification for the invited user
        create_notification(
            notif_type='conversation_invite',
            user_id=user.id,
            sender_id=current_user.id,
            details={
                'message': f'You have been invited to join conversation "{conversation.name}"',
                'conversation_id': conversation.id,
                'conversation_name': conversation.name,
                'invited_by': f"{current_user.username} {current_user.last_name}",
                'timestamp': datetime.utcnow().isoformat()
            }
        )

        # Log member addition
        log_event(
            event_type='conversation_member_add',
            details={
                'conversation_id': conversation.id,
                'conversation_name': conversation.name,
                'added_by': current_user.id,
                'added_by_name': f"{current_user.username} {current_user.last_name}",
                'added_user_id': user.id,
                'added_user_name': f"{user.username} {user.last_name}",
                'added_user_email': user.email
            }
        )
        db.session.commit()

        flash(f'{user.username} has been added to the conversation.', 'success')
    
    return redirect(url_for('conversations.conversation_members', conversation_id=conversation_id))

@conversations_bp.route('/<int:conversation_id>/members/<int:user_id>/remove', methods=['POST'])
@login_required
@require_password_change
def remove_member(conversation_id, user_id):
    conversation = Conversation.query.get_or_404(conversation_id)
    if not current_user.is_admin:
        flash('Only administrators can manage conversation members.', 'error')
        return redirect(url_for('conversations.conversation', conversation_id=conversation_id))
    
    if user_id == conversation.created_by:
        flash('Cannot remove the conversation creator.', 'error')
    else:
        user = User.query.get_or_404(user_id)
        if user not in conversation.members:
            flash('User is not a member of this conversation.', 'error')
        else:
            conversation.members.remove(user)
            db.session.commit()

            # Create notification for the removed user
            create_notification(
                notif_type='conversation_invite_removed',
                user_id=user.id,
                sender_id=current_user.id,
                details={
                    'message': f'You have been removed from conversation "{conversation.name}"',
                    'conversation_id': conversation.id,
                    'conversation_name': conversation.name,
                    'removed_by': f"{current_user.username} {current_user.last_name}",
                    'timestamp': datetime.utcnow().isoformat()
                }
            )

            # Log member removal
            log_event(
                event_type='conversation_member_remove',
                details={
                    'conversation_id': conversation.id,
                    'conversation_name': conversation.name,
                    'removed_by': current_user.id,
                    'removed_by_name': f"{current_user.username} {current_user.last_name}",
                    'removed_user_id': user.id,
                    'removed_user_name': f"{user.username} {user.last_name}",
                    'removed_user_email': user.email
                }
            )
            db.session.commit()

            flash('User has been removed from the conversation.', 'success')
    
    return redirect(url_for('conversations.conversation_members', conversation_id=conversation_id))

@conversations_bp.route('/<int:conversation_id>/edit', methods=['GET', 'POST'])
@login_required
@require_password_change
def edit_conversation(conversation_id):
    if not current_user.is_admin:
        flash('Only administrators can edit conversations.', 'error')
        return redirect(url_for('conversations.conversations'))
    conversation = Conversation.query.get_or_404(conversation_id)
    form = ConversationForm(obj=conversation)
    
    if request.method == 'POST':
        # Store old values for comparison
        old_values = {
            'name': conversation.name,
            'description': conversation.description,
            'member_ids': [member.id for member in conversation.members]
        }

        # Update name and description from form data
        conversation.name = form.name.data
        conversation.description = form.description.data

        # Get members from the form data and convert to integers
        member_ids = [int(id) for id in request.form.getlist('members')]
        
        # Update members
        current_member_ids = {user.id for user in conversation.members}
        new_member_ids = set(member_ids)
        
        # Remove members that are no longer in the list
        members_to_remove = current_member_ids - new_member_ids
        for member_id in members_to_remove:
            if member_id != conversation.created_by:  # Don't remove the creator
                user = User.query.get(member_id)
                if user:
                    conversation.members.remove(user)
                    # Create notification for removed user
                    create_notification(
                        notif_type='conversation_invite_removed',
                        user_id=user.id,
                        sender_id=current_user.id,
                        details={
                            'message': f'You have been removed from conversation "{conversation.name}"',
                            'conversation_id': conversation.id,
                            'conversation_name': conversation.name,
                            'removed_by': f"{current_user.username} {current_user.last_name}",
                            'timestamp': datetime.utcnow().isoformat()
                        }
                    )
        
        # Add new members
        members_to_add = new_member_ids - current_member_ids
        for member_id in members_to_add:
            user = User.query.get(member_id)
            if user and user not in conversation.members:
                conversation.members.append(user)
                # Create notification for the invited user
                create_notification(
                    notif_type='conversation_invite',
                    user_id=user.id,
                    sender_id=current_user.id,
                    details={
                        'message': f'You have been invited to join conversation "{conversation.name}"',
                        'conversation_id': conversation.id,
                        'conversation_name': conversation.name,
                        'invited_by': f"{current_user.username} {current_user.last_name}",
                        'timestamp': datetime.utcnow().isoformat()
                    }
                )
        
        db.session.commit()

        # Log conversation update
        log_event(
            event_type='conversation_update',
            details={
                'conversation_id': conversation.id,
                'updated_by': current_user.id,
                'updated_by_name': f"{current_user.username} {current_user.last_name}",
                'old_values': old_values,
                'new_values': {
                    'name': conversation.name,
                    'description': conversation.description,
                    'member_ids': [member.id for member in conversation.members],
                    'member_names': [f"{member.username} {member.last_name}" for member in conversation.members]
                }
            }
        )
        db.session.commit()

        flash('Conversation updated successfully!', 'success')
        
        # Check if redirect parameter is provided
        redirect_url = request.args.get('redirect')
        if redirect_url:
            return redirect(redirect_url)
        return redirect(url_for('conversations.conversations'))
    
    # Prepopulate form members with current members
    form.members.data = [str(user.id) for user in conversation.members]
    unread_count = get_unread_count(current_user.id)
    return render_template('conversations/create_conversation.html', form=form, edit_mode=True, conversation=conversation, unread_notifications=unread_count)

@conversations_bp.route('/<int:conversation_id>/delete', methods=['POST'])
@login_required
@require_password_change
def delete_conversation(conversation_id):
    if not current_user.is_admin:
        flash('Only administrators can delete conversations.', 'error')
        return redirect(url_for('conversations.conversations'))
    
    conversation = Conversation.query.get_or_404(conversation_id)
    
    # Log conversation deletion
    log_event(
        event_type='conversation_delete',
        details={
            'conversation_id': conversation.id,
            'conversation_name': conversation.name,
            'deleted_by': current_user.id,
            'deleted_by_name': f"{current_user.username} {current_user.last_name}",
            'member_count': len(conversation.members),
            'message_count': Message.query.filter_by(conversation_id=conversation_id).count()
        }
    )
    db.session.commit()
    
    # Get all messages in the conversation
    messages = Message.query.filter_by(conversation_id=conversation_id).all()
    
    # Delete attachments and their files first
    for message in messages:
        for attachment in message.attachments:
            # Delete the physical file
            if os.path.exists(attachment.path):
                os.remove(attachment.path)
            # Delete the attachment record
            db.session.delete(attachment)
    
    # Delete all messages in the conversation
    for message in messages:
        db.session.delete(message)
    
    # Delete the conversation
    db.session.delete(conversation)
    db.session.commit()
    
    flash('Conversation has been deleted successfully.', 'success')
    return redirect(url_for('conversations.conversations'))

@conversations_bp.route('/<int:conversation_id>/messages', methods=['GET'])
@login_required
@require_password_change
def get_messages(conversation_id):
    conversation = Conversation.query.get_or_404(conversation_id)
    
    # Check if user is a member
    if not current_user.is_admin and current_user not in conversation.members:
        return jsonify({'error': 'Unauthorized'}), 403
    
    # Get the last message ID from the request
    last_message_id = request.args.get('last_message_id', type=int)
    
    # Query for new messages
    query = Message.query.filter_by(conversation_id=conversation_id)
    if last_message_id:
        query = query.filter(Message.id > last_message_id)
    
    messages = query.order_by(Message.created_at.asc()).all()
    
    # Format messages for response
    message_data = [{
        'id': message.id,
        'content': message.content,
        'created_at': message.created_at.strftime('%b %d, %Y %H:%M'),
        'sender_id': str(message.user_id),
        'sender_name': f"{message.user.username} {message.user.last_name}",
        'attachments': [{
            'name': attachment.name,
            'size': attachment.size,
            'url': url_for('conversations.download_attachment', message_id=message.id, attachment_index=index)
        } for index, attachment in enumerate(message.attachments)]
    } for message in messages]
    
    return jsonify({
        'success': True,
        'messages': message_data
    })

@conversations_bp.route('/<int:conversation_id>/send_message', methods=['POST'])
@login_required
@require_password_change
def send_message(conversation_id):
    conversation = Conversation.query.get_or_404(conversation_id)
    
    # Check if user is a member
    if not current_user.is_admin and current_user not in conversation.members:
        return jsonify({'error': 'Unauthorized'}), 403
    
    message_content = request.form.get('message', '').strip()
    file_count = int(request.form.get('file_count', 0))
    
    if not message_content and file_count == 0:
        return jsonify({'error': 'Message cannot be empty'}), 400
    
    # Create the message
    message = Message(
        content=message_content,
        user_id=current_user.id,
        conversation_id=conversation_id
    )
    db.session.add(message)
    db.session.flush()  # Get the message ID
    
    # Handle file attachments
    attachments = []
    for i in range(file_count):
        file_key = f'file_{i}'
        if file_key in request.files:
            file = request.files[file_key]
            if file and allowed_file(file.filename):
                filename = secure_filename(file.filename)
                file_path = os.path.join(UPLOAD_FOLDER, filename)
                file.save(file_path)
                
                attachment = MessageAttachment(
                    message_id=message.id,
                    name=filename,
                    path=file_path,
                    size=os.path.getsize(file_path)
                )
                db.session.add(attachment)
                attachments.append(attachment)
    
    db.session.commit()

    # Create notifications for all conversation members except the sender
    for member in conversation.members:
        if member.id != current_user.id:
            create_notification(
                notif_type='conversation_message',
                user_id=member.id,
                sender_id=current_user.id,
                details={
                    'message': f'New message in conversation "{conversation.name}"',
                    'conversation_id': conversation.id,
                    'conversation_name': conversation.name,
                    'sender': f"{current_user.username} {current_user.last_name}",
                    'message_preview': message_content[:100] + ('...' if len(message_content) > 100 else ''),
                    'has_attachments': len(attachments) > 0,
                    'attachment_count': len(attachments),
                    'timestamp': datetime.utcnow().isoformat()
                }
            )

    # Log message creation
    log_event(
        event_type='message_create',
        details={
            'message_id': message.id,
            'conversation_id': conversation_id,
            'conversation_name': conversation.name,
            'sender_id': current_user.id,
            'sender_name': f"{current_user.username} {current_user.last_name}",
            'has_attachments': len(attachments) > 0,
            'attachment_count': len(attachments),
            'attachment_types': [get_file_extension(att.name) for att in attachments] if attachments else []
        }
    )
    db.session.commit()
    
    # Prepare message data for response
    message_data = {
        'id': message.id,
        'content': message.content,
        'created_at': message.created_at.strftime('%b %d, %Y %H:%M'),
        'sender_id': str(current_user.id),
        'sender_name': f"{current_user.username} {current_user.last_name}",
        'sender_avatar': url_for('profile_pic', filename=current_user.profile_picture) if current_user.profile_picture else url_for('static', filename='default-avatar.png'),
        'attachments': [{
            'name': attachment.name,
            'size': attachment.size,
            'url': url_for('conversations.download_attachment', message_id=message.id, attachment_index=index)
        } for index, attachment in enumerate(attachments)]
    }
    
    return jsonify({
        'success': True,
        'message': message_data
    })

@conversations_bp.route('/messages/<int:message_id>/attachment/<int:attachment_index>')
@login_required
@require_password_change
def download_attachment(message_id, attachment_index):
    message = Message.query.get_or_404(message_id)
    conversation = message.conversation
    
    # Check if user is a member
    if not current_user.is_admin and current_user not in conversation.members:
        flash('You do not have access to this file.', 'error')
        return redirect(url_for('conversations.conversation', conversation_id=conversation.id))
    
    try:
        attachment = message.attachments[attachment_index]
        return send_file(
            attachment.path,
            as_attachment=True,
            download_name=attachment.name
        )
    except (IndexError, Exception) as e:
        flash('File not found.', 'error')
        return redirect(url_for('conversations.conversation', conversation_id=conversation.id)) 