# -*- coding: utf-8 -*-
# Copyright (c) 2025 ContextForge Contributors.
# SPDX-License-Identifier: Apache-2.0

"""Location: ./tests/playwright/teams/test_team_invitations.py
Copyright 2026
SPDX-License-Identifier: Apache-2.0
Authors: Mihai Criveti

Team Invitation E2E Tests.

Tests the invitation workflow: send, list, accept, cancel, and permission checks.
"""

# Future
from __future__ import annotations

# Standard
import logging
import uuid

# Third-Party
from playwright.sync_api import APIRequestContext, Playwright

# Local
from .conftest import _make_jwt, BASE_URL, create_test_user, delete_test_user

logger = logging.getLogger(__name__)


class TestTeamInvitations:
    """Test team invitation send/accept/cancel workflow."""

    def test_send_invitation(self, admin_api: APIRequestContext, private_team: dict):
        """Team owner can send an invitation to a user."""
        email = f"invite-send-{uuid.uuid4().hex[:8]}@example.com"
        create_test_user(admin_api, email)

        resp = admin_api.post(
            f"/teams/{private_team['id']}/invitations",
            data={"email": email, "role": "member"},
        )
        assert resp.status in (200, 201)
        inv = resp.json()
        assert inv["email"] == email
        assert inv["role"] == "member"
        assert inv["token"], "Invitation should include a token"
        assert inv["is_active"] is True

        # Cleanup
        admin_api.delete(f"/teams/invitations/{inv['id']}")
        delete_test_user(admin_api, email)

    def test_list_pending_invitations(self, admin_api: APIRequestContext, private_team: dict):
        """Team owner can list pending invitations."""
        email = f"invite-list-{uuid.uuid4().hex[:8]}@example.com"
        create_test_user(admin_api, email)
        inv_resp = admin_api.post(f"/teams/{private_team['id']}/invitations", data={"email": email, "role": "member"})
        assert inv_resp.status in (200, 201), f"Failed to create invitation: {inv_resp.status} {inv_resp.text()}"

        resp = admin_api.get(f"/teams/{private_team['id']}/invitations")
        assert resp.status == 200
        invitations = resp.json()
        assert isinstance(invitations, list)
        emails = [i["email"] for i in invitations]
        assert email in emails

        # Cleanup
        inv_id = next(i["id"] for i in invitations if i["email"] == email)
        admin_api.delete(f"/teams/invitations/{inv_id}")
        delete_test_user(admin_api, email)

    def test_accept_invitation(self, admin_api: APIRequestContext, private_team: dict, playwright: Playwright):
        """Invited user can accept an invitation and become a team member."""
        email = f"invite-accept-{uuid.uuid4().hex[:8]}@example.com"
        create_test_user(admin_api, email)

        # Send invitation
        inv_resp = admin_api.post(f"/teams/{private_team['id']}/invitations", data={"email": email, "role": "member"})
        assert inv_resp.status in (200, 201), f"Failed to create invitation: {inv_resp.status} {inv_resp.text()}"
        invitation_token = inv_resp.json()["token"]

        # Accept as the invited user
        user_jwt = _make_jwt(email, is_admin=False)
        user_ctx = playwright.request.new_context(
            base_url=BASE_URL,
            extra_http_headers={"Authorization": f"Bearer {user_jwt}", "Accept": "application/json"},
        )
        accept_resp = user_ctx.post(f"/teams/invitations/{invitation_token}/accept")
        user_ctx.dispose()
        assert accept_resp.status == 200

        # Verify user is now a member
        members_resp = admin_api.get(f"/teams/{private_team['id']}/members")
        members = members_resp.json()
        member_list = members if isinstance(members, list) else members.get("members", [])
        member_emails = [m["user_email"] for m in member_list]
        assert email in member_emails

        # Cleanup
        admin_api.delete(f"/teams/{private_team['id']}/members/{email}")
        delete_test_user(admin_api, email)

    def test_cancel_invitation(self, admin_api: APIRequestContext, private_team: dict):
        """Team owner can cancel a pending invitation."""
        email = f"invite-cancel-{uuid.uuid4().hex[:8]}@example.com"
        create_test_user(admin_api, email)
        inv_resp = admin_api.post(f"/teams/{private_team['id']}/invitations", data={"email": email, "role": "member"})
        assert inv_resp.status in (200, 201), f"Failed to create invitation: {inv_resp.status} {inv_resp.text()}"
        inv_id = inv_resp.json()["id"]

        # Cancel the invitation
        cancel_resp = admin_api.delete(f"/teams/invitations/{inv_id}")
        assert cancel_resp.status == 200

        # Verify invitation is no longer listed
        list_resp = admin_api.get(f"/teams/{private_team['id']}/invitations")
        inv_ids = [i["id"] for i in list_resp.json()]
        assert inv_id not in inv_ids

        delete_test_user(admin_api, email)

    def test_non_owner_cannot_invite(self, admin_api: APIRequestContext, private_team: dict, playwright: Playwright):
        """A non-owner member cannot send invitations."""
        # Create and add a member
        member_email = f"member-noinvite-{uuid.uuid4().hex[:8]}@example.com"
        invitee_email = f"target-noinvite-{uuid.uuid4().hex[:8]}@example.com"
        create_test_user(admin_api, member_email)
        create_test_user(admin_api, invitee_email)

        # Add member via invitation
        inv_resp = admin_api.post(f"/teams/{private_team['id']}/invitations", data={"email": member_email, "role": "member"})
        assert inv_resp.status in (200, 201), f"Failed to create invitation: {inv_resp.status} {inv_resp.text()}"
        inv_token = inv_resp.json()["token"]
        member_jwt = _make_jwt(member_email, is_admin=False)
        member_ctx = playwright.request.new_context(
            base_url=BASE_URL,
            extra_http_headers={"Authorization": f"Bearer {member_jwt}", "Accept": "application/json"},
        )
        member_ctx.post(f"/teams/invitations/{inv_token}/accept")

        # Try to invite as member (should be denied)
        invite_resp = member_ctx.post(
            f"/teams/{private_team['id']}/invitations",
            data={"email": invitee_email, "role": "member"},
        )
        member_ctx.dispose()
        assert invite_resp.status in (403, 422), f"Non-owner should be denied invite, got {invite_resp.status}"

        # Cleanup
        admin_api.delete(f"/teams/{private_team['id']}/members/{member_email}")
        delete_test_user(admin_api, member_email)
        delete_test_user(admin_api, invitee_email)
