from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.db.models import Case, Count, IntegerField, Value, When
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.views.decorators.http import require_http_methods, require_POST

from accounts.decorators import company_required, employer_or_admin_required
from companies.models import Company
from home.pagination import paginate
from jobs.models import Job, JobApplication, JobAttachment
from jobs.attachments import save_job_attachments
from jobs.status import closed_jobs_q, open_jobs_q
from subscriptions.usage import can_company_access_cv_library, can_company_post_job, company_usage_summary, get_company_benefits

from companies.follow_notifications import (
    company_profile_changed,
    company_profile_snapshot,
    notify_followers_new_job,
    notify_followers_profile_updated,
)
from .access import apply_company_to_job, get_editable_attachment, get_editable_job
from .forms import AdminJobPostForm, ApplicationStatusForm, CompanyProfileForm, JobPostForm, ReactivateJobForm


def _get_company(user):
    return Company.objects.filter(owner=user).first()


def _job_form_class(user):
    if user.is_platform_admin:
        return AdminJobPostForm
    return JobPostForm


def _company_name_from_form(form, user):
    if user.is_platform_admin:
        return form.cleaned_data.get('company_name')
    return None


def _subscription_dashboard_context(company, cv_access_allowed):
    """Build subscription labels and flags for employer dashboard."""
    from subscriptions.models import SubscriptionPlan

    usage = company_usage_summary(company) if company else None
    sub = getattr(company, 'subscription', None) if company else None
    plan = None
    subscription_active = False
    if sub and sub.is_valid:
        subscription_active = True
        plan = sub.plan

    has_recruitment_subscription = bool(
        plan
        and plan.category == SubscriptionPlan.Category.RECRUITMENT
    )

    def limit_label(used, limit):
        if limit is None:
            return f'{used} · Unlimited'
        if limit == 0:
            return str(used)
        return f'{used} / {limit}'

    usage_labels = None
    if usage:
        benefits = usage['benefits']
        usage_labels = {
            'jobs': limit_label(usage['jobs_posted'], benefits.job_posts_per_month),
            'profile_views': limit_label(
                usage['profile_views'], benefits.cv_profile_views_per_month
            ),
            'cv_downloads': limit_label(
                usage['cv_downloads'], benefits.cv_downloads_per_month
            ),
        }

    return {
        'subscription_usage': usage,
        'subscription_usage_labels': usage_labels,
        'subscription_plan': plan,
        'subscription_active': subscription_active,
        'has_recruitment_subscription': has_recruitment_subscription,
    }


@login_required
@company_required
def employer_dashboard(request):
    company = Company.objects.filter(owner=request.user).select_related('subscription__plan').first()
    jobs_qs = Job.objects.filter(posted_by=request.user).annotate(
        application_count=Count('applications'),
        _ongoing_pin=Case(
            When(deadline__isnull=True, then=Value(0)),
            default=Value(1),
            output_field=IntegerField(),
        ),
    ).order_by('_ongoing_pin', '-created_at')
    total_applications = JobApplication.objects.filter(job__posted_by=request.user).count()
    active_listings = jobs_qs.filter(open_jobs_q())
    archived_qs = jobs_qs.filter(closed_jobs_q())
    active_jobs = active_listings.count()
    active_page_obj, _, _ = paginate(request, active_listings, per_page=10, page_param='page')
    archived_page_obj, query_string, page_range = paginate(
        request, archived_qs, per_page=10, page_param='archive_page'
    )
    cv_access_allowed, cv_access_message = can_company_access_cv_library(company, request.user)
    cv_library_stats = None
    if cv_access_allowed and company:
        from seekers.cv_library import get_cv_library_stats
        cv_library_stats = get_cv_library_stats(company)
    profile_progress = None
    if company:
        from companies.completion import completion
        profile_progress = completion(company)
    context = {
        'company': company,
        'profile_progress': profile_progress,
        'employer_nav': 'dashboard',
        'jobs': active_page_obj,
        'page_obj': active_page_obj,
        'archived_jobs': archived_page_obj,
        'archived_page_obj': archived_page_obj,
        'query_string': query_string,
        'page_range': page_range,
        'total_jobs': jobs_qs.count(),
        'active_jobs': active_jobs,
        'archived_jobs_count': archived_qs.count(),
        'total_applications': total_applications,
        'cv_access_allowed': cv_access_allowed,
        'cv_access_message': cv_access_message,
        'cv_library_stats': cv_library_stats,
    }
    context.update(_subscription_dashboard_context(company, cv_access_allowed))
    return render(request, 'employers/dashboard.html', context)


@login_required
@employer_or_admin_required
def post_job(request):
    if request.user.is_platform_admin:
        return redirect('platform_admin:post_job')

    company = _get_company(request.user)
    allowed, limit_message = can_company_post_job(company, request.user)
    form_class = _job_form_class(request.user)
    form = form_class(
        request.POST or None,
        request.FILES or None,
        company=company,
        user=request.user,
    )
    if request.method == 'POST':
        if not allowed:
            messages.error(request, limit_message)
            return redirect('employers:post_job')
        if form.is_valid():
            job = form.save(commit=False)
            job.posted_by = request.user
            apply_company_to_job(
                job,
                request.user,
                company,
                company_name=_company_name_from_form(form, request.user),
            )
            job.is_active = True
            benefits = get_company_benefits(company, user=request.user)
            if benefits.auto_feature_jobs:
                job.is_featured = True
            job.save()
            files = form.cleaned_data.get('attachment_files') or []
            if files:
                save_job_attachments(job, files)
            if company:
                notify_followers_new_job(company, job)
            messages.success(request, f'"{job.title}" was published successfully.')
            return redirect('employers:dashboard')
    return render(request, 'employers/post_job.html', {
        'form': form,
        'company': company,
        'can_post_job': allowed,
        'post_job_message': limit_message,
        'subscription_usage': company_usage_summary(company) if company else None,
    })


@login_required
@employer_or_admin_required
@require_http_methods(['GET', 'POST'])
def edit_job(request, slug):
    if request.user.is_platform_admin:
        return redirect('platform_admin:edit_job', slug=slug)

    company = _get_company(request.user)
    job = get_editable_job(request.user, slug)
    form_class = _job_form_class(request.user)
    form = form_class(
        request.POST or None,
        request.FILES or None,
        instance=job,
        company=company,
        user=request.user,
    )
    if request.method == 'POST' and form.is_valid():
        job = form.save(commit=False)
        apply_company_to_job(
            job,
            request.user,
            company,
            company_name=_company_name_from_form(form, request.user),
        )
        job.save()
        files = form.cleaned_data.get('attachment_files') or []
        if files:
            save_job_attachments(job, files, start_order=job.attachments.count())
        messages.success(request, f'"{job.title}" was updated successfully.')
        return redirect('employers:dashboard')
    return render(request, 'employers/edit_job.html', {
        'form': form,
        'company': company,
        'job': job,
        'attachments': job.attachments.all(),
        'delete_attachment_url_name': 'employers:delete_job_attachment',
        'cancel_url_name': 'employers:dashboard',
    })


@login_required
@employer_or_admin_required
@require_POST
def delete_job_attachment(request, pk):
    attachment = get_editable_attachment(request.user, pk)
    job_slug = attachment.job.slug
    attachment.file.delete(save=False)
    attachment.delete()
    messages.success(request, 'Attachment removed.')
    if request.user.is_platform_admin:
        return redirect('platform_admin:edit_job', slug=job_slug)
    return redirect('employers:edit_job', slug=job_slug)


MAX_GALLERY_PHOTOS = 24


def _company_context(company, section, extra=None):
    from companies.completion import SECTIONS, STEPS, completion
    from .company_forms import COUNTRIES, PROVINCES

    progress = completion(company)
    ctx = {
        'company': company,
        'employer_nav': 'company',
        'section': section,
        'sections': [
            {'key': key, 'label': label, 'icon': icon, 'done': progress['section_done'].get(key, False)}
            for key, label, icon in SECTIONS
        ],
        'progress': progress,
        'steps': [
            {'number': n, 'label': label, 'first': tabs[0], 'active': section in tabs,
             'done': all(progress['section_done'].get(t, True) for t in tabs) and n != 7,
             'past': section not in tabs and any(section in t2 for _, _, t2 in STEPS[n:])}
            for n, label, tabs in STEPS
        ],
        'provinces': PROVINCES,
        'countries': COUNTRIES,
    }
    ctx.update(extra or {})
    return ctx


def _section_url(section):
    return f"{reverse('employers:edit_company')}?section={section}"


def _after_save(request, section, company=None):
    """Save and continue later goes to the dashboard; save and continue jumps to the
    next section that still has something missing, not just the next one in order -
    a finished section is skipped so the employer always lands on unfinished work."""
    then = request.POST.get('then')
    if then == 'later':
        return redirect('employers:dashboard')
    if then == 'next':
        from companies.completion import SECTIONS, completion

        keys = [k for k, _, _ in SECTIONS]
        index = keys.index(section)
        ordered = keys[index + 1:] + keys[:index + 1]
        section_done = completion(company)['section_done'] if company else {}
        next_section = next((k for k in ordered if not section_done.get(k, True)), None)
        if next_section:
            section = next_section
        elif index + 1 < len(keys):
            section = keys[index + 1]
    return redirect(_section_url(section))


def _handle_products(request, company):
    from companies.models import CompanyProduct
    from .company_forms import ProductForm

    action = request.POST.get('action')
    if action == 'product_delete':
        product = get_object_or_404(CompanyProduct, pk=request.POST.get('product'), company=company)
        if product.image:
            product.image.delete(save=False)
        product.delete()
        messages.success(request, 'Product or service removed.')
        return None
    instance = None
    if action == 'product_update':
        instance = get_object_or_404(CompanyProduct, pk=request.POST.get('product'), company=company)
    form = ProductForm(request.POST, request.FILES, instance=instance)
    if form.is_valid():
        product = form.save(commit=False)
        product.company = company
        if not instance:
            product.position = company.products.count()
        product.save()
        messages.success(request, 'Product or service saved.')
        return None
    return form


def _handle_gallery(request, company):
    from django import forms as dj_forms

    from companies.models import CompanyPhoto

    if request.POST.get('action') == 'photo_delete':
        photo = get_object_or_404(CompanyPhoto, pk=request.POST.get('photo'), company=company)
        photo.image.delete(save=False)
        photo.delete()
        messages.success(request, 'Photo removed.')
        return
    field = dj_forms.ImageField()
    uploads = request.FILES.getlist('photos')
    if not uploads:
        messages.warning(request, 'Choose one or more photos to upload.')
        return
    added = 0
    room = MAX_GALLERY_PHOTOS - company.photos.count()
    for upload in uploads:
        if added >= room:
            messages.warning(request, f'The gallery holds up to {MAX_GALLERY_PHOTOS} photos.')
            break
        try:
            field.clean(upload)
        except dj_forms.ValidationError:
            messages.error(request, f'{upload.name} is not a valid image and was skipped.')
            continue
        CompanyPhoto.objects.create(company=company, image=upload)
        added += 1
    if added:
        messages.success(request, f'{added} photo{"s" if added != 1 else ""} added to your gallery.')


@login_required
@company_required
@require_http_methods(['GET', 'POST'])
def edit_company(request):
    from companies.completion import SECTIONS

    from .company_forms import SECTION_FORMS, ProductForm, SpecialisationForm

    company = _get_company(request.user)
    if not company:
        messages.warning(request, 'No company profile found. Contact support if this persists.')
        return redirect('employers:dashboard')

    valid = {k for k, _, _ in SECTIONS}
    section = request.POST.get('section') or request.GET.get('section') or 'info'
    if section not in valid:
        section = 'info'

    product_form = None
    form = None
    if request.method == 'POST':
        action = request.POST.get('action', '')
        if section == 'gallery':
            _handle_gallery(request, company)
            return _after_save(request, section, company) if request.POST.get('then') else redirect(_section_url('gallery'))
        if section == 'products' and action.startswith('product_'):
            product_form = _handle_products(request, company)
            if product_form is None:
                return redirect(_section_url('products'))
            form = SpecialisationForm(instance=company)
        else:
            form = SECTION_FORMS[section](request.POST, request.FILES, instance=company)
            if form.is_valid():
                before = company_profile_snapshot(company)
                updated = form.save()
                if company_profile_changed(before, updated):
                    notify_followers_profile_updated(updated)
                messages.success(request, 'Saved.')
                return _after_save(request, section, updated)
            messages.error(request, 'Please fix the highlighted fields and save again.')
    elif section in SECTION_FORMS:
        form = SECTION_FORMS[section](instance=company)

    extra = {
        'form': form,
        'product_form': product_form or ProductForm(),
        'products': company.products.all() if section == 'products' else [],
        'photos': company.photos.all() if section == 'gallery' else [],
        'max_photos': MAX_GALLERY_PHOTOS,
    }
    return render(request, 'employers/company_profile/edit.html', _company_context(company, section, extra))


@login_required
@company_required
def applications_list(request):
    return redirect('ats:pipeline')


@login_required
@company_required
@require_POST
def update_application_status(request, pk):
    application = get_object_or_404(
        JobApplication.objects.select_related('job', 'applicant'),
        pk=pk,
        job__posted_by=request.user,
    )
    old_status = application.status
    form = ApplicationStatusForm(request.POST, instance=application)
    if form.is_valid():
        new_status = form.cleaned_data['status']
        if new_status != old_status:
            from ats.services import change_application_status

            change_application_status(
                application,
                new_status,
                request.user,
                old_status=old_status,
            )
        messages.success(
            request,
            f'Application status updated to {application.get_status_display()}.',
        )
    else:
        messages.error(request, 'Could not update application status.')
    return redirect('ats:detail', pk=pk)


@login_required
@company_required
@require_POST
def toggle_job(request, slug):
    job = get_editable_job(request.user, slug)
    if not job.accepts_applications:
        if job.is_expired:
            messages.info(
                request,
                f'"{job.title}" closed automatically when its deadline passed. '
                'Use Reactivate to set a new date.',
            )
        else:
            messages.info(request, f'"{job.title}" is already closed. Use Reactivate to open it again.')
        return redirect('employers:dashboard')
    job.is_active = False
    job.save(update_fields=['is_active'])
    if job.is_ongoing:
        messages.success(
            request,
            f'"{job.title}" has been closed. Ongoing listings can only be reopened by your company.',
        )
    else:
        messages.success(request, f'"{job.title}" has been closed. It remains visible on the site as Closed.')
    return redirect('employers:dashboard')


@login_required
@company_required
@require_http_methods(['GET', 'POST'])
def reactivate_job(request, slug):
    company = _get_company(request.user)
    job = get_editable_job(request.user, slug)
    if job.accepts_applications:
        messages.info(request, f'"{job.title}" is already active.')
        return redirect('employers:dashboard')

    form = ReactivateJobForm(request.POST or None, instance=job)
    if request.method == 'POST' and form.is_valid():
        job = form.save(commit=False)
        job.is_active = True
        job.save()
        if job.deadline:
            messages.success(request, f'"{job.title}" has been reactivated with deadline {job.deadline:%b %d, %Y}.')
        else:
            messages.success(request, f'"{job.title}" has been reactivated as an ongoing listing.')
        return redirect('employers:dashboard')

    return render(request, 'employers/reactivate_job.html', {
        'form': form,
        'company': company,
        'job': job,
    })


@login_required
@company_required
def cv_library(request):
    from seekers.cv_library_views import cv_library_context

    ctx = cv_library_context(request, admin_mode=False)
    page_obj, query_string, page_range = paginate(request, ctx['records_queryset'], per_page=50)
    cv_library_stats = None
    if ctx.get('can_access') and ctx.get('company'):
        from seekers.cv_library import get_cv_library_stats
        cv_library_stats = get_cv_library_stats(ctx['company'])
    ctx.update({
        'page_obj': page_obj,
        'query_string': query_string,
        'page_range': page_range,
        'page_title': 'CV Library',
        'download_url': 'employers:cv_library_download',
        'single_download_url_name': 'employers:cv_library_download_one',
        'cv_library_stats': cv_library_stats,
        'pricing_talent_url': reverse('subscriptions:pricing') + '#talent',
    })
    return render(request, 'employers/cv_library.html', ctx)


@login_required
@company_required
@require_POST
def cv_library_download(request):
    from seekers.cv_library_views import handle_cv_download

    response = handle_cv_download(request, admin_mode=False)
    if response:
        return response
    return redirect('employers:cv_library')


@login_required
@company_required
def cv_library_download_one(request, pk):
    from seekers.cv_library_views import handle_single_cv_download

    return handle_single_cv_download(request, pk, admin_mode=False)
