import mimetypes
import os

from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.db.models import Count, Q
from django.http import FileResponse, Http404
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils import timezone
from django.views.decorators.http import require_http_methods

from accounts.decorators import seeker_required
from accounts.profile_setup import unique_slug
from companies.models import Company
from home.pagination import paginate
from jobs.category_data import category_form_choices, numbered_category_label
from jobs.models import JobApplication, JobCategory, SavedJob
from jobs.recommendations import category_browse_url, recommended_jobs_for_profile
from subscriptions.access import user_has_seeker_plus
from subscriptions.usage import get_seeker_benefits, record_cv_download

from accounts.visibility import get_visible_seeker_or_404, visible_seekers

from .forms import SeekerFilterForm, SeekerProfileForm
from .models import ProfileView, SeekerProfile
from .profile_access import FULL, resolve_seeker_profile_access, viewer_sees_full_seeker_listings

_PROFILE_COMPLETION_FIELDS = (
    'full_name',
    'headline',
    'job_category_id',
    'location',
    'years_experience',
    'experience_level',
    'availability',
    'skills',
    'bio',
    'education',
    'experience_summary',
    'cv_file',
)

_PROFILE_FORM_FULL_WIDTH = {
    'cover_image',
    'skills',
    'bio',
    'education',
    'experience_summary',
    'cv_file',
    'clear_cover_image',
}


def _seeker_subscription_plan(user):
    subscription = getattr(user, 'seeker_subscription', None)
    if subscription and subscription.is_valid:
        return subscription.plan
    return None


def _new_completion(profile):
    if not profile:
        return None
    from .completion import completion
    return completion(profile)['percent']


def _profile_completion(profile):
    if not profile:
        return None
    filled = 0
    for field_name in _PROFILE_COMPLETION_FIELDS:
        value = getattr(profile, field_name, None)
        if value not in (None, ''):
            filled += 1
    total = len(_PROFILE_COMPLETION_FIELDS)
    return round(filled / total * 100) if total else 0


def _filter_seekers(queryset, form):
    q = form.cleaned_data.get('q')
    location = form.cleaned_data.get('location')
    industry = form.cleaned_data.get('industry')
    categories = form.cleaned_data.get('category')
    experience_levels = form.cleaned_data.get('experience_level')
    availability = form.cleaned_data.get('availability')
    sort = form.cleaned_data.get('sort') or 'featured'

    if q:
        queryset = queryset.filter(
            Q(full_name__icontains=q)
            | Q(headline__icontains=q)
            | Q(skills__icontains=q)
            | Q(category__icontains=q)
        )
    if location:
        queryset = queryset.filter(location__icontains=location)
    if industry:
        queryset = queryset.filter(category__icontains=industry)
    if categories:
        queryset = queryset.filter(
            Q(category_slug__in=categories) | Q(job_category__slug__in=categories)
        )
    if experience_levels:
        queryset = queryset.filter(experience_level__in=experience_levels)
    if availability:
        queryset = queryset.filter(availability__in=availability)

    if sort == 'experience':
        queryset = queryset.order_by('-years_experience', 'full_name')
    elif sort == 'recent':
        queryset = queryset.order_by('-updated_at')
    elif sort == 'name':
        queryset = queryset.order_by('full_name')
    else:
        queryset = queryset.order_by('-is_featured', '-tier', '-years_experience')

    return queryset


def _cv_filename(profile):
    return os.path.basename(profile.cv_file.name)


def _cv_is_pdf(profile):
    name = profile.cv_file.name.lower()
    return name.endswith('.pdf')


def _cv_file_response(profile, *, as_attachment):
    content_type, _encoding = mimetypes.guess_type(profile.cv_file.name)
    content_type = content_type or 'application/octet-stream'
    response = FileResponse(
        profile.cv_file.open('rb'),
        as_attachment=as_attachment,
        filename=_cv_filename(profile),
        content_type=content_type,
    )
    if not as_attachment:
        response['Content-Disposition'] = f'inline; filename="{_cv_filename(profile)}"'
    return response


def seeker_list(request):
    queryset = visible_seekers()
    category_choices = category_form_choices()
    form = SeekerFilterForm(request.GET or None, category_choices=category_choices)

    if form.is_valid():
        queryset = _filter_seekers(queryset, form)

    page_obj, query_string, page_range = paginate(request, queryset, per_page=12)

    category_filters = [
        {
            'value': slug,
            'label': label,
            'count': visible_seekers().filter(
                Q(category_slug=slug) | Q(job_category__slug=slug)
            ).count(),
        }
        for slug, label in category_choices
    ]
    experience_filters = [
        {
            'value': choice[0],
            'label': choice[1],
            'count': visible_seekers().filter(experience_level=choice[0]).count(),
        }
        for choice in SeekerProfile.ExperienceLevel.choices
    ]
    availability_filters = [
        {
            'value': choice[0],
            'label': choice[1],
            'count': visible_seekers().filter(availability=choice[0]).count(),
        }
        for choice in SeekerProfile.Availability.choices
    ]

    context = {
        'seekers': page_obj,
        'form': form,
        'page_obj': page_obj,
        'query_string': query_string,
        'page_range': page_range,
        'total_count': page_obj.paginator.count,
        'category_filters': category_filters,
        'experience_filters': experience_filters,
        'availability_filters': availability_filters,
        'show_full_seeker_details': viewer_sees_full_seeker_listings(request),
    }
    return render(request, 'seekers/list.html', context)


def seeker_detail(request, slug):
    profile = get_visible_seeker_or_404(request, slug)
    profile_access = resolve_seeker_profile_access(request, profile)

    similar_q = Q(category_slug=profile.category_slug)
    if profile.job_category_id:
        similar_q |= Q(job_category=profile.job_category)
    similar = visible_seekers().filter(similar_q).exclude(pk=profile.pk)[:4]
    if similar.count() < 4:
        similar = visible_seekers().exclude(pk=profile.pk)[:4]

    can_message_seeker = profile_access['can_message_seeker']
    message_start_url = (
        reverse('messaging:start_direct', kwargs={'slug': profile.slug})
        if can_message_seeker else ''
    )

    context = {
        'profile': profile,
        'similar_seekers': similar,
        'profile_access': profile_access,
        'can_download_cv': profile_access['can_download_cv'],
        'can_view_cv': profile_access['can_view_cv'],
        'can_message_seeker': can_message_seeker,
        'message_start_url': message_start_url,
        'show_full_seeker_details': profile_access['level'] == FULL,
    }
    return render(request, 'seekers/public_profile.html', context)


@login_required
def view_cv(request, slug):
    profile = get_visible_seeker_or_404(request, slug)
    if not profile.cv_file:
        raise Http404

    profile_access = resolve_seeker_profile_access(request, profile)
    if not profile_access['can_view_cv']:
        if profile_access['message']:
            messages.error(request, profile_access['message'])
        else:
            messages.error(request, 'You do not have permission to view this CV.')
        return redirect('seekers:detail', slug=slug)

    return render(request, 'seekers/cv_view.html', {
        'profile': profile,
        'can_download_cv': profile_access['can_download_cv'],
        'cv_is_pdf': _cv_is_pdf(profile),
    })


@login_required
def serve_cv(request, slug):
    profile = get_visible_seeker_or_404(request, slug)
    if not profile.cv_file:
        raise Http404

    profile_access = resolve_seeker_profile_access(request, profile)
    if not profile_access['can_view_cv']:
        raise Http404

    return _cv_file_response(profile, as_attachment=False)


@login_required
def download_cv(request, slug):
    profile = get_visible_seeker_or_404(request, slug)
    if not profile.cv_file:
        raise Http404

    profile_access = resolve_seeker_profile_access(request, profile)
    if not profile_access['can_download_cv']:
        if profile_access['message']:
            messages.error(request, profile_access['message'])
        else:
            messages.error(request, 'CV downloads require a Professional plan or higher.')
        return redirect('seekers:detail', slug=slug)

    if not request.user.is_platform_admin:
        company = Company.objects.filter(owner=request.user, is_active=True).first()
        if company:
            record_cv_download(company, profile, request.user)

    return _cv_file_response(profile, as_attachment=True)


@login_required
@seeker_required
@require_http_methods(['GET', 'POST'])
def seeker_dashboard(request):
    profile = SeekerProfile.objects.filter(user=request.user).first()

    if profile:
        form = SeekerProfileForm(request.POST or None, request.FILES or None, instance=profile)
    else:
        full_name = f'{request.user.first_name} {request.user.last_name}'.strip() or request.user.username
        form = SeekerProfileForm(
            request.POST or None,
            request.FILES or None,
            initial={
                'full_name': full_name,
                'headline': 'Job Seeker',
                'location': 'Lusaka',
                'is_public': False,
                'is_online': True,
            },
        )

    if request.method == 'POST' and form.is_valid():
        profile = form.save(commit=False)
        profile.user = request.user
        if not profile.slug:
            profile.slug = unique_slug(SeekerProfile, profile.full_name)
        profile.save()
        messages.success(request, 'Your profile has been updated.')
        return redirect('seekers:dashboard')

    saved_jobs_qs = SavedJob.objects.filter(user=request.user)
    applications_qs = JobApplication.objects.filter(applicant=request.user)
    followed_companies_qs = Company.objects.filter(
        followers__user=request.user,
        is_active=True,
        is_public=True,
    )

    saved_jobs = saved_jobs_qs.select_related('job').order_by('-created_at')[:8]
    applications = applications_qs.select_related('job').order_by('-created_at')[:8]

    has_job_alerts = user_has_seeker_plus(request.user)
    recommended_jobs = recommended_jobs_for_profile(profile, limit=6) if profile else []
    category_browse_link = (
        category_browse_url(profile.job_category.slug)
        if profile and profile.job_category_id else ''
    )
    benefits = get_seeker_benefits(request.user)
    profile_insights = None
    if benefits.profile_insights and profile and profile.user_id:
        profile_insights = {
            'views': ProfileView.objects.filter(profile_user=profile.user).count(),
        }

    context = {
        'profile': profile,
        'form': form,
        'saved_jobs': saved_jobs,
        'applications': applications,
        'applications_count': applications_qs.count(),
        'saved_jobs_count': saved_jobs_qs.count(),
        'followed_companies_count': followed_companies_qs.count(),
        'job_alerts_active': has_job_alerts,
        'recommended_jobs': recommended_jobs,
        'category_browse_link': category_browse_link,
        'seeker_benefits': benefits,
        'profile_insights': profile_insights,
        'profile_completion': _new_completion(profile),
        'followed_companies': followed_companies_qs.order_by('name')[:12],
        'seeker_nav': 'dashboard',
        'seeker_subscription_plan': _seeker_subscription_plan(request.user),
        'full_width_fields': _PROFILE_FORM_FULL_WIDTH,
    }
    return render(request, 'seekers/dashboard.html', context)


@login_required
@seeker_required
def seeker_applications(request):
    base_qs = (
        JobApplication.objects.filter(applicant=request.user)
        .select_related('job')
        .prefetch_related('interviews')
        .order_by('-created_at')
    )

    status = request.GET.get('status', '')
    q = request.GET.get('q', '').strip()
    if status in dict(JobApplication.Status.choices):
        base_qs = base_qs.filter(status=status)
    if q:
        base_qs = base_qs.filter(
            Q(job__title__icontains=q)
            | Q(job__company_name__icontains=q)
            | Q(job__location__icontains=q)
        )

    benefits = get_seeker_benefits(request.user)
    if not benefits.full_application_history:
        base_qs = base_qs[:5]

    status_counts = {
        row['status']: row['count']
        for row in JobApplication.objects.filter(applicant=request.user)
        .values('status')
        .annotate(count=Count('id'))
    }
    status_tabs = [
        {'value': value, 'label': label, 'count': status_counts.get(value, 0)}
        for value, label in JobApplication.Status.choices
    ]

    page_obj, query_string, page_range = paginate(request, base_qs, per_page=15)

    profile = SeekerProfile.objects.filter(user=request.user).first()
    total_applications = JobApplication.objects.filter(applicant=request.user).count()

    return render(request, 'seekers/applications.html', {
        'applications': page_obj,
        'page_obj': page_obj,
        'query_string': query_string,
        'page_range': page_range,
        'status_choices': JobApplication.Status.choices,
        'status_tabs': status_tabs,
        'current_status': status,
        'search_query': q,
        'full_application_history': benefits.full_application_history,
        'profile': profile,
        'applications_count': total_applications,
        'seeker_nav': 'applications',
        'seeker_subscription_plan': _seeker_subscription_plan(request.user),
    })



@login_required
@seeker_required
def saved_jobs(request):
    q = request.GET.get('q', '').strip()
    saved = SavedJob.objects.filter(user=request.user).select_related('job', 'job__category')
    if q:
        saved = saved.filter(Q(job__title__icontains=q) | Q(job__company_name__icontains=q) | Q(job__location__icontains=q))
    page_obj, query_string, page_range = paginate(request, saved, per_page=12)
    profile = SeekerProfile.objects.filter(user=request.user).first()
    return render(request, 'seekers/saved_jobs.html', {
        'saved_jobs': page_obj,
        'page_obj': page_obj,
        'query_string': query_string,
        'page_range': page_range,
        'search_query': q,
        'total_saved': SavedJob.objects.filter(user=request.user).count(),
        'profile': profile,
        'applications_count': JobApplication.objects.filter(applicant=request.user).count(),
        'seeker_nav': 'saved',
        'seeker_subscription_plan': _seeker_subscription_plan(request.user),
    })
