"""Tests for Activity Feed functionality"""

import pytest
from datetime import datetime, timedelta
from app.models import Activity, User, Project, Task, TimeEntry, Client
from app import db


class TestActivityModel:
    """Tests for the Activity model"""

    def test_activity_creation(self, app, test_user, test_project):
        """Test creating an activity log entry"""
        with app.app_context():
            activity = Activity(
                user_id=test_user.id,
                action="created",
                entity_type="project",
                entity_id=test_project.id,
                entity_name=test_project.name,
                description=f'Created project "{test_project.name}"',
            )
            db.session.add(activity)
            db.session.commit()

            assert activity.id is not None
            assert activity.user_id == test_user.id
            assert activity.action == "created"
            assert activity.entity_type == "project"
            assert activity.entity_id == test_project.id
            assert activity.created_at is not None

    def test_activity_log_method(self, app, test_user, test_project):
        """Test the Activity.log() class method"""
        with app.app_context():
            Activity.log(
                user_id=test_user.id,
                action="updated",
                entity_type="project",
                entity_id=test_project.id,
                entity_name=test_project.name,
                description=f'Updated project "{test_project.name}"',
                extra_data={"field": "name"},
            )

            activity = Activity.query.filter_by(
                user_id=test_user.id, entity_type="project", entity_id=test_project.id
            ).first()

            assert activity is not None
            assert activity.action == "updated"
            assert activity.extra_data == {"field": "name"}

    def test_activity_get_recent(self, app, test_user, test_project):
        """Test getting recent activities"""
        with app.app_context():
            # Create multiple activities
            for i in range(5):
                Activity.log(
                    user_id=test_user.id,
                    action="updated",
                    entity_type="project",
                    entity_id=test_project.id,
                    entity_name=test_project.name,
                    description=f"Action {i}",
                )

            # Get recent activities
            activities = Activity.get_recent(user_id=test_user.id, limit=3)

            assert len(activities) == 3
            assert activities[0].description == "Action 4"  # Most recent first

    def test_activity_filter_by_entity_type(self, app, test_user, test_project, test_task):
        """Test filtering activities by entity type"""
        with app.app_context():
            # Create activities for different entity types
            Activity.log(
                user_id=test_user.id,
                action="created",
                entity_type="project",
                entity_id=test_project.id,
                entity_name=test_project.name,
                description="Project created",
            )

            Activity.log(
                user_id=test_user.id,
                action="created",
                entity_type="task",
                entity_id=test_task.id,
                entity_name=test_task.name,
                description="Task created",
            )

            # Filter by entity type
            project_activities = Activity.get_recent(user_id=test_user.id, entity_type="project")

            task_activities = Activity.get_recent(user_id=test_user.id, entity_type="task")

            assert len(project_activities) == 1
            assert project_activities[0].entity_type == "project"
            assert len(task_activities) == 1
            assert task_activities[0].entity_type == "task"

    def test_activity_to_dict(self, app, test_user, test_project):
        """Test converting activity to dictionary"""
        with app.app_context():
            Activity.log(
                user_id=test_user.id,
                action="created",
                entity_type="project",
                entity_id=test_project.id,
                entity_name=test_project.name,
                description="Test activity",
            )

            activity = Activity.query.filter_by(user_id=test_user.id).first()
            activity_dict = activity.to_dict()

            assert activity_dict["id"] == activity.id
            assert activity_dict["user_id"] == test_user.id
            assert activity_dict["action"] == "created"
            assert activity_dict["entity_type"] == "project"
            assert activity_dict["entity_id"] == test_project.id
            assert activity_dict["description"] == "Test activity"
            assert "created_at" in activity_dict

    def test_activity_get_icon(self, app, test_user, test_project):
        """Test getting icon for different activity types"""
        with app.app_context():
            actions = ["created", "updated", "deleted", "started", "stopped"]

            for action in actions:
                Activity.log(
                    user_id=test_user.id,
                    action=action,
                    entity_type="project",
                    entity_id=test_project.id,
                    entity_name=test_project.name,
                    description=f"{action} project",
                )

                activity = Activity.query.filter_by(action=action).first()
                icon = activity.get_icon()

                assert icon is not None
                assert "fas fa-" in icon


class TestActivityAPIEndpoints:
    """Tests for Activity Feed API endpoints"""

    def test_get_activities(self, authenticated_client, test_user, test_project):
        """Test GET /api/activities endpoint"""
        # Create some test activities
        with authenticated_client.application.app_context():
            for i in range(3):
                Activity.log(
                    user_id=test_user.id,
                    action="updated",
                    entity_type="project",
                    entity_id=test_project.id,
                    entity_name=test_project.name,
                    description=f"Activity {i}",
                )

        response = authenticated_client.get("/api/activities")
        assert response.status_code == 200

        data = response.get_json()
        assert "activities" in data
        assert len(data["activities"]) >= 3
        assert "total" in data
        assert "pages" in data

    def test_get_activities_with_entity_type_filter(self, authenticated_client, test_user, test_project, test_task):
        """Test filtering activities by entity type"""
        with authenticated_client.application.app_context():
            Activity.log(
                user_id=test_user.id,
                action="created",
                entity_type="project",
                entity_id=test_project.id,
                entity_name=test_project.name,
                description="Project activity",
            )

            Activity.log(
                user_id=test_user.id,
                action="created",
                entity_type="task",
                entity_id=test_task.id,
                entity_name=test_task.name,
                description="Task activity",
            )

        # Filter by project entity type
        response = authenticated_client.get("/api/activities?entity_type=project")
        assert response.status_code == 200

        data = response.get_json()
        assert all(act["entity_type"] == "project" for act in data["activities"])

    def test_get_activities_with_pagination(self, authenticated_client, test_user, test_project):
        """Test pagination of activities"""
        with authenticated_client.application.app_context():
            # Create 15 activities
            for i in range(15):
                Activity.log(
                    user_id=test_user.id,
                    action="updated",
                    entity_type="project",
                    entity_id=test_project.id,
                    entity_name=test_project.name,
                    description=f"Activity {i}",
                )

        # Get first page
        response = authenticated_client.get("/api/activities?limit=5&page=1")
        assert response.status_code == 200

        data = response.get_json()
        assert len(data["activities"]) == 5
        assert data["has_next"] is True

        # Get second page
        response = authenticated_client.get("/api/activities?limit=5&page=2")
        assert response.status_code == 200

        data = response.get_json()
        assert len(data["activities"]) == 5

    def test_get_activity_stats(self, authenticated_client, test_user, test_project, test_task):
        """Test GET /api/activities/stats endpoint"""
        with authenticated_client.application.app_context():
            # Create varied activities
            Activity.log(
                user_id=test_user.id,
                action="created",
                entity_type="project",
                entity_id=test_project.id,
                entity_name=test_project.name,
                description="Project created",
            )

            Activity.log(
                user_id=test_user.id,
                action="updated",
                entity_type="project",
                entity_id=test_project.id,
                entity_name=test_project.name,
                description="Project updated",
            )

            Activity.log(
                user_id=test_user.id,
                action="created",
                entity_type="task",
                entity_id=test_task.id,
                entity_name=test_task.name,
                description="Task created",
            )

        response = authenticated_client.get("/api/activities/stats")
        assert response.status_code == 200

        data = response.get_json()
        assert "total_activities" in data
        assert "entity_counts" in data
        assert "action_counts" in data
        assert data["total_activities"] >= 3


class TestActivityIntegration:
    """Tests for activity logging integration in routes"""

    def test_project_create_logs_activity(self, admin_authenticated_client, test_client):
        """Test that creating a project logs an activity"""
        with admin_authenticated_client.application.app_context():
            # Count activities before
            before_count = Activity.query.count()

        response = admin_authenticated_client.post(
            "/projects/create",
            data={
                "name": "Test Activity Project",
                "client_id": test_client.id,
                "billable": "on",
                "description": "Test project for activity",
            },
            follow_redirects=False,
        )

        with admin_authenticated_client.application.app_context():
            # Check activity was logged
            after_count = Activity.query.count()
            assert after_count == before_count + 1

            activity = Activity.query.order_by(Activity.created_at.desc()).first()
            assert activity.action == "created"
            assert activity.entity_type == "project"
            assert "Test Activity Project" in activity.description

    def test_task_create_logs_activity(self, authenticated_client, test_project):
        """Test that creating a task logs an activity"""
        with authenticated_client.application.app_context():
            before_count = Activity.query.count()

        response = authenticated_client.post(
            "/tasks/create",
            data={
                "project_id": test_project.id,
                "name": "Test Activity Task",
                "priority": "high",
                "description": "Test task for activity",
            },
            follow_redirects=False,
        )

        with authenticated_client.application.app_context():
            after_count = Activity.query.count()
            assert after_count == before_count + 1

            activity = Activity.query.order_by(Activity.created_at.desc()).first()
            assert activity.action == "created"
            assert activity.entity_type == "task"
            assert "Test Activity Task" in activity.description

    def test_timer_start_logs_activity(self, authenticated_client, test_project):
        """Test that starting a timer logs an activity"""
        with authenticated_client.application.app_context():
            before_count = Activity.query.count()

        response = authenticated_client.post(
            "/timer/start", data={"project_id": test_project.id, "notes": "Test timer"}, follow_redirects=False
        )

        with authenticated_client.application.app_context():
            after_count = Activity.query.count()
            assert after_count == before_count + 1

            activity = Activity.query.order_by(Activity.created_at.desc()).first()
            assert activity.action == "started"
            assert activity.entity_type == "time_entry"
            assert test_project.name in activity.description

    def test_timer_stop_logs_activity(self, authenticated_client, test_user, test_project):
        """Test that stopping a timer logs an activity"""
        with authenticated_client.application.app_context():
            # Create an active timer
            from app.models.time_entry import local_now

            timer = TimeEntry(user_id=test_user.id, project_id=test_project.id, start_time=local_now(), source="auto")
            db.session.add(timer)
            db.session.commit()

            before_count = Activity.query.count()

        response = authenticated_client.post("/timer/stop", follow_redirects=False)

        with authenticated_client.application.app_context():
            after_count = Activity.query.count()
            assert after_count == before_count + 1

            activity = Activity.query.order_by(Activity.created_at.desc()).first()
            assert activity.action == "stopped"
            assert activity.entity_type == "time_entry"
            assert test_project.name in activity.description


class TestActivityFeedDateParams:
    """Tests for activity_feed blueprint date parameter validation (/activity and /api/activity).
    Uses session_transaction to set user so requests are authenticated; activity_feed module must be enabled.
    """

    def test_api_activity_valid_date_params(self, app, client, user, test_project):
        """GET /api/activity with valid start_date and end_date returns 200 and applies filter."""
        with client.session_transaction() as sess:
            sess["_user_id"] = str(user.id)
            sess["_fresh"] = True
        with app.app_context():
            Activity.log(
                user_id=user.id,
                action="created",
                entity_type="project",
                entity_id=test_project.id,
                entity_name=test_project.name,
                description="Activity for date filter",
            )
        response = client.get("/api/activity?start_date=2024-01-01&end_date=2025-12-31")
        assert response.status_code == 200
        data = response.get_json()
        assert "activities" in data
        assert "pagination" in data

    def test_api_activity_invalid_start_date_returns_400(self, app, client, user):
        """GET /api/activity with invalid start_date returns 400 and error message."""
        with client.session_transaction() as sess:
            sess["_user_id"] = str(user.id)
            sess["_fresh"] = True
        response = client.get("/api/activity?start_date=not-a-date")
        assert response.status_code == 400
        data = response.get_json()
        assert data.get("error") == "Invalid parameter"
        assert "start_date" in data.get("message", "").lower() or "ISO 8601" in data.get("message", "")

    def test_api_activity_invalid_end_date_returns_400(self, app, client, user):
        """GET /api/activity with invalid end_date returns 400."""
        with client.session_transaction() as sess:
            sess["_user_id"] = str(user.id)
            sess["_fresh"] = True
        response = client.get("/api/activity?end_date=invalid")
        assert response.status_code == 400
        data = response.get_json()
        assert "message" in data

    def test_web_activity_invalid_date_filter_skipped_no_crash(self, app, client, user):
        """GET /activity (web) with invalid date param: date filter is skipped (no crash in route)."""
        with client.session_transaction() as sess:
            sess["_user_id"] = str(user.id)
            sess["_fresh"] = True
        response = client.get("/activity?start_date=not-a-date")
        # Route must not crash on invalid date (ValueError caught and logged); 500 may be template missing
        assert response.status_code in (200, 500)


class TestActivityWidget:
    """Tests for the activity feed widget on dashboard"""

    def test_dashboard_includes_activities(self, authenticated_client, test_user, test_project):
        """Test that the dashboard includes recent activities"""
        with authenticated_client.application.app_context():
            # Create some activities
            Activity.log(
                user_id=test_user.id,
                action="created",
                entity_type="project",
                entity_id=test_project.id,
                entity_name=test_project.name,
                description="Test activity",
            )

        response = authenticated_client.get("/dashboard")
        assert response.status_code == 200
        assert b"Recent Activity" in response.data
        assert b"Test activity" in response.data
