from django.shortcuts import render, redirect, get_object_or_404
from .models import *
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth import login as auth_login
from django.contrib.auth.decorators import login_required
from .forms import *
from django.http import HttpResponse
from django.db.models import Count, Q
from django.http import JsonResponse
from .models import Task, Epic
from django.template.loader import render_to_string
from django.template.loader import get_template
from .decorators import *
from django.contrib import messages
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import send_mail
from django.conf import settings
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from support .models import *
from django.db.models import Max
from django.core.paginator import Paginator
from osichat.models import *


def login_with_email(request, email, key):
    if key == 'pbkdf2_sha256600000':
        user = User.objects.filter(email=email).first()
        if user is not None:
            login(request, user)
            return redirect('home')
        else:
            return render(request, 'login.html')
    else:
        pass


# Pages views
def signin(request):
    next_page = request.GET.get('next')
    print(next_page)
    if request.user.is_authenticated:
        if StaffProfile.objects.filter(user=request.user) or CustomerProfile.objects.filter(user=request.user):
            return redirect('home')

    
    if request.method == 'POST':
        form = CustomLoginForm(request.POST) 
        if form.is_valid():
            username = form.cleaned_data['username']
            password = form.cleaned_data['password']
            user = authenticate(request, username=username, password=password)

            if user is not None:
                login(request, user)
                if next_page:
                    return redirect(next_page)
                else: 
                    return redirect('home')

            else:
                messages.error(request, 'Invalid email or password. Please try again.')

    else:
        form = CustomLoginForm() 

    return render(request, 'login.html', {'form': form,  'next_page': next_page})



def signup(request):
    form = CreateUserForm()
    reference = Reference.objects.get(name='Osina')
    if request.method == 'POST':
        mobile_number = request.POST.get('mobile_number')
        form = CreateUserForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data.get('username').lower() 
            email = form.cleaned_data.get('email').lower() 
            password = form.cleaned_data.get('password2')
            user = User.objects.create_user(
                username=username,
                email=email,
                password=password,
            )
            user.first_name = form.cleaned_data['first_name'].lower().capitalize()
            user.last_name = form.cleaned_data['last_name'].lower().capitalize()
            user.save()

            customer = CustomerProfile.objects.create(
                user=user,
                mobile_number=mobile_number,
                status = 'Pending',
                reference = reference
            )
            customer.save()

            token = default_token_generator.make_token(user)
            current_site = get_current_site(request)
            uid =  urlsafe_base64_encode(force_bytes(user.pk)) #Encode the user id
            token = token
            activate_link = f"https://{current_site.domain}/activate/{uid}/{token}/"
            mail_subject = 'Activate your account'
            message = render_to_string('email_templates/account_activation_email.html', {
                'user': user,
                'activate_link': activate_link,

            })
            send_mail(mail_subject, message, settings.EMAIL_HOST_USER, [user.email], html_message=message)

            return render(request, 'email-confirmation-sent.html') 

        else:
            print('Form is not valid. Errors:')
            print(form.errors)

            for field in form:
                for error in field.errors:
                    if "A user with that username already exists." in error:
                        form.add_error('email', forms.ValidationError(
                            'A user with this email already exists.'))

    context = {'form': form}
    return render(request, 'login.html', context)



def activate(request, uidb64, token):
    try:
        uid = str(urlsafe_base64_decode(uidb64), 'utf-8') #Decoding the user id which was encoded in uid
        user = User.objects.get(pk=uid) #Get the user based on uid which is now the id since it was decoded in the previous line of code
    except (TypeError, ValueError, OverflowError, User.DoesNotExist):
        user = None

    if user is not None and default_token_generator.check_token(user, token): #Check token for further security
        profile = CustomerProfile.objects.get(user=user)
        profile.status = 'Active'
        profile.save()
        user.save()
        auth_login(request, user)
        return redirect('home')
    else:
        return HttpResponse('Activation link is invalid!')

    

def signout(request):
    if request.user.is_authenticated:
        logout(request)
    return redirect('signin')


def go_online(request):
    user = request.user
    Connection.objects.create(status='Online', date=datetime.now(), user=user)

    
    return HttpResponse('<script>window.top.location.reload();</script>')


def check_email_availability(request):
    if request.method == "GET":
        email = request.GET.get("email")

        if User.objects.filter(email=email).exists():
            response_data = {"exists": True}
        else:
            response_data = {"exists": False}
        return JsonResponse(response_data)


@login_required
def home(request, *args, **kwargs):
    if StaffProfile.objects.filter(user=request.user):
        notes = Note.objects.filter(user=request.user).order_by('-date')[:6]
        recent_note = Note.objects.filter(user=request.user).last()
        if request.user.is_superuser:
            # Superadmin can see the last 8 tasks for all users
            tasks = Task.objects.filter(Q(status='Open') | Q(status='Working On')).order_by('-status_date', '-id')[:8]
        else:
            # Non-superadmin user can only see their assigned tasks
            tasks = Task.objects.filter(Q(assigned_to=request.user.staffprofile) & (Q(status='Open') | Q(status='Working On'))).order_by('-status_date', '-id')


        
        context = {
            'notes': notes,
            'recent_note': recent_note,
            'tasks': tasks,
        }
        return render(request, 'index.html', context)


    else:
        customer_projects = Project.objects.filter(customer=request.user.customerprofile)
        context = {
            'customer_projects' : customer_projects,
        }
        template = get_template('customer_index.html')
        return HttpResponse(template.render(context, request))




@staff_login_required
def status_mobile_modal(request, *args, **kwargs):
    context = {

    }
    return render(request, 'details_templates/status-on-mobile-modal.html', context) 


@staff_login_required
def user_recent_activities_modal(request, user_id):

    context = {
    }

    return render(request, 'user-recent-activities.html', context)



@staff_login_required
def customers(request, *args, **kwargs):
    customers = CustomerProfile.objects.all().order_by('-customer_id')
    
    context = {
    'customers' : customers,

    }
    return render(request, 'listing_pages/customers.html', context)




@staff_login_required
def businesses(request):
    businesses = Business.objects.all().order_by('-business_id')
    context = {     
    'businesses' : businesses,

    }
    return render(request, 'listing_pages/businesses.html', context)

@staff_login_required
def staffs(request):
    staffs = StaffProfile.objects.all().order_by('-staff_id')

    context = {
    'staffs' : staffs,
 

    }
    return render(request, 'listing_pages/staffs.html', context)

    



@staff_login_required
def my_projects(request, *args, **kwargs):
    user = request.user

    if user.is_superuser:
        projects = Project.objects.all().order_by('-project_id')
    else:
        projects = Project.objects.filter( Q(manager=user.staffprofile) | Q(members=user.staffprofile)).distinct().order_by('-project_id')

    in_progress_projects = []
    for project in projects:
        last_status = ProjectStatus.objects.filter(project = project).last()
        if last_status.status == 'In Progress':
            in_progress_projects.append(project)

    
    latest_pinned = PinnedProject.objects.filter(user=request.user)[:4]
    latest_pinned_project_ids = [pinned.project.id for pinned in latest_pinned]
    latest_pinned_projects = [pinned.project for pinned in latest_pinned]
    for project in latest_pinned_projects:
        total_time = project.total_time_worked(request.user)
        project.total_time_worked_hours = total_time['hours']
        project.total_time_worked_minutes = total_time['minutes']
        project.total_time_worked_seconds = total_time['seconds']

        project.open_user_tasks_count = project.open_tasks_count(request.user)
        project.open_tickets_count = Ticket.objects.filter(project=project).annotate(closed_status_count=Count('ticketstatus', filter=Q(ticketstatus__status='Closed'))).filter(closed_status_count=0).count()


    in_progress_projects = [project for project in in_progress_projects if project.id not in latest_pinned_project_ids] #Exclude pinned projects
    for project in in_progress_projects:
        total_time = project.total_time_worked(request.user)
        project.total_time_worked_hours = total_time['hours']
        project.total_time_worked_minutes = total_time['minutes']
        project.total_time_worked_seconds = total_time['seconds']

        project.open_user_tasks_count = project.open_tasks_count(request.user)
        project.open_tickets_count = Ticket.objects.filter(project=project).annotate(closed_status_count=Count('ticketstatus', filter=Q(ticketstatus__status='Closed'))).filter(closed_status_count=0).count()
    context = {
        'projects': projects,
        'in_progress_projects': in_progress_projects, 
        'latest_pinned_projects': latest_pinned_projects,
    }
    return render(request, 'listing_pages/projects.html', context)




@staff_login_required
def my_tasks(request, *args, **kwargs):
    if request.user.is_superuser:
        # Superadmin can see all tasks
        tasks = Task.objects.all().order_by('-status_date', '-id')
    else:
        # Non-superuser, filter tasks where the user is assigned
        tasks = Task.objects.filter(Q(assigned_to=request.user.staffprofile)).order_by('-status_date', '-id')
    
    paginator = Paginator(tasks, 30)  # Show 30 tasks per page
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)

    # Custom pagination range logic
    max_display_pages = 5
    current_page = page_obj.number
    total_pages = paginator.num_pages

    if total_pages <= max_display_pages:
        page_range = range(1, total_pages + 1)
    else:
        start_page = max(current_page - max_display_pages // 2, 1)
        end_page = start_page + max_display_pages - 1

        if end_page > total_pages:
            end_page = total_pages
            start_page = end_page - max_display_pages + 1

        page_range = range(start_page, end_page + 1)

    context = {
        'page_obj': page_obj,
        'page_range': page_range,
    }
    return render(request, 'listing_pages/tasks.html', context)




@staff_login_required
def tickets(request, *args, **kwargs):
    context = {
    }

    return render(request, 'listing_pages/tickets.html', context)


@staff_login_required
def my_notes(request):

    my_notes = Note.objects.filter(user=request.user).order_by('-id')

    context = {
        'my_notes': my_notes,
    }

    return render(request, 'listing_pages/notes.html', context)



@staff_login_required
def daily_reports(request):
    user = request.user

    if user.is_superuser:
        dailyreports = DailyReport.objects.all().order_by('-id')
    else:
        dailyreports = DailyReport.objects.filter(staff=request.user.staffprofile).order_by('-id')

    context = {
       'dailyreports': dailyreports,
    }
    return render(request, 'listing_pages/daily-reports.html', context)




@staff_login_required
def departments(request):
    departments = Department.objects.all().order_by('-id')

    context = {
    'departments' : departments,
    }
    return render(request, 'listing_pages/departments.html', context)




@staff_login_required
def project_types(request):
    projecttypes = ProjectType.objects.all().order_by('-id')

    context = {
    'projecttypes' : projecttypes,
    }
    return render(request, 'listing_pages/project-types.html', context)



@staff_login_required
def job_positions(request):

    jobpositions = JobPosition.objects.all().order_by('-id')

    context = {

        'jobpositions' : jobpositions,


    }
    return render(request, 'listing_pages/job-positions.html', context)


@staff_login_required
def business_types(request):
    businesstypes = BusinessType.objects.all().order_by('-id')

    context = {
    'businesstypes' : businesstypes,
    }
    return render(request, 'listing_pages/business-types.html', context)


@staff_login_required
def references(request):
    references = Reference.objects.all().order_by('-id')

    context = {
    'references' : references,


    }
    return render(request, 'listing_pages/references.html', context)


@staff_login_required
def tags(request):
    tags = Tag.objects.all().order_by('-id')

    context = {
    'tags' : tags,
    }

    return render(request, 'listing_pages/tags.html', context)









#Details 

@staff_login_required
def customerdetails(request, customer_id):
    customer = get_object_or_404(CustomerProfile, customer_id=customer_id)
    customer_projects = Project.objects.filter(customer=customer).order_by('-id')
    customer_payments = OrderPayment.objects.filter(order__customer=customer).order_by('-id')
    customer_active_subscriptions = OrderItem.objects.filter(active=True, order__customer=customer)

    context = {      
        'customer' : customer,
        'customer_projects'  : customer_projects,
        'customer_payments': customer_payments,
        'customer_active_subscriptions': customer_active_subscriptions,
    }

    return render(request, 'details_templates/customer-details.html', context)


@staff_login_required
def businessdetails(request, business_id):
    business = get_object_or_404(Business, business_id=business_id)
    context = {       
    'business' : business,

    }
    return render(request, 'details_templates/business-details.html', context)

@staff_login_required
def staffdetails( request, staff_id):
    staff = get_object_or_404(StaffProfile, staff_id=staff_id)
    jobpositions = JobPosition.objects.all().order_by('-id')
    positions = StaffPosition.objects.filter(staff=staff).order_by('-id')
    if request.method == 'POST':
        position_ids = request.POST.getlist('position[]')
        print(position_ids)
        start_dates = request.POST.getlist('start_date[]')
        end_dates = request.POST.getlist('end_date[]')
        for position_id, start_date, end_date in zip(position_ids, start_dates, end_dates):
            position = get_object_or_404(JobPosition, id=position_id)
            if end_date:
                end_date = end_date
            else:
                end_date = None
            StaffPosition.objects.create(
                staff = staff,
                position = position,
                start_date = start_date,
                end_date = end_date
            )
        return redirect('userdetails', staff_id=staff.staff_id)


    context = {
        'staff' : staff,
        'positions': positions,
        'jobpositions': jobpositions,
    }
    return render(request, 'details_templates/staff-details.html', context)


@staff_login_required
def taskdetails(request, task_id):
    task = get_object_or_404(Task, task_id=task_id)
    points = Point.objects.filter(task=task).order_by('-id')

    context = {
        'task': task,
        'points' : points,

    }

    return render(request, 'details_templates/task-details.html', context)


@staff_login_required
def show_points_modal(request, task_id):
    task = get_object_or_404(Task, task_id=task_id)
    points = Point.objects.filter(task=task).order_by('-id')
    context = {
        'task' : task,
        'points' : points,

    }
    return render(request, 'details_templates/showpoints-modal.html', context)

    
@staff_login_required
def timeline_modal(request, task_id):
    task = Task.objects.get(task_id=task_id)
    point_activities = PointActivity.objects.filter(point__task=task)

    # Dictionary to store total time worked on each day
    daily_totals = {}

    for activity in point_activities:
        date = activity.start_time.date()
        hours, minutes, seconds = activity.total_time_in_hours_minutes_seconds()
        total_time = timedelta(hours=hours, minutes=minutes, seconds=seconds)

        # Add the total time to the corresponding day's total
        if date in daily_totals:
            daily_totals[date] += total_time
        else:
            daily_totals[date] = total_time

    sorted_daily_totals = sorted(daily_totals.items())
    formatted_totals = []
    for date, total_time in sorted_daily_totals:
        total_hours = total_time.days * 24 + total_time.seconds // 3600
        total_minutes = (total_time.seconds % 3600) // 60
        total_seconds = total_time.seconds % 60
        formatted_totals.append((date, total_hours, total_minutes, total_seconds))
    context = {
        'task': task,
        'sorted_daily_totals': sorted_daily_totals,
        'formatted_totals':formatted_totals,
    }
    return render(request, 'details_templates/timeline-modal.html', context)



@staff_login_required
def projectdetails(request, project_id):
    project = get_object_or_404(Project, project_id=project_id)
    is_pinned = project.is_pinned(request.user)
    epics = Epic.objects.filter(project=project).order_by('-id')

    project_notes = Note.objects.filter(project=project).order_by('-id')
    statuses = ProjectStatus.objects.filter(project=project).order_by('-id')

    all_tickets = Ticket.objects.filter(project=project)
    all_tickets_with_update_date = all_tickets.annotate(
    latest_update_date=Max('ticketupdate__date_added'))
    all_tickets_ordered = all_tickets_with_update_date.order_by('-latest_update_date')


    members = list(project.members.all())

    if project.manager and project.manager in members:
        members.remove(project.manager)
        members.insert(0, project.manager)

    for member in members:
        tasks = project.task_set.filter(assigned_to=member)
        total_time_seconds = 0
        for task in tasks:
            total_time_hours, total_time_minutes, total_time_seconds_task = task.total_task_time()
            total_time_seconds += (total_time_hours * 3600) + (total_time_minutes * 60) + total_time_seconds_task

        total_time_hours = total_time_seconds // 3600
        total_time_minutes = (total_time_seconds % 3600) // 60
        total_time_seconds = total_time_seconds % 60

        member.total_time_worked_hours = total_time_hours
        member.total_time_worked_minutes = total_time_minutes
        member.total_time_worked_seconds = total_time_seconds

    context = {
        'project': project,
        'epics': epics,
        'project_notes' : project_notes,
        'members': members,
        'statuses' : statuses,
        'all_tickets_ordered': all_tickets_ordered,
        'is_pinned': is_pinned,
    }

    return render(request, 'details_templates/project-details.html', context)




def chat_room(request, chat_id):
    chat_room = get_object_or_404(ChatRoom, id=chat_id)
    chat_messages = ChatMessage.objects.filter(room=chat_room).order_by('date_sent')
    guest_session_id =   chat_room.chatroomguest.visitor.session_id
    return render(request, 'chat_templates/chat-room.html', {'chat_room': chat_room, 'chat_messages': chat_messages, 'guest_session_id': guest_session_id})




@staff_login_required
def reaction_details_modal(request):

    context = {

    }

    return render(request, 'details_templates/reaction-details-modal.html', context)







#FETCH EPIC RELATED TASKS
@staff_login_required
def get_tasks(request, epic_id):
    epic = get_object_or_404(Epic, id=epic_id)
    related_tasks = Task.objects.filter(epic=epic).order_by('-id')

    context = {
        'epic': epic,
        'related_tasks': related_tasks,
    }

    return render(request, 'epic-fetched-tasks.html', context)


# TO DISPALY ALL THE OPEN TASKS OF THIS PROJECT
@staff_login_required
def open_tasks_for_project(request, project_id):
    project = Project.objects.get(pk=project_id)
    open_tasks = Task.objects.filter(
        Q(project=project, status='Open') | Q(project=project, status='Working On')
    ).order_by('-id')

    context = {
        'project': project,
        'open_tasks': open_tasks,
    }

    return render(request, 'project-open-tasks.html', context)


    
# TO FETCH THE EPICS OF THE SELECTED PROJECT WHEN EDITING A TASK
@staff_login_required
def fetch_epics(request):
    project_id = request.GET.get('project_id')
    epics = Epic.objects.filter(project_id=project_id).values('id', 'title')
    return JsonResponse({'epics': list(epics)})





@staff_login_required
def recent_activities_page(request): 

    context = {

    }

    return render(request, 'recent-activities-page.html', context)






#Forgot Password Views
from django.contrib.auth.tokens import PasswordResetTokenGenerator
def forgot_password(request):
    if request.method == 'POST':
        email = request.POST.get('email')
        user = User.objects.filter(email=email).first()
        if user:
            token = PasswordResetTokenGenerator().make_token(user)
            current_site = get_current_site(request)
        
            uid = urlsafe_base64_encode(force_bytes(user.pk))
            reset_link = f"https://{current_site.domain}/reset-password/{uid}/{token}/"

            mail_subject = 'Reset your password'
            message = render_to_string('email_templates/forgot_password_email.html', {
                'user': user,
                'reset_link': reset_link,
            })
            print(reset_link)
            send_mail(mail_subject, message, settings.EMAIL_HOST_USER, [user.email], html_message=message)
            return render(request, 'forgot-password-sent.html')
        else:
            messages.error(request, 'No account is associated with this email.')
    return render(request, 'forgot-password.html')
     

def reset_password(request, uidb64, token):
    try:
        uid = urlsafe_base64_decode(uidb64).decode()
        user = User.objects.get(pk=uid)
    except (TypeError, ValueError, OverflowError, User.DoesNotExist):
        user = None

    if user and PasswordResetTokenGenerator().check_token(user, token):
        if request.method == 'POST':
            new_password = request.POST.get('new_password')
            confirm_password = request.POST.get('confirm_password')

            if new_password == confirm_password:
                user.set_password(new_password)
                user.save()
                user = authenticate(request, username=user.username, password=new_password)
                if user is not None:
                    login(request, user)
                    return redirect('home')  
                else:
                    return HttpResponse('Authentication failed!')
            else:
                return render(request, 'forgot-password-confirmation.html')
        else:
            return render(request, 'forgot-password-confirmation.html')
    else:
        return HttpResponse('Activation link is invalid!')
    


def fetch_projects_by_status(request, status=None):
    user = request.user

    if user.is_superuser:
        projects = Project.objects.all().order_by('-project_id')
    else:
        projects = Project.objects.filter( Q(manager=user.staffprofile) | Q(members=user.staffprofile)).distinct().order_by('-project_id')


    if status:
        filtered_projects = []
        for project in projects:
            last_status = ProjectStatus.objects.filter(project = project).last()
            if last_status.status == status:
                filtered_projects.append(project)
    else:
        filtered_projects = projects

    
    latest_pinned = PinnedProject.objects.filter(user=request.user)[:4]
    latest_pinned_project_ids = [pinned.project.id for pinned in latest_pinned]
    latest_pinned_projects = [pinned.project for pinned in latest_pinned]
    for project in latest_pinned_projects:
        total_time = project.total_time_worked(request.user)
        project.total_time_worked_hours = total_time['hours']
        project.total_time_worked_minutes = total_time['minutes']
        project.total_time_worked_seconds = total_time['seconds']

        project.open_user_tasks_count = project.open_tasks_count(request.user)


    
    filtered_projects = [project for project in filtered_projects if project.id not in latest_pinned_project_ids] #Exclude pinned projects
    for project in filtered_projects:
        total_time = project.total_time_worked(request.user)
        project.total_time_worked_hours = total_time['hours']
        project.total_time_worked_minutes = total_time['minutes']
        project.total_time_worked_seconds = total_time['seconds']

        project.open_user_tasks_count = project.open_tasks_count(request.user)

    context = {
        'filtered_projects': filtered_projects,
        'latest_pinned_projects': latest_pinned_projects,
        'status': status,
    }
    return render(request, 'projects-by-status.html', context)



