import io
from datetime import datetime
# from lib2to3.fixes.fix_input import context

import openpyxl
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import authenticate, login

from django.contrib.auth.decorators import user_passes_test
from django.contrib.auth.mixins import UserPassesTestMixin
from django.contrib.auth.models import User
from django.core.mail import send_mail
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404, redirect
from django.urls import reverse, reverse_lazy
from django.utils import timezone
from django.contrib.auth.views import LoginView
from django.views.generic import FormView
from openpyxl import Workbook

from apps.carers.forms import FileUploadForm
from apps.carers.lib.process_excel import process_excel_file
from apps.carers.models import Profile, Review
from apps.orgs.decorators import check_user_is_administrator
from apps.orgs.forms import OrganisationForm, SignupForm
from apps.orgs.models import StaffLinkRequest, Administrator, Organisation


# Create your views here.
@user_passes_test(check_user_is_administrator, login_url='/organisations/login')
def dashboard(request):
    # todo: user should be able to select an organisation here if multiple, or switch organisation
    organisation = request.user.administrator.organisations.all().first()
    # Get all the organizations to include: the current one and its children
    child_organisations = organisation.children.all()
    all_organisations = [organisation] + list(child_organisations)

    # Fetch reviews for staff of the current and child organizations
    reviews = Review.objects.filter(
        user__profile__organisations__in=all_organisations
    ).distinct().order_by('-created_at')[:30]

    context = {
        'organisation': organisation,
        'reviews': reviews,
    }

    return render(request, 'orgs/dashboard.html', context)

@user_passes_test(check_user_is_administrator, login_url='/organisations/login')
def find_your_staff(request):
    organisation = request.user.administrator.organisations.first()


    return render(request, 'orgs/find_your_staff.html', {'organisation': organisation})

@user_passes_test(check_user_is_administrator, login_url='/organisations/login')
def staff_search(request):
    email = request.GET.get('staffEmail')
    results = User.objects.filter(email=email)

    return render(request, 'orgs/components/staff-search-results.html', context={'results': results})

def see_profile(request, user_id):
    user = User.objects.get(id=user_id)

    return render(request, 'orgs/see_profile.html', context={'user': user})


def create_xlsx_with_reviews(reviews, organisation_name):
    # Create an in-memory output file for the workbook.
    output = io.BytesIO()

    workbook = Workbook()
    sheet = workbook.active
    sheet.title = f"{organisation_name} reviews"

    # Write headers
    sheet.append(['Employee Name', 'Rating (1 - 5)', 'Review Text', 'Reviewer Name', 'Reviewer Phone', 'Reviewer Email',
                  'Relationship to User', 'Date of Review'])

    # Write data rows
    for review in reviews:
        user_name = f"{review.user.first_name} {review.user.last_name}"  # Get first and last name from the user
        sheet.append([
            user_name,  # User's first name and last name
            review.rating,  # The rating
            review.text,  # Review text
            review.reviewer_name,  # Reviewer's name
            review.reviewer_phone,  # Reviewer's phone
            review.reviewer_email,  # Reviewer's email
            review.relationship_to_user,  # Reviewer's relationship to the user
            review.created_at.strftime('%d/%m/%Y')
        ])

    # Save the workbook to the BytesIO object
    workbook.save(output)

    # Rewind the buffer
    output.seek(0)
    return output


@user_passes_test(check_user_is_administrator, login_url='/organisations/login')
def export_review(request):
    # Get the current organization
    organisation = request.user.administrator.organisations.all().first()

    # Get child organizations and combine with the current organization
    child_organisations = organisation.children.all()
    all_organisations = [organisation] + list(child_organisations)


    if request.POST:
        # Get selected reviews
        selected_ids_string = request.POST.get('selected_reviews', '')
        selected_ids = selected_ids_string.split(',') if selected_ids_string else []

        # Filter reviews for the selected IDs and relevant organizations
        reviews = Review.objects.filter(
            pk__in=selected_ids,
            user__profile__organisations__in=all_organisations
        ).distinct()

        # Generate the Excel file
        output = create_xlsx_with_reviews(reviews, organisation.name)

        now = timezone.now()
        date_string = now.strftime('%d/%m/%Y')

        # Create a response with the appropriate Excel MIME type and attach the file
        response = HttpResponse(
            output,
            content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        )
        response['Content-Disposition'] = f'attachment; filename={organisation.name}_reviews_export_{date_string}.xlsx'

        return response

    # Get reviews for relevant organizations
    reviews = Review.objects.filter(
        user__profile__organisations__in=all_organisations
    ).distinct()

    context = {
        'reviews': reviews,
        'children': child_organisations,
        'organisation': organisation,
    }
    return render(request, 'orgs/export_review.html', context=context)



@user_passes_test(check_user_is_administrator, login_url='/organisations/login')
def account(request):
    # todo: user should be able to select an organisation here if multiple, or switch organisation
    organisation = request.user.administrator.organisations.all().first()


    if request.method == 'POST':
        form = OrganisationForm(request.POST, request.FILES, instance=organisation)
        if form.is_valid():
            form.save()
        else:
            context = {
                'form': form,
                'organisation': organisation,
            }
            return render(request, 'orgs/account.html', context)

    form = OrganisationForm(instance=organisation)
    context = {
        'form': form,
        'organisation': organisation,
    }
    return render(request, 'orgs/account.html', context)

@user_passes_test(check_user_is_administrator, login_url='/organisations/login')
def send_link_request(request, profile_pk):
    # todo: user should be able to select an organisation here if multiple, or switch organisation
    organisation = request.user.administrator.organisations.all().first()
    profile = get_object_or_404(Profile, pk=profile_pk)
    StaffLinkRequest.objects.create(organisation=organisation, profile=profile)
    subject = f'Request to join {organisation.name}'
    login_url = f"{settings.SITE_BASE_URL}{reverse('login')}"  # Assuming you have a SITE_URL in your settings

    message = f'''
    Hello {profile},

    You have been invited to join {organisation.name} on our platform. By joining, you will be able to share your special mentions with the organisation.

    To accept this invitation, please log in to your account and follow the instructions.

    [Click here to login and accept the invitation]({login_url})

    Best regards,
    The {organisation.name} Team
    '''

    recipient_list = [profile.user.email]
    send_mail(
        subject,
        message,
        settings.DEFAULT_FROM_EMAIL,
        recipient_list,
        fail_silently=False,
    )
    return HttpResponse("Request Sent")


def accept_link_request(request, link_request_pk):
    link_request = get_object_or_404(StaffLinkRequest, pk=link_request_pk)
    organisation = link_request.organisation
    profile = link_request.profile
    organisation.staff.add(link_request.profile)
    profile.employer_name = organisation.name
    # TODO: this could cause massive issues
    profile.employer_email = organisation.email
    profile.save()
    link_request.delete()

    return HttpResponse(f"You are now a member of {link_request.organisation.name}.")


def org_signup(request):
    if request.method == 'POST':
        form = SignupForm(request.POST)
        if form.is_valid():
            first_name = form.cleaned_data['first_name']
            last_name = form.cleaned_data['last_name']
            email = form.cleaned_data['email']
            org_name = form.cleaned_data['org_name']
            password = form.cleaned_data['password']

            user = User.objects.create_user(username=email, email=email, password=password, first_name=first_name, last_name=last_name)

            administrator = Administrator.objects.create(user=user)

            organisation = Organisation.objects.create(name=org_name, email=email)
            organisation.admins.add(administrator)

            user = authenticate(username=email, password=password)
            if user is not None:
                login(request, user)

            return redirect('org_dashboard')

    else:
        form = SignupForm()

    return render(request, 'orgs/signup.html', {'form': form})

@user_passes_test(check_user_is_administrator, login_url='/organisations/login')
def filter_reviews(request):
    organisation = request.user.administrator.organisations.all().first()
    search_string = request.GET.get('search', None)
    rating = request.GET.get('rating', None)
    start_date = request.GET.get('start', None)
    end_date = request.GET.get('end', None)
    children = request.GET.get('children', None)

    # Start with the base query
    reviews = Review.objects.filter(user__profile__in=organisation.staff.all())

    # Apply rating filter if rating is provided and valid
    if rating:
        try:
            rating = int(rating)
            reviews = reviews.filter(rating__gte=rating)
        except ValueError:
            pass  # Ignore invalid rating value

    # Apply start date filter if start_date is provided and valid
    if start_date:
        start_date_object = datetime.strptime(start_date, '%Y-%m-%d').date()
        if start_date_object:
            reviews = reviews.filter(created_at__gte=start_date_object)

    # Apply end date filter if end_date is provided and valid
    if end_date:
        end_date_object = datetime.strptime(end_date, '%Y-%m-%d').date()
        if end_date_object:
            reviews = reviews.filter(created_at__lte=end_date_object)

    # Apply search string filter if provided
    if search_string:
        reviews = reviews.filter(Q(text__icontains=search_string) |Q(user__profile__preferred_name__icontains=search_string) | Q(user__first_name__icontains=search_string) | Q(user__last_name__icontains=search_string))


    # Apply children (staff member) filter if provided
    if children:
        try:
            # Get the child organization by PK
            child_org = get_object_or_404(Organisation, pk=children, parent=organisation)
            # Filter reviews to include only staff of the child organization
            reviews = reviews.filter(user__profile__in=child_org.staff.all())
        except Organisation.DoesNotExist:
            reviews = reviews.none()


    context = {
        'reviews': reviews,
        'organisation': organisation,
    }

    return render(request, 'orgs/reviews_table.html', context=context)

# def filter_reviews(request):
#     organisation = request.user.administrator.organisations.all().first()
#     search_string = request.GET.get('search', None)
#     rating = int(request.GET.get('rating', None))
#     start_date = request.GET.get('start', None)
#     if start_date:
#         start_date_object = datetime.strptime(start_date, '%Y-%m-%d').date()
#     end_date = request.GET.get('end', None)
#
#     # include start_date_object in the filter, only if it exists
#     reviews = Review.objects.filter(user__profile__in=organisation.staff.all(), rating__gte=rating, created_at__gte=start_date_object)
#
#
#
#     context = {
#         'reviews': reviews,
#     }
#
#     return render(request, 'orgs/reviews_table.html', context=context)
def remove_staff_from_organisation(request, profile_pk, org_pk):
    profile = get_object_or_404(Profile, pk=profile_pk)
    organisation = get_object_or_404(Organisation, pk=org_pk)

    if organisation in request.user.administrator.organisations.all():
        organisation.staff.remove(profile)

    context = {
        'organisation': organisation,
    }

    return render(request, 'orgs/components/staff_table.html', context=context)


class OrganisationLoginView(LoginView):
    template_name = 'orgs/login.html'
    next_page = reverse_lazy('org_dashboard')  # Use reverse_lazy for named URL

    def get_success_url(self):
        return self.next_page or reverse_lazy('org_dashboard')


class UploadStaffForm(FormView, UserPassesTestMixin):
    template_name = "orgs/upload_staff.html"
    form_class = FileUploadForm

    def test_func(self):
        # This method checks if the user is a superuser
        try:
            administrator = self.request.user.administrator
            return Organisation.objects.filter(admins=administrator).exists()
        except:
            return False

    def form_valid(self, form):
        organisation = self.request.user.administrator.organisations.first()
        file = form.cleaned_data['file']
        number_added, total_rows = process_excel_file(file, organisation, admins=False, send_emails=True)
        messages.add_message(self.request, messages.INFO,
                             f"File processed. Imported {number_added} rows out of {total_rows}.")

        return super().form_valid(form)

    def get_success_url(self):
        return reverse('org_dashboard')

    def get_context_data(self, **kwargs):
        context = super().get_context_data()
        organisation = self.request.user.administrator.organisations.first()
        context['organisation'] = organisation
        return context
