# views.py
import logging
import re

from django.forms import ValidationError
from django.shortcuts import get_object_or_404, render, redirect
from django.db import transaction
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden
from django.template.loader import get_template
from django.urls import reverse
from django.contrib import messages

from events.utils.cookieutils import set_rsvp_cookie
from events.utils.summary import summary_activities, summary_email, summary_guests, summary_notes, summary_questions

from ..utils.rsvputils import add_to_context, create_or_switch_activity_response, create_context, get_event, get_from_context, get_rsvp

from ..forms import GuestForm, NotesForm, RSVPForm

from ..models import (
    Event,
    Guest,
    Activity,
    ActivityGroup,
    RSVP,
    ActivitySelection,
    Question,
    QuestionChoice,
    Response,
    ResponseChoice,
)

# Logger
logger = logging.getLogger(__name__)

# Start a new or unfinished RSVP
def rsvp_start(request, id:str):
    rsvp = get_object_or_404(RSVP, id=id)

    if not (rsvp.status == RSVP.Status.NOT_STARTED or rsvp.status == RSVP.Status.INCOMPLETE):
        return HttpResponseBadRequest()

    response = redirect(reverse('events:rsvp-base', args=[rsvp.edit_token, rsvp.page]))
    return response

# Update a completed RSVP
def rsvp_update(request, id:str):
    rsvp = get_object_or_404(RSVP, id=id)

    if rsvp.status != RSVP.Status.YES_JOINING:
        return HttpResponseBadRequest()

    rsvp.page = RSVP.Page.ACTIVITIES
    rsvp.save()

    response = redirect(reverse('events:rsvp-base', args=[rsvp.edit_token, rsvp.page]))
    return response

# Stop an existing RSVP
def rsvp_decline(request, id:str):
    rsvp = get_object_or_404(RSVP, id=id)

    rsvp.status = RSVP.Status.NOT_COMING
    rsvp.page = RSVP.Page.EMAIL
    rsvp.furthest_page = RSVP.Page.EMAIL
    rsvp.save()
    
    # Remove any current activity selection
    ActivitySelection.objects.filter(rsvp=rsvp).delete()
    
    # Remove any question responses
    Response.objects.filter(rsvp=rsvp).delete()
    Response.objects.filter(guest__rsvp=rsvp).delete()

    response = redirect(reverse('events:event', args=[rsvp.event.slug]))
    return response

def rsvp_restart(request, id:str):
    rsvp = get_object_or_404(RSVP, id=id)

    rsvp.status = RSVP.Status.INCOMPLETE
    rsvp.save()

    response = redirect(reverse('events:rsvp-base', args=[rsvp.edit_token, rsvp.page]))
    return response



# Main entrypoint for rsvp page
def rsvp_base_page(request, code:str, page:str):
    rsvp = get_object_or_404(RSVP, edit_token=code)

    context = {
        "rsvp": rsvp,
        "event": rsvp.event,
        "invite": rsvp.invite,
        "page": page,
        "is_locked": rsvp.event.is_locked()
    }

    response = render(request, "rsvp/rsvp.html", context)
    set_rsvp_cookie(response, rsvp.event, rsvp)
    return response


# Render selector based on the current step of the RSVP process
def rsvp_pages(request, id:str, page:str, error:str=""):
    logger.debug(f"rsvp_pages: id={id}, page={page}")
    
    rsvp = get_object_or_404(RSVP, id=id)

    if page == "":
        page = rsvp.page

    context = create_context(rsvp, error)

    rendercontent = {
        "page_email": _render_page_email(request, context, page),
        "page_guest": _render_page_guest(request, context, page),
        "page_activities": _render_page_activities(request, context, page),
        "page_questions": _render_page_questions(request, context, page),
        "page_notes": _render_page_notes(request, context, page)
    }
       
    response = render(request, "rsvp/rsvp_pages.html", rendercontent)
    response["HX-Push-Url"] = reverse('events:rsvp-base', args=[rsvp.edit_token, page])
    return response

# Email page
def _render_page_email(request, context, page) -> str:
    
    note = summary_email(get_rsvp(context))
    
    should_render = _render_page(request, get_rsvp(context), note, RSVP.Page.EMAIL, page)
    if(type(should_render) is not bool): 
        return should_render
    
    # Render the form
    add_to_context(context, "form", RSVPForm(instance=get_rsvp(context)))
    
    template = get_template("rsvp/rsvp_page_email.html")
    
    return template.render(context=context, request=request)

def _render_page_guest(request, context, page) -> str:
    
    note = summary_guests(get_rsvp(context))
    
    should_render = _render_page(request, get_rsvp(context), note, RSVP.Page.GUESTS, page)
    if(type(should_render) is not bool): 
        return should_render
    
    guests = (
        Guest.objects
        .filter(rsvp=get_rsvp(context))
    )
    
    add_to_context(context, "guests", guests)        
    add_to_context(context, "form", GuestForm())
    
    selected_activities = (
        ActivitySelection.objects
        .filter(rsvp=get_rsvp(context))
    )
    
    # Check if any of the activities is full
    selected_activities_full = [(True if act.activity.remaining_capacity() < 1 else False) for act in selected_activities]
    add_to_context(context, "space_for_extra_guest", True not in selected_activities_full)

    template = get_template("rsvp/rsvp_page_guest.html")
    return template.render(context=context, request=request)

def _render_page_activities(request, context, page) -> str:
    
    note = summary_activities(get_rsvp(context))
    
    should_render = _render_page(request, get_rsvp(context), note, RSVP.Page.ACTIVITIES, page)
    if(type(should_render) is not bool): 
        return should_render
    
    activities = (
        Activity.objects
        .filter(event=get_event(context))
        .select_related("group")
        .order_by("start_time")
    )
    
    groups = {}
    ungrouped = []

    for activity in activities:
        
        if activity.group:
            groups.setdefault(activity.group, []).append(activity)
        else:
            ungrouped.append(activity)
    
    add_to_context(context, "groups", groups)        
    add_to_context(context, "ungrouped_activities", ungrouped)        

    selected_activities = (
        ActivitySelection.objects
        .filter(rsvp=get_rsvp(context))
    )
    selected_activities_ids = [act.activity.id for act in selected_activities]
    add_to_context(context, "selected_activities", selected_activities_ids)

    template = get_template("rsvp/rsvp_page_activities.html")
    return template.render(context=context, request=request)

def _render_page_questions(request, context, page) -> str:
    """
    Gather questions for activities selected by this RSVP and the guests list.
    Pass `questions` and `guests` into the template context.
    """
    note = summary_questions(get_rsvp(context))
    
    should_render = _render_page(request, get_rsvp(context), note, RSVP.Page.QUESTIONS, page)
    if(type(should_render) is not bool): 
        return should_render

    rsvp = get_rsvp(context)

    # Get activities that were selected for this RSVP
    selections = ActivitySelection.objects.filter(rsvp=rsvp).select_related("activity")
    activities = [sel.activity for sel in selections]

    # Get all questions for the selected activities, with related activity and choices prefetched
    if activities:
        questions = (
            Question.objects
            .filter(activity__in=activities)
            .select_related("activity")
            .prefetch_related("choices")
            .order_by("activity__start_time", "activity__title", "order")
        )
    else:
        questions = Question.objects.none()

    # Guests are needed for guest-scoped questions
    guests = Guest.objects.filter(rsvp=rsvp).order_by("name")

    # Existing responses so templates can prefill current answers.
    # RSVP-scoped responses
    responses_rsvp = Response.objects.filter(rsvp=rsvp, question__in=questions).select_related("question").prefetch_related("choices")
    # Guest-scoped responses (responses for guests of this rsvp)
    responses_guest = Response.objects.filter(guest__rsvp=rsvp, question__in=questions).select_related("question", "guest").prefetch_related("choices")

    add_to_context(context, "questions", questions)
    add_to_context(context, "guests", guests)
    add_to_context(context, "responses_rsvp", responses_rsvp)
    add_to_context(context, "responses_guest", responses_guest)

    template = get_template("rsvp/rsvp_page_questions.html")
    return template.render(context=context, request=request)

def _render_page_notes(request, context, page) -> str:
    
    note = summary_notes(get_rsvp(context))
    
    should_render = _render_page(request, get_rsvp(context), note, RSVP.Page.NOTES, page)
    if(type(should_render) is not bool): 
        return should_render
    
    add_to_context(context, "form", NotesForm(instance=get_rsvp(context)))
    
    template = get_template("rsvp/rsvp_page_notes.html")
    return template.render(context=context, request=request)

# Render generic page placeholder
def _render_page(request, rsvp:RSVP, note:str, page:RSVP.Page, current_page:RSVP.Page, oob:bool=False) -> bool | str:
    if page == current_page:
        return True
    
    template = get_template("rsvp/rsvp_page_inactive.html")
    
    context = {
        "note": note,
        "rsvp": rsvp,
        "page": page,
        "complete": rsvp.is_page_finished(page),
        "oob": oob
    }
    
    return template.render(context=context, request=request)

# Change the page of the request
def page_change(request, id:str, page:str):
    logger.debug(f"page_change: id={id}, page={page}")
    
    rsvp = get_object_or_404(RSVP, id=id)
    error = ""
    
    if page == "next":
        allowed = rsvp.is_next_page_allowed()
        if(type(allowed) is bool and allowed == True):
            rsvp.next_page()
        elif type(allowed) is str:
        # allowed contains an error
            error = allowed
    elif page == "previous":
        rsvp.previous_page()
    else:
        rsvp.set_page(page)
    
    # Done with rsvp
    if rsvp.page == RSVP.Page.COMPLETED:
        messages.success(request, "Thank you for responding!")
        response = HttpResponse()
        response["HX-Redirect"] = reverse('events:event', args=[rsvp.event.slug])
        return response
    
    return rsvp_pages(request, id, rsvp.page, error=error)

# Email step: handle post data
def post_step_email(request, id:str):
    rsvp = get_object_or_404(RSVP, id=id)
    
    if request.method != 'POST':
        return HttpResponseBadRequest()
    
    form = RSVPForm(request.POST)
    if form.is_valid():
        rsvp.responder_email = form.cleaned_data['responder_email']
        rsvp.send_email_updates = form.cleaned_data['send_email_updates']
        rsvp.save()

        return HttpResponse('Success', status=200)

    return HttpResponseBadRequest(form.errors)

# Guest step: handle post data
def post_step_guest(request, id:str):
    rsvp = get_object_or_404(RSVP, id=id)
    
    if request.method != 'POST':
        return HttpResponseBadRequest()
    
    form = GuestForm(request.POST)
    if form.is_valid():
        guest = form.save(commit=False)
        guest.event = rsvp.event
        guest.invite = rsvp.invite
        guest.rsvp = rsvp
        guest.save()

        content = _render_page_guest(request, create_context(rsvp), RSVP.Page.GUESTS)
        return HttpResponse(content)

    return HttpResponseBadRequest(form.errors)

# Guest step: handle post data
def delete_step_guest(request, id:str, gid:str):
    rsvp = get_object_or_404(RSVP, id=id)
    guest = get_object_or_404(Guest, id=gid)
    
    if request.method != 'DELETE':
        return HttpResponseBadRequest()
    
    if guest.rsvp.id != rsvp.id:
        return HttpResponseBadRequest()
    
    guest.delete()
    
    content = _render_page_guest(request, create_context(rsvp), RSVP.Page.GUESTS)
    return HttpResponse(content)

# Guest step: handle post data
def post_step_activities(request, id:str, aid:str):
    rsvp = get_object_or_404(RSVP, id=id)
    activity = get_object_or_404(Activity, id=aid)
    
    if request.method != 'POST':
        return HttpResponseBadRequest()
    
    try:
        create_or_switch_activity_response(rsvp, activity)
    except ValidationError as e:
        logger.info(str(e))
        return HttpResponseBadRequest(str(e))

    content = _render_page_activities(request, create_context(rsvp), RSVP.Page.ACTIVITIES)
    
    # update the guests summary
    note = summary_questions(rsvp)
    summary = _render_page(request, rsvp, note, RSVP.Page.QUESTIONS, RSVP.Page.ACTIVITIES, oob=True)
    content = content + str(summary)
    
    return HttpResponse(content)


def _create_or_update_response(response_kwargs, value_text=None, choice_obj=None):
    """
    Ensure a single Response exists for (activity, question, rsvp|guest).
    - For non-choice questions: store `value_text` and clear any choices.
    - For single-choice questions: set exactly one ResponseChoice (replace existing).
    - For multiple-choice questions: add the provided choice (caller can call repeatedly for multiple selections).
    """
    # Find existing response for this owner/question/activity
    existing = Response.objects.filter(
        activity=response_kwargs['activity'],
        question=response_kwargs['question'],
        rsvp=response_kwargs.get('rsvp'),
        guest=response_kwargs.get('guest'),
    ).first()

    # Create response if it doesn't exist
    if not existing:
        resp = Response(**response_kwargs)
        # Clear choice-related storage; we'll attach choices via ResponseChoice below
        resp.value_text = "" if choice_obj else (value_text or "")
        resp.full_clean()
        resp.save()
    else:
        resp = existing
        if choice_obj:
            # When attaching choices, keep value_text empty
            resp.value_text = ""
            resp.save()
        else:
            # Non-choice answer: set value_text and remove any existing selected choices
            resp.value_text = value_text or ""
            resp.save()
            ResponseChoice.objects.filter(response=resp).delete()

    # If a choice was supplied, attach it via ResponseChoice
    if choice_obj:
        # question kind influences behavior: single should replace, multiple should add
        kind = response_kwargs['question'].kind if 'question' in response_kwargs else None

        if kind == "single":
            # Keep exactly this single choice for the response
            ResponseChoice.objects.filter(response=resp).exclude(choice=choice_obj).delete()
            ResponseChoice.objects.get_or_create(response=resp, choice=choice_obj)
        else:
            # multiple: ensure this choice exists (caller may call repeatedly for several choices)
            ResponseChoice.objects.get_or_create(response=resp, choice=choice_obj)

    return resp

# Question step: handle post data
def post_response_rsvp(request, id:str, qid:str):
    
    if request.method != "POST":
        return HttpResponseBadRequest()
    
    rsvp = get_object_or_404(RSVP, id=id)
    question = get_object_or_404(Question, id=qid)
    
    activity = question.activity

    # Parse inputs:
    if question.kind == "single":
        choice_id = request.POST.get("choice")
        choice = get_object_or_404(QuestionChoice, id=choice_id) if choice_id else None
        resp = _create_or_update_response(
            {"activity": activity, "question": question, "rsvp": rsvp},
            choice_obj=choice
        )
    elif question.kind == "multiple":
        choice_ids = request.POST.getlist("choices")
        # For simplicity: create one Response per choice (or replace existing)
        # Implementation detail depends on Response model use-case.
        # (Example: delete existing and create new ones)
        Response.objects.filter(activity=activity, question=question, rsvp=rsvp).delete()
        for cid in choice_ids:
            choice = get_object_or_404(QuestionChoice, id=cid)
            _create_or_update_response({"activity": activity, "question": question, "rsvp": rsvp}, choice_obj=choice)
    elif question.kind == "boolean":
        val = request.POST.get("value_bool")
        _create_or_update_response({"activity": activity, "question": question, "rsvp": rsvp}, value_text=val)
    else:
        val = request.POST.get("value", "")
        _create_or_update_response({"activity": activity, "question": question, "rsvp": rsvp}, value_text=val)

    # Re-render the questions page (or the single question partial). For simplicity render the whole questions page:
    content = _render_page_questions(request, create_context(rsvp), RSVP.Page.QUESTIONS)
    return HttpResponse(content)

def post_response_guest(request, id:str, qid:str, gid:str):
    if request.method != "POST":
        return HttpResponseBadRequest()
    rsvp = get_object_or_404(RSVP, id=id)
    question = get_object_or_404(Question, id=qid)
    guest = get_object_or_404(Guest, id=gid)
    activity = question.activity

    if question.scope != "guest":
        return HttpResponseBadRequest("Question is not guest-scoped")

    if question.kind == "single":
        choice_id = request.POST.get("choice")
        choice = get_object_or_404(QuestionChoice, id=choice_id) if choice_id else None
        _create_or_update_response({"activity": activity, "question": question, "guest": guest}, choice_obj=choice)
    elif question.kind == "multiple":
        choice_ids = request.POST.getlist("choices")
        Response.objects.filter(activity=activity, question=question, guest=guest).delete()
        for cid in choice_ids:
            choice = get_object_or_404(QuestionChoice, id=cid)
            _create_or_update_response({"activity": activity, "question": question, "guest": guest}, choice_obj=choice)
    elif question.kind == "boolean":
        val = request.POST.get("value_bool")
        _create_or_update_response({"activity": activity, "question": question, "guest": guest}, value_text=val)
    else:
        val = request.POST.get("value", "")
        _create_or_update_response({"activity": activity, "question": question, "guest": guest}, value_text=val)

    content = _render_page_questions(request, create_context(rsvp), RSVP.Page.QUESTIONS)
    return HttpResponse(content)

# Guest step: handle post data
def post_step_notes(request, id:str):
    rsvp = get_object_or_404(RSVP, id=id)
    
    if request.method != 'POST':
        return HttpResponseBadRequest()
    
    form = NotesForm(request.POST)
    if form.is_valid():
        rsvp.notes = form.cleaned_data['notes']
        rsvp.save()

        content = _render_page_notes(request, create_context(rsvp), RSVP.Page.NOTES)
        return HttpResponse(content)

    return HttpResponseBadRequest(form.errors)