"""Mentorship meeting-request flow (entrepreneur → mentor).

Covers:
* entrepreneur can request a meeting with a verified mentor (+ notification)
* duplicate pending request to the same mentor is rejected
* mentor accepts (with link) / declines (with note) — requester notified
* requester can cancel a pending request; only pending ones
* RBAC: inbox is mentor-only, responding requires the recipient mentor
"""
from __future__ import annotations

import pytest
from django.contrib.auth import get_user_model
from django.test import Client
from django.urls import reverse

from apps.core.notifications.models import Notification
from apps.core.permissions.roles import UserRole
from apps.smehub.onboarding import services
from apps.smehub.onboarding.models import MeetingRequest, MentorProfile

User = get_user_model()
pytestmark = pytest.mark.django_db


@pytest.fixture
def entrepreneur():
    return User.objects.create_user(
        email="founder@example.com", password="x", role=UserRole.ENTREPRENEUR,
    )


@pytest.fixture
def mentor_user():
    return User.objects.create_user(
        email="mentor@example.com", password="x", role=UserRole.MENTOR,
    )


@pytest.fixture
def mentor(mentor_user):
    return MentorProfile.objects.create(
        user=mentor_user,
        country="Uganda",
        verification_status=MentorProfile.Status.VERIFIED,
    )


@pytest.fixture
def client_for():
    def _login(user):
        c = Client(SERVER_NAME="localhost")
        c.force_login(user)
        return c
    return _login


# ── service layer ──────────────────────────────────────────────────────────

def test_request_meeting_creates_record_and_notifies(entrepreneur, mentor):
    mr = services.request_mentor_meeting(
        requester=entrepreneur, mentor=mentor, agenda="Pricing strategy review",
    )
    assert mr.status == MeetingRequest.Status.REQUEST
    assert mr.recipient == mentor
    assert Notification.objects.filter(
        recipient=mentor.user, verb=Notification.Verb.SMEHUB_MEETING_REQUESTED,
    ).exists()


def test_duplicate_pending_request_rejected(entrepreneur, mentor):
    services.request_mentor_meeting(requester=entrepreneur, mentor=mentor, agenda="A")
    with pytest.raises(services.MeetingRequestError):
        services.request_mentor_meeting(requester=entrepreneur, mentor=mentor, agenda="B")


def test_unverified_mentor_not_requestable(entrepreneur, mentor):
    MentorProfile.objects.filter(pk=mentor.pk).update(
        verification_status=MentorProfile.Status.PENDING_VERIFICATION)
    # refresh_from_db cannot write a protected FSMField — re-fetch instead.
    mentor = MentorProfile.objects.get(pk=mentor.pk)
    with pytest.raises(services.MeetingRequestError):
        services.request_mentor_meeting(requester=entrepreneur, mentor=mentor, agenda="A")


def test_accept_sets_link_and_notifies_requester(entrepreneur, mentor):
    mr = services.request_mentor_meeting(requester=entrepreneur, mentor=mentor, agenda="A")
    services.respond_meeting_request(
        mr, by_user=mentor.user, accept=True, meeting_link="https://meet.example.com/x",
    )
    mr.refresh_from_db()
    assert mr.status == MeetingRequest.Status.ACCEPTED
    assert mr.meeting_link == "https://meet.example.com/x"
    assert Notification.objects.filter(
        recipient=entrepreneur, verb=Notification.Verb.SMEHUB_MEETING_ACCEPTED,
    ).exists()


def test_decline_records_reason(entrepreneur, mentor):
    mr = services.request_mentor_meeting(requester=entrepreneur, mentor=mentor, agenda="A")
    services.respond_meeting_request(mr, by_user=mentor.user, accept=False, reason="Full calendar")
    mr.refresh_from_db()
    assert mr.status == MeetingRequest.Status.REJECTED
    assert mr.reason == "Full calendar"
    assert Notification.objects.filter(
        recipient=entrepreneur, verb=Notification.Verb.SMEHUB_MEETING_DECLINED,
    ).exists()


def test_respond_twice_rejected(entrepreneur, mentor):
    mr = services.request_mentor_meeting(requester=entrepreneur, mentor=mentor, agenda="A")
    services.respond_meeting_request(mr, by_user=mentor.user, accept=True)
    with pytest.raises(services.MeetingRequestError):
        services.respond_meeting_request(mr, by_user=mentor.user, accept=False)


def test_cancel_own_pending_only(entrepreneur, mentor, mentor_user):
    mr = services.request_mentor_meeting(requester=entrepreneur, mentor=mentor, agenda="A")
    with pytest.raises(services.MeetingRequestError):
        services.cancel_meeting_request(mr, by_user=mentor_user)
    services.cancel_meeting_request(mr, by_user=entrepreneur)
    mr.refresh_from_db()
    assert mr.status == MeetingRequest.Status.CANCELLED
    with pytest.raises(services.MeetingRequestError):
        services.cancel_meeting_request(mr, by_user=entrepreneur)


# ── views ──────────────────────────────────────────────────────────────────

def test_request_meeting_via_view(entrepreneur, mentor, client_for):
    c = client_for(entrepreneur)
    url = reverse("smehub_onboarding:mentor_request_meeting", kwargs={"pk": mentor.pk})
    resp = c.post(url, {"agenda": "Market entry advice"})
    assert resp.status_code == 302
    assert MeetingRequest.objects.filter(requester=entrepreneur).count() == 1


def test_mentor_detail_shows_request_form(entrepreneur, mentor, client_for):
    c = client_for(entrepreneur)
    resp = c.get(reverse("smehub_onboarding:mentor_detail", kwargs={"pk": mentor.pk}))
    assert resp.status_code == 200
    assert b"Request a meeting" in resp.content


def test_my_requests_page_lists_and_cancels(entrepreneur, mentor, client_for):
    mr = services.request_mentor_meeting(requester=entrepreneur, mentor=mentor, agenda="A")
    c = client_for(entrepreneur)
    resp = c.get(reverse("smehub_onboarding:my_meeting_requests"))
    assert resp.status_code == 200
    assert mentor.user.email.encode() in resp.content
    resp = c.post(reverse("smehub_onboarding:meeting_request_cancel", kwargs={"pk": mr.pk}))
    assert resp.status_code == 302
    mr.refresh_from_db()
    assert mr.status == MeetingRequest.Status.CANCELLED


def test_inbox_mentor_only(entrepreneur, mentor, client_for):
    url = reverse("smehub_onboarding:mentor_meeting_inbox")
    assert client_for(entrepreneur).get(url).status_code == 403
    assert client_for(mentor.user).get(url).status_code == 200


def test_mentor_responds_via_view(entrepreneur, mentor, client_for):
    mr = services.request_mentor_meeting(requester=entrepreneur, mentor=mentor, agenda="A")
    c = client_for(mentor.user)
    resp = c.post(
        reverse("smehub_onboarding:meeting_request_respond", kwargs={"pk": mr.pk}),
        {"decision": "accept", "meeting_link": "https://meet.example.com/y"},
    )
    assert resp.status_code == 302
    mr.refresh_from_db()
    assert mr.status == MeetingRequest.Status.ACCEPTED
    assert mr.meeting_link == "https://meet.example.com/y"


def test_other_mentor_cannot_respond(entrepreneur, mentor, client_for):
    other = User.objects.create_user(
        email="mentor2@example.com", password="x", role=UserRole.MENTOR,
    )
    MentorProfile.objects.create(
        user=other, country="Kenya",
        verification_status=MentorProfile.Status.VERIFIED,
    )
    mr = services.request_mentor_meeting(requester=entrepreneur, mentor=mentor, agenda="A")
    resp = client_for(other).post(
        reverse("smehub_onboarding:meeting_request_respond", kwargs={"pk": mr.pk}),
        {"decision": "accept"},
    )
    assert resp.status_code == 404
    mr.refresh_from_db()
    assert mr.status == MeetingRequest.Status.REQUEST
