from django.contrib import messages as flash_messages
from django.contrib.auth.decorators import login_required
from django.db.models import Prefetch
from django.http import Http404
from django.shortcuts import get_object_or_404, redirect, render
from django.views.decorators.http import require_POST

from jobs.models import JobApplication
from seekers.models import SeekerProfile

from .forms import MessageForm
from .models import Conversation, Message
from .notifications import notify_new_message
from .services import (
    ConversationClosedError,
    close_conversation,
    get_conversation_for_user,
    get_or_create_conversation,
    get_user_conversations,
    mark_conversation_read,
    reopen_conversation,
    send_message,
    start_conversation_for_application,
    start_direct_conversation_with_seeker,
    user_can_access_application,
)
from companies.models import Company
from subscriptions.usage import can_company_message


def _participant_required(view_func):
    """Allow seekers and employers only."""

    @login_required
    def wrapper(request, *args, **kwargs):
        if not (request.user.is_seeker or request.user.is_company or request.user.is_platform_admin):
            raise Http404
        return view_func(request, *args, **kwargs)

    return wrapper


@_participant_required
def inbox(request, pk=None):
    message_prefetch = Prefetch(
        'messages',
        queryset=Message.objects.select_related('sender').order_by('-created_at'),
        to_attr='recent_messages',
    )
    conversations = list(get_user_conversations(request.user).prefetch_related(message_prefetch))
    active = None
    messages_list = []
    form = MessageForm()

    if pk:
        active = get_conversation_for_user(request.user, pk)
        if not active:
            raise Http404
    elif conversations:
        active = conversations[0]

    if active:
        mark_conversation_read(active, request.user)
        messages_list = active.messages.select_related('sender').all()
        active.unread_count = 0

    return render(request, 'messaging/inbox.html', {
        'conversations': conversations,
        'active_conversation': active,
        'messages_list': messages_list,
        'form': form,
    })


def _company_for_user(user):
    return Company.objects.filter(owner=user, is_active=True).first()


def _company_can_message(user):
    if user.is_platform_admin:
        return True, ''
    if not user.is_company:
        return True, ''
    company = _company_for_user(user)
    return can_company_message(company, user)


@_participant_required
@require_POST
def send_message_view(request, pk):
    conversation = get_conversation_for_user(request.user, pk)
    if not conversation:
        raise Http404

    if request.user.is_company:
        allowed, message = _company_can_message(request.user)
        if not allowed:
            flash_messages.error(request, message)
            return redirect('messaging:inbox_conversation', pk=pk)

    if conversation.is_closed:
        flash_messages.warning(request, 'This conversation is closed. Reopen it to send messages.')
        return redirect('messaging:inbox_conversation', pk=pk)

    form = MessageForm(request.POST)
    if form.is_valid():
        try:
            message = send_message(conversation, request.user, form.cleaned_data['body'])
        except ConversationClosedError:
            flash_messages.warning(request, 'This conversation is closed.')
        else:
            notify_new_message(message)
    return redirect('messaging:inbox_conversation', pk=pk)


@_participant_required
@require_POST
def close_conversation_view(request, pk):
    conversation = get_conversation_for_user(request.user, pk)
    if not conversation:
        raise Http404
    if not request.user.is_company and not request.user.is_platform_admin:
        raise Http404
    if close_conversation(conversation, request.user):
        flash_messages.success(request, 'Conversation closed. The candidate can still read the history.')
    return redirect('messaging:inbox_conversation', pk=pk)


@_participant_required
@require_POST
def reopen_conversation_view(request, pk):
    conversation = get_conversation_for_user(request.user, pk)
    if not conversation:
        raise Http404
    if not request.user.is_company and not request.user.is_platform_admin:
        raise Http404
    if reopen_conversation(conversation, request.user):
        flash_messages.success(request, 'Conversation reopened. You can message the candidate again.')
    return redirect('messaging:inbox_conversation', pk=pk)


@login_required
def start_direct_with_seeker(request, slug):
    if not request.user.is_company and not request.user.is_platform_admin:
        flash_messages.error(request, 'Only employers can message candidates directly.')
        return redirect('seekers:detail', slug=slug)

    profile = get_object_or_404(SeekerProfile.objects.select_related('user'), slug=slug)
    if not profile.is_public:
        raise Http404
    if not profile.user_id:
        flash_messages.error(request, 'This candidate cannot receive messages yet.')
        return redirect('seekers:detail', slug=slug)

    if request.user.is_company:
        allowed, message = _company_can_message(request.user)
        if not allowed:
            flash_messages.error(request, message)
            return redirect('subscriptions:pricing')

    conversation = start_direct_conversation_with_seeker(request.user, profile.user)
    if not conversation:
        raise Http404
    return redirect('messaging:inbox_conversation', pk=conversation.pk)


@_participant_required
def start_from_application(request, application_id):
    if request.user.is_company:
        allowed, message = _company_can_message(request.user)
        if not allowed:
            flash_messages.error(request, message)
            return redirect('subscriptions:pricing')
    try:
        conversation = start_conversation_for_application(request.user, application_id)
    except JobApplication.DoesNotExist:
        raise Http404
    if not conversation:
        raise Http404
    next_url = request.GET.get('next')
    if next_url and next_url.startswith('/'):
        return redirect(next_url)
    return redirect('messaging:inbox_conversation', pk=conversation.pk)


def _redirect_after_application_action(request, application):
    next_url = request.POST.get('next') or request.GET.get('next')
    if next_url and next_url.startswith('/'):
        return redirect(next_url)
    if request.user.is_platform_admin:
        return redirect('platform_admin:application_detail', pk=application.pk)
    return redirect('ats:detail', pk=application.pk)


@_participant_required
@require_POST
def send_from_application(request, application_id):
    application = get_object_or_404(
        JobApplication.objects.select_related('job', 'applicant'),
        pk=application_id,
    )
    if not user_can_access_application(request.user, application):
        raise Http404

    if request.user.is_company:
        allowed, message = _company_can_message(request.user)
        if not allowed:
            flash_messages.error(request, message)
            return _redirect_after_application_action(request, application)

    conversation = get_or_create_conversation(application)
    if conversation.is_closed:
        flash_messages.warning(request, 'This conversation is closed.')
        return _redirect_after_application_action(request, application)

    form = MessageForm(request.POST)
    if form.is_valid():
        try:
            message = send_message(conversation, request.user, form.cleaned_data['body'])
        except ConversationClosedError:
            flash_messages.warning(request, 'This conversation is closed.')
        else:
            notify_new_message(message)
    return _redirect_after_application_action(request, application)


@_participant_required
@require_POST
def close_from_application(request, application_id):
    application = get_object_or_404(JobApplication.objects.select_related('job'), pk=application_id)
    if not user_can_access_application(request.user, application):
        raise Http404
    if not (request.user.is_company or request.user.is_platform_admin):
        raise Http404

    conversation = Conversation.objects.filter(application=application).first()
    if conversation and close_conversation(conversation, request.user):
        flash_messages.success(request, 'Conversation closed.')
    return _redirect_after_application_action(request, application)


@_participant_required
@require_POST
def reopen_from_application(request, application_id):
    application = get_object_or_404(JobApplication.objects.select_related('job'), pk=application_id)
    if not user_can_access_application(request.user, application):
        raise Http404
    if not (request.user.is_company or request.user.is_platform_admin):
        raise Http404

    conversation = Conversation.objects.filter(application=application).first()
    if conversation and reopen_conversation(conversation, request.user):
        flash_messages.success(request, 'Conversation reopened.')
    return _redirect_after_application_action(request, application)
