from __future__ import annotations

import pytest
from django.contrib.auth.models import Permission
from django.urls import reverse

from apps.alumni.profiles.models import AlumniProfile
from apps.alumni.careers.models import JobApplication, JobListing


@pytest.fixture
def alumni_listing(db, alumni_user):
    return JobListing.objects.create(
        title="Climate Resilience Lead",
        company="EcoSeed Labs",
        location="Kampala, UG",
        description="Lead climate resilience projects with smallholder farmers.",
        sector="Climate & Environment",
        posted_by=alumni_user,
        audience=JobListing.Audience.ALUMNI,
        listing_status=JobListing.ListingStatus.PUBLISHED,
    )


@pytest.fixture
def all_audience_listing(db, alumni_user):
    return JobListing.objects.create(
        title="Agronomy Researcher",
        company="MillerLab",
        location="Nairobi, KE",
        description="Research roles for agronomy professionals.",
        sector="Seed Systems & Agronomy",
        posted_by=alumni_user,
        audience=JobListing.Audience.ALL,
        listing_status=JobListing.ListingStatus.PUBLISHED,
    )


@pytest.fixture
def learner_only_listing(db):
    # Posted by a distinct employer (NOT the alumnus who views it in tests) so
    # the audience gate is tested for a genuine non-owner — an owner can always
    # open their own listing.
    from django.contrib.auth import get_user_model

    employer = get_user_model().objects.create_user(
        email="employer.miller@example.com", password="pw"
    )
    return JobListing.objects.create(
        title="Junior Trainee",
        company="MillerLab",
        location="Nairobi, KE",
        description="Trainee placement scoped to REP learners.",
        sector="Education & Training",
        posted_by=employer,
        audience=JobListing.Audience.LEARNERS,
        listing_status=JobListing.ListingStatus.PUBLISHED,
    )


@pytest.mark.django_db
def test_anonymous_redirected_to_login(client):
    resp = client.get(reverse("alumni_jobs:board"))
    assert resp.status_code in {302, 301}


@pytest.mark.django_db
def test_alumni_sees_alumni_and_all_listings_but_not_learner_only(
    client, alumni_user, alumni_listing, all_audience_listing, learner_only_listing
):
    client.force_login(alumni_user)
    resp = client.get(reverse("alumni_jobs:board"))
    assert resp.status_code == 200
    titles = [L.title for L in resp.context["listings"]]
    assert alumni_listing.title in titles
    assert all_audience_listing.title in titles
    assert learner_only_listing.title not in titles


@pytest.mark.django_db
def test_match_sort_ranks_by_expertise_overlap(
    client, alumni_user, alumni_listing, all_audience_listing
):
    AlumniProfile.objects.create(
        user=alumni_user,
        expertise_tags=["agronomy", "seed"],
    )
    client.force_login(alumni_user)
    resp = client.get(reverse("alumni_jobs:board"))
    listings = list(resp.context["listings"])
    # The agronomy-tagged listing should rank ahead of the climate one.
    assert listings[0].title == all_audience_listing.title
    # Match-reason badge surfaces the overlapping tag for the top hit.
    assert getattr(listings[0], "match_reason", "") in {"agronomy", "seed"}
    body = resp.content.decode()
    assert "Matches your" in body


@pytest.mark.django_db
def test_apply_post_creates_application_and_appears_in_my(
    client, alumni_user, alumni_listing
):
    client.force_login(alumni_user)
    resp = client.post(
        reverse("alumni_jobs:detail", kwargs={"pk": alumni_listing.pk}),
        {"cover_letter": "I'd love to lead this team."},
    )
    assert resp.status_code in {302, 301}
    assert JobApplication.objects.filter(
        applicant=alumni_user, listing=alumni_listing
    ).exists()
    resp2 = client.get(reverse("alumni_jobs:my_applications"))
    assert resp2.status_code == 200
    apps = list(resp2.context["applications"])
    assert len(apps) == 1
    assert apps[0].listing_id == alumni_listing.pk


@pytest.mark.django_db
def test_detail_view_404s_for_learner_only_listing(
    client, alumni_user, learner_only_listing
):
    client.force_login(alumni_user)
    resp = client.get(reverse("alumni_jobs:detail", kwargs={"pk": learner_only_listing.pk}))
    assert resp.status_code == 404


@pytest.mark.django_db
def test_plain_alumni_without_partner_entry_cannot_submit_job(client, alumni_user):
    client.force_login(alumni_user)
    resp = client.get(reverse("alumni_jobs:submit"))
    assert resp.status_code == 403


@pytest.mark.django_db
def test_post_job_listing_permission_grants_submit_access(client, alumni_user):
    perm = Permission.objects.get(
        content_type__app_label="rep_career", codename="post_job_listing"
    )
    alumni_user.user_permissions.add(perm)
    client.force_login(alumni_user)

    resp = client.get(reverse("alumni_jobs:submit"))
    assert resp.status_code == 200

    resp2 = client.post(
        reverse("alumni_jobs:submit"),
        {
            "title": "Agronomy Field Officer",
            "company": "GreenGrow Partners",
            "location": "Kampala, UG",
            "job_type": JobListing.JobType.FULL_TIME,
            "sector": "Agriculture / Food Systems",
            "salary_range": "",
            "description": "Support smallholder outreach.",
            "requirements": "",
            "closes_at": "",
        },
    )
    assert resp2.status_code in {302, 301}
    listing = JobListing.objects.get(title="Agronomy Field Officer")
    assert listing.posted_by_id == alumni_user.pk
    assert listing.audience == JobListing.Audience.ALUMNI
    assert listing.listing_status == JobListing.ListingStatus.PENDING


@pytest.mark.django_db
def test_apply_records_application(client, alumni_listing, alumni_user):
    """An eligible alumnus can apply to a published listing (process completes)."""
    client.force_login(alumni_user)
    resp = client.post(
        reverse("alumni_jobs:detail", args=[alumni_listing.pk]),
        {"cover_letter": "I would love to apply."},
        follow=True,
    )
    assert resp.status_code == 200
    assert JobApplication.objects.filter(
        listing=alumni_listing, applicant=alumni_user
    ).exists()


@pytest.mark.django_db
def test_duplicate_application_is_graceful_not_500(client, alumni_listing, alumni_user):
    """Re-applying must show an error, not crash (regression: self.object unset)."""
    client.force_login(alumni_user)
    url = reverse("alumni_jobs:detail", args=[alumni_listing.pk])
    client.post(url, {"cover_letter": "first"})
    resp = client.post(url, {"cover_letter": "again"})
    assert resp.status_code == 200  # re-render, not AttributeError 500
    assert "already applied" in resp.content.decode().lower()
    assert (
        JobApplication.objects.filter(
            listing=alumni_listing, applicant=alumni_user
        ).count()
        == 1
    )


@pytest.mark.django_db
def test_owner_can_view_own_pending_listing(client, alumni_user):
    """A partner must be able to open their own pending listing (no 404)."""
    listing = JobListing.objects.create(
        title="Pending Submission",
        company="GreenGrow",
        description="d",
        posted_by=alumni_user,
        audience=JobListing.Audience.ALUMNI,
        listing_status=JobListing.ListingStatus.PENDING,
        is_active=False,
    )
    client.force_login(alumni_user)
    resp = client.get(reverse("alumni_jobs:detail", args=[listing.pk]))
    assert resp.status_code == 200  # regression: used to 404
    assert resp.context["is_owner"] is True
    assert resp.context["can_apply"] is False  # can't apply to your own pending listing


@pytest.mark.django_db
def test_non_owner_cannot_view_pending_listing(client, alumni_user):
    from django.contrib.auth import get_user_model

    owner = get_user_model().objects.create_user(email="owner@example.com", password="pw")
    listing = JobListing.objects.create(
        title="Someone Else Pending",
        company="X",
        description="d",
        posted_by=owner,
        audience=JobListing.Audience.ALUMNI,
        listing_status=JobListing.ListingStatus.PENDING,
        is_active=False,
    )
    client.force_login(alumni_user)
    resp = client.get(reverse("alumni_jobs:detail", args=[listing.pk]))
    assert resp.status_code == 404  # not the owner, not published → hidden
