import pytest
from unittest.mock import patch
from django.contrib.auth import get_user_model
from rest_framework.test import APIClient

from apps.chatbot.models import ChatMessage, ChatSession
from apps.chatbot.tools import list_projects, list_tasks
from apps.core.models import Permission, Role, UserRole
from apps.companies.models import Organization, OrganizationMembership
from apps.projects.models import Project, ProjectTeamMember
from apps.tasks.models import Task

User = get_user_model()


@pytest.fixture
def organization(db):
    return Organization.objects.create(name="Test Org")


@pytest.fixture
def staff_user(db, organization):
    u = User.objects.create_user(email="staff@example.com", password="pass123")
    organization.members.add(u)
    return u


@pytest.fixture
def manager_user(db, organization):
    user = User.objects.create_user(email="manager@example.com", password="pass123")
    organization.members.add(user)
    perm, _ = Permission.objects.get_or_create(domain="project", action="delete", defaults={"label": "Project: Delete"})
    task_perm, _ = Permission.objects.get_or_create(domain="tasks", action="delete", defaults={"label": "Tasks: Delete"})
    role = Role.objects.create(name="Manager", slug="manager-test")
    role.permissions.add(perm, task_perm)
    UserRole.objects.create(user=user, role=role, organization=organization)
    return user


@pytest.fixture
def own_project(db, staff_user, organization):
    project = Project.objects.create(organization=organization, name="Own Project")
    ProjectTeamMember.objects.create(project=project, user=staff_user, role="member")
    return project


@pytest.fixture
def other_project(db, organization):
    return Project.objects.create(organization=organization, name="Other Project")


@pytest.mark.django_db
class TestChatSessionModel:
    def test_session_str_defaults_to_id(self, staff_user):
        session = ChatSession.objects.create(user=staff_user)
        assert str(session.id) in str(session)

    def test_session_scoped_to_owner(self, staff_user, manager_user):
        ChatSession.objects.create(user=staff_user, title="staff session")
        ChatSession.objects.create(user=manager_user, title="manager session")
        assert ChatSession.objects.filter(user=staff_user).count() == 1


@pytest.mark.django_db
class TestChatSessionTenantIsolation:
    def test_session_list_is_paginated(self, staff_user, organization):
        sessions = [
            ChatSession(user=staff_user, organization=organization, title=f"Session {index}")
            for index in range(120)
        ]
        ChatSession.objects.bulk_create(sessions)
        ChatMessage.objects.bulk_create([
            ChatMessage(session=session, role="user", content=f"Message {index}")
            for index, session in enumerate(sessions)
        ])
        client = APIClient()
        client.force_authenticate(staff_user)

        response = client.get("/api/chatbot/sessions/")

        assert response.status_code == 200
        assert response.data["count"] == 120
        assert len(response.data["results"]) == 100

    def test_legacy_unscoped_session_is_not_visible_in_active_organization(self):
        organization_a = Organization.objects.create(name="Chatbot Org A")
        organization_b = Organization.objects.create(name="Chatbot Org B")
        user = User.objects.create_user(email="multi-org@chatbot-security.test", password="pass123")
        OrganizationMembership.objects.create(organization=organization_a, user=user)
        OrganizationMembership.objects.create(organization=organization_b, user=user)
        legacy_session = ChatSession.objects.create(user=user, title="Org A history")
        ChatMessage.objects.create(session=legacy_session, role="user", content="private")
        client = APIClient()
        session = client.session
        session["active_org_id"] = str(organization_b.pk)
        session.save()
        client.force_authenticate(user)

        response = client.get("/api/chatbot/sessions/")

        assert response.status_code == 200
        rows = response.data["results"] if "results" in response.data else response.data
        assert rows == []

    def test_assistant_tools_use_active_organization(self):
        organization_a = Organization.objects.create(name="Tool Org A")
        organization_b = Organization.objects.create(name="Tool Org B")
        user = User.objects.create_user(email="tool-user@chatbot-security.test", password="pass123")
        OrganizationMembership.objects.create(organization=organization_a, user=user)
        OrganizationMembership.objects.create(organization=organization_b, user=user)
        project_a = Project.objects.create(organization=organization_a, name="Private Org A Project")
        project_b = Project.objects.create(organization=organization_b, name="Active Org B Project")
        ProjectTeamMember.objects.create(project=project_a, user=user, role="member")
        ProjectTeamMember.objects.create(project=project_b, user=user, role="member")
        client = APIClient()
        session = client.session
        session["active_org_id"] = str(organization_b.pk)
        session.save()
        client.force_authenticate(user)

        def fake_completion(base_url, api_key, model, messages, tools=None):
            tool_messages = [message for message in messages if message["role"] == "tool"]
            if not tool_messages:
                return {
                    "content": "",
                    "tool_calls": [{
                        "id": "projects-1",
                        "function": {"name": "list_projects", "arguments": "{}"},
                    }],
                }
            projects = __import__("json").loads(tool_messages[-1]["content"])
            return {"content": projects[0]["name"]}

        with patch("apps.chatbot.views._resolve_ai_config", return_value=("key", "https://ai.test", "model")), patch(
            "apps.chatbot.views._chat_completion", side_effect=fake_completion
        ):
            response = client.post(
                "/api/chatbot/sessions/send/",
                {"content": "Which project is active?"},
                format="json",
            )

        assert response.status_code == 201, response.content
        assert response.data["messages"][-1]["content"] == "Active Org B Project"


@pytest.mark.django_db
class TestListProjectsScoping:
    def test_staff_sees_only_own_projects(self, staff_user, own_project, other_project):
        result = list_projects(staff_user, own_project.organization)
        names = {p["name"] for p in result}
        assert names == {"Own Project"}

    def test_manager_sees_all_projects(self, manager_user, own_project, other_project):
        result = list_projects(manager_user, own_project.organization)
        names = {p["name"] for p in result}
        assert names == {"Own Project", "Other Project"}


@pytest.mark.django_db
class TestListTasksScoping:
    def test_staff_sees_only_own_tasks(self, staff_user, own_project, other_project, organization):
        Task.objects.create(organization=organization, title="Mine", project=own_project, created_by=staff_user, assignee=staff_user)
        other_owner = User.objects.create_user(email="other@example.com", password="pass123")
        Task.objects.create(organization=organization, title="Not mine", project=other_project, created_by=other_owner, assignee=other_owner)

        result = list_tasks(staff_user, organization)
        titles = {t["title"] for t in result}
        assert titles == {"Mine"}

    def test_manager_sees_all_tasks(self, manager_user, staff_user, own_project, other_project, organization):
        Task.objects.create(organization=organization, title="Mine", project=own_project, created_by=staff_user, assignee=staff_user)
        Task.objects.create(organization=organization, title="Not mine", project=other_project, created_by=staff_user)

        result = list_tasks(manager_user, organization)
        titles = {t["title"] for t in result}
        assert titles == {"Mine", "Not mine"}

    def test_archived_tasks_excluded_by_default(self, staff_user, own_project, organization):
        Task.objects.create(organization=organization, title="Active", project=own_project, created_by=staff_user, assignee=staff_user, status="todo")
        Task.objects.create(organization=organization, title="Archived", project=own_project, created_by=staff_user, assignee=staff_user, status="archived")

        result = list_tasks(staff_user, organization)
        titles = {t["title"] for t in result}
        assert titles == {"Active"}
