"""Repeatable "Academic qualifications" / "Work experience" entries on the
account profile (UserProfile.qualifications / UserProfile.experience — both
free-form JSON lists of dicts, not separate models). Popup-based add/edit/
delete UI, US-common-app-style ("add as many as apply"), requested after the
profile-completeness gate shipped: the applicant already has to fill in a
single highest-degree-level to apply, so let them enrich the rest of their
academic/professional history the same way, in as many entries as they need.

Covers:
- QualificationEntryForm / ExperienceEntryForm validation
- qualification_save_view / qualification_delete_view (add, edit-in-place,
  delete, ordering, ownership scoping, auth requirement, method restriction)
- experience_save_view / experience_delete_view (same coverage)
"""
import pytest
from django.urls import reverse

from apps.core.authentication.forms import ExperienceEntryForm, QualificationEntryForm
from apps.core.authentication.models import UserProfile


# ── Form validation ──


def test_qualification_entry_form_requires_institution_and_qualification():
    form = QualificationEntryForm(data={"institution": "", "qualification": "", "field_of_study": "", "year": ""})
    assert not form.is_valid()
    assert "institution" in form.errors
    assert "qualification" in form.errors


def test_qualification_entry_form_accepts_minimal_valid_data():
    form = QualificationEntryForm(
        data={"institution": "Makerere University", "qualification": "BSc Agriculture", "field_of_study": "", "year": ""}
    )
    assert form.is_valid(), form.errors


def test_experience_entry_form_rejects_end_year_before_start_year():
    form = ExperienceEntryForm(
        data={
            "organization": "RUFORUM",
            "title": "Programme Officer",
            "start_year": "2020",
            "end_year": "2018",
            "is_current": "",
            "description": "",
        }
    )
    assert not form.is_valid()
    assert "__all__" in form.errors or form.non_field_errors()


def test_experience_entry_form_clears_end_year_when_current():
    form = ExperienceEntryForm(
        data={
            "organization": "RUFORUM",
            "title": "Programme Officer",
            "start_year": "2020",
            "end_year": "2022",
            "is_current": "on",
            "description": "",
        }
    )
    assert form.is_valid(), form.errors
    assert form.cleaned_data["end_year"] is None
    assert form.cleaned_data["is_current"] is True


# ── qualification_save_view / qualification_delete_view ──


@pytest.mark.django_db
def test_qualification_save_view_adds_entry_with_stable_id(client, django_user_model):
    user = django_user_model.objects.create_user(email="qual@example.com", password="pw")
    client.force_login(user)

    resp = client.post(
        reverse("accounts:qualification_save"),
        {"institution": "Makerere University", "qualification": "BSc Agriculture", "field_of_study": "Agronomy", "year": "2019"},
    )
    assert resp.status_code == 302
    assert resp.url == reverse("accounts:profile")

    user.profile.refresh_from_db()
    entries = user.profile.qualifications
    assert len(entries) == 1
    assert entries[0]["institution"] == "Makerere University"
    assert entries[0]["field_of_study"] == "Agronomy"
    assert entries[0]["year"] == 2019
    assert entries[0]["id"]  # a uuid4 hex was assigned


@pytest.mark.django_db
def test_qualification_save_view_supports_multiple_entries(client, django_user_model):
    """The whole point of this feature: not capped at one entry."""
    user = django_user_model.objects.create_user(email="multi-qual@example.com", password="pw")
    client.force_login(user)

    client.post(
        reverse("accounts:qualification_save"),
        {"institution": "Makerere University", "qualification": "BSc Agriculture", "field_of_study": "", "year": "2015"},
    )
    client.post(
        reverse("accounts:qualification_save"),
        {"institution": "University of Nairobi", "qualification": "MSc Agricultural Economics", "field_of_study": "", "year": "2019"},
    )
    client.post(
        reverse("accounts:qualification_save"),
        {"institution": "Wageningen University", "qualification": "PhD Development Studies", "field_of_study": "", "year": "2023"},
    )

    user.profile.refresh_from_db()
    entries = user.profile.qualifications
    assert len(entries) == 3
    # sorted newest-year-first
    assert [e["year"] for e in entries] == [2023, 2019, 2015]


@pytest.mark.django_db
def test_qualification_save_view_edits_existing_entry_in_place(client, django_user_model):
    user = django_user_model.objects.create_user(email="edit-qual@example.com", password="pw")
    client.force_login(user)

    client.post(
        reverse("accounts:qualification_save"),
        {"institution": "Makerere University", "qualification": "BSc Agriculture", "field_of_study": "", "year": "2015"},
    )
    user.profile.refresh_from_db()
    entry_id = user.profile.qualifications[0]["id"]

    client.post(
        reverse("accounts:qualification_save"),
        {
            "entry_id": entry_id,
            "institution": "Makerere University",
            "qualification": "BSc Agriculture (Honours)",
            "field_of_study": "Soil Science",
            "year": "2016",
        },
    )
    user.profile.refresh_from_db()
    entries = user.profile.qualifications
    assert len(entries) == 1, "editing must not create a duplicate row"
    assert entries[0]["id"] == entry_id
    assert entries[0]["qualification"] == "BSc Agriculture (Honours)"
    assert entries[0]["year"] == 2016


@pytest.mark.django_db
def test_qualification_save_view_invalid_data_does_not_touch_list(client, django_user_model):
    user = django_user_model.objects.create_user(email="bad-qual@example.com", password="pw")
    client.force_login(user)

    resp = client.post(reverse("accounts:qualification_save"), {"institution": "", "qualification": "", "field_of_study": "", "year": ""})
    assert resp.status_code == 302
    user.profile.refresh_from_db()
    assert user.profile.qualifications == []


@pytest.mark.django_db
def test_qualification_delete_view_removes_only_target_entry(client, django_user_model):
    user = django_user_model.objects.create_user(email="del-qual@example.com", password="pw")
    client.force_login(user)

    client.post(
        reverse("accounts:qualification_save"),
        {"institution": "Makerere University", "qualification": "BSc Agriculture", "field_of_study": "", "year": "2015"},
    )
    client.post(
        reverse("accounts:qualification_save"),
        {"institution": "University of Nairobi", "qualification": "MSc Agricultural Economics", "field_of_study": "", "year": "2019"},
    )
    user.profile.refresh_from_db()
    entries = user.profile.qualifications
    assert len(entries) == 2
    keep_id = next(e["id"] for e in entries if e["year"] == 2019)
    remove_id = next(e["id"] for e in entries if e["year"] == 2015)

    resp = client.post(reverse("accounts:qualification_delete", kwargs={"entry_id": remove_id}))
    assert resp.status_code == 302
    user.profile.refresh_from_db()
    remaining = user.profile.qualifications
    assert len(remaining) == 1
    assert remaining[0]["id"] == keep_id


@pytest.mark.django_db
def test_qualification_save_view_requires_login(client):
    resp = client.post(reverse("accounts:qualification_save"), {"institution": "X", "qualification": "Y", "field_of_study": "", "year": ""})
    assert resp.status_code == 302
    assert resp.url.startswith(reverse("accounts:login"))


@pytest.mark.django_db
def test_qualification_save_view_rejects_get(client, django_user_model):
    user = django_user_model.objects.create_user(email="get-qual@example.com", password="pw")
    client.force_login(user)
    resp = client.get(reverse("accounts:qualification_save"))
    assert resp.status_code == 405



@pytest.mark.django_db
def test_qualification_save_view_only_touches_own_profile(client, django_user_model):
    """entry_id addressing is scoped to request.user's own list — one user's
    save/delete calls can never reach into another user's qualifications."""
    user_a = django_user_model.objects.create_user(email="a@example.com", password="pw")
    user_b = django_user_model.objects.create_user(email="b@example.com", password="pw")

    client.force_login(user_a)
    client.post(
        reverse("accounts:qualification_save"),
        {"institution": "A's University", "qualification": "BSc", "field_of_study": "", "year": "2015"},
    )
    user_a.profile.refresh_from_db()
    entry_id = user_a.profile.qualifications[0]["id"]

    client.force_login(user_b)
    client.post(reverse("accounts:qualification_delete", kwargs={"entry_id": entry_id}))

    user_a.profile.refresh_from_db()
    user_b.profile.refresh_from_db()
    assert len(user_a.profile.qualifications) == 1, "user B's delete call must not affect user A's entries"
    assert user_b.profile.qualifications == []


# ── experience_save_view / experience_delete_view ──


@pytest.mark.django_db
def test_experience_save_view_adds_entry(client, django_user_model):
    user = django_user_model.objects.create_user(email="exp@example.com", password="pw")
    client.force_login(user)

    resp = client.post(
        reverse("accounts:experience_save"),
        {
            "organization": "RUFORUM",
            "title": "Programme Officer",
            "start_year": "2020",
            "end_year": "",
            "is_current": "on",
            "description": "Managed grant calls.",
        },
    )
    assert resp.status_code == 302
    user.profile.refresh_from_db()
    entries = user.profile.experience
    assert len(entries) == 1
    assert entries[0]["organization"] == "RUFORUM"
    assert entries[0]["is_current"] is True
    assert entries[0]["end_year"] is None
    assert entries[0]["id"]


@pytest.mark.django_db
def test_experience_save_view_supports_multiple_entries(client, django_user_model):
    user = django_user_model.objects.create_user(email="multi-exp@example.com", password="pw")
    client.force_login(user)

    client.post(
        reverse("accounts:experience_save"),
        {"organization": "Org One", "title": "Intern", "start_year": "2016", "end_year": "2017", "is_current": "", "description": ""},
    )
    client.post(
        reverse("accounts:experience_save"),
        {"organization": "Org Two", "title": "Analyst", "start_year": "2018", "end_year": "2020", "is_current": "", "description": ""},
    )
    user.profile.refresh_from_db()
    assert len(user.profile.experience) == 2


@pytest.mark.django_db
def test_experience_save_view_edits_existing_entry_in_place(client, django_user_model):
    user = django_user_model.objects.create_user(email="edit-exp@example.com", password="pw")
    client.force_login(user)

    client.post(
        reverse("accounts:experience_save"),
        {"organization": "Org One", "title": "Intern", "start_year": "2016", "end_year": "2017", "is_current": "", "description": ""},
    )
    user.profile.refresh_from_db()
    entry_id = user.profile.experience[0]["id"]

    client.post(
        reverse("accounts:experience_save"),
        {
            "entry_id": entry_id,
            "organization": "Org One",
            "title": "Senior Intern",
            "start_year": "2016",
            "end_year": "2017",
            "is_current": "",
            "description": "Promoted mid-way.",
        },
    )
    user.profile.refresh_from_db()
    entries = user.profile.experience
    assert len(entries) == 1
    assert entries[0]["title"] == "Senior Intern"
    assert entries[0]["description"] == "Promoted mid-way."


@pytest.mark.django_db
def test_experience_delete_view_removes_only_target_entry(client, django_user_model):
    user = django_user_model.objects.create_user(email="del-exp@example.com", password="pw")
    client.force_login(user)

    client.post(
        reverse("accounts:experience_save"),
        {"organization": "Org One", "title": "Intern", "start_year": "2016", "end_year": "2017", "is_current": "", "description": ""},
    )
    client.post(
        reverse("accounts:experience_save"),
        {"organization": "Org Two", "title": "Analyst", "start_year": "2018", "end_year": "2020", "is_current": "", "description": ""},
    )
    user.profile.refresh_from_db()
    entries = user.profile.experience
    keep_id = next(e["id"] for e in entries if e["organization"] == "Org Two")
    remove_id = next(e["id"] for e in entries if e["organization"] == "Org One")

    client.post(reverse("accounts:experience_delete", kwargs={"entry_id": remove_id}))
    user.profile.refresh_from_db()
    remaining = user.profile.experience
    assert len(remaining) == 1
    assert remaining[0]["id"] == keep_id


@pytest.mark.django_db
def test_experience_save_view_requires_login(client):
    resp = client.post(reverse("accounts:experience_save"), {"organization": "X", "title": "Y"})
    assert resp.status_code == 302
    assert resp.url.startswith(reverse("accounts:login"))



# ── profile page renders the new sections ──


@pytest.mark.django_db
def test_profile_page_renders_qualifications_and_experience(client, django_user_model):
    user = django_user_model.objects.create_user(email="render@example.com", password="pw")
    profile = user.profile
    profile.qualifications = [
        {"id": "abc123", "institution": "Makerere University", "qualification": "BSc Agriculture", "field_of_study": "", "year": 2015}
    ]
    profile.experience = [
        {"id": "def456", "organization": "RUFORUM", "title": "Programme Officer", "start_year": 2020, "end_year": None, "is_current": True, "description": ""}
    ]
    profile.save()
    client.force_login(user)

    resp = client.get(reverse("accounts:profile"))
    assert resp.status_code == 200
    body = resp.content.decode()
    assert "Academic qualifications" in body
    assert "Work experience" in body
    assert "BSc Agriculture" in body
    assert "Programme Officer" in body
