"""
Comprehensive tests for Time Entry Templates feature.

This module tests:
- TimeEntryTemplate model functionality
- Time entry template routes (CRUD operations)
- Template usage tracking
- Integration with time entries
"""

import pytest
from datetime import datetime
from app.models import TimeEntryTemplate, User, Project, Task, TimeEntry
from app import db


# ============================================================================
# Model Tests
# ============================================================================


@pytest.mark.models
class TestTimeEntryTemplateModel:
    """Test TimeEntryTemplate model functionality"""

    def test_create_template_with_all_fields(self, app, user, project, task):
        """Test creating a template with all fields populated"""
        with app.app_context():
            template = TimeEntryTemplate(
                user_id=user.id,
                name="Daily Standup",
                description="Template for daily standup meetings",
                project_id=project.id,
                task_id=task.id,
                default_duration_minutes=15,
                default_notes="Discussed progress and blockers",
                tags="meeting,standup,daily",
                billable=True,
            )
            db.session.add(template)
            db.session.commit()

            # Verify all fields
            assert template.id is not None
            assert template.name == "Daily Standup"
            assert template.description == "Template for daily standup meetings"
            assert template.project_id == project.id
            assert template.task_id == task.id
            assert template.default_duration_minutes == 15
            assert template.default_notes == "Discussed progress and blockers"
            assert template.tags == "meeting,standup,daily"
            assert template.billable is True
            assert template.usage_count == 0
            assert template.last_used_at is None
            assert template.created_at is not None
            assert template.updated_at is not None

    def test_create_template_minimal_fields(self, app, user):
        """Test creating a template with only required fields"""
        with app.app_context():
            template = TimeEntryTemplate(user_id=user.id, name="Quick Task")
            db.session.add(template)
            db.session.commit()

            assert template.id is not None
            assert template.name == "Quick Task"
            assert template.project_id is None
            assert template.task_id is None
            assert template.default_duration_minutes is None
            assert template.default_notes is None
            assert template.tags is None
            assert template.billable is True  # Default value
            assert template.usage_count == 0

    def test_template_default_duration_property(self, app, user):
        """Test the default_duration property (hours conversion)"""
        with app.app_context():
            template = TimeEntryTemplate(user_id=user.id, name="Test Template", default_duration_minutes=90)
            db.session.add(template)
            db.session.commit()

            # Test getter
            assert template.default_duration == 1.5

            # Test setter
            template.default_duration = 2.25
            assert template.default_duration_minutes == 135

            # Test None handling
            template.default_duration = None
            assert template.default_duration_minutes is None
            assert template.default_duration is None

    def test_template_record_usage(self, app, user):
        """Test the record_usage method"""
        with app.app_context():
            template = TimeEntryTemplate(user_id=user.id, name="Test Template")
            db.session.add(template)
            db.session.commit()

            initial_count = template.usage_count
            initial_last_used = template.last_used_at

            # Record usage
            template.record_usage()
            db.session.commit()

            assert template.usage_count == initial_count + 1
            assert template.last_used_at is not None
            assert template.last_used_at != initial_last_used

    def test_template_increment_usage(self, app, user):
        """Test the increment_usage method"""
        with app.app_context():
            template = TimeEntryTemplate(user_id=user.id, name="Test Template")
            db.session.add(template)
            db.session.commit()

            # Increment usage multiple times
            for i in range(3):
                template.increment_usage()

            template_id = template.id

            # Verify in new query
            updated_template = TimeEntryTemplate.query.get(template_id)
            assert updated_template.usage_count == 3
            assert updated_template.last_used_at is not None

    def test_template_to_dict(self, app, user, project, task):
        """Test the to_dict method"""
        with app.app_context():
            template = TimeEntryTemplate(
                user_id=user.id,
                name="Test Template",
                description="Test description",
                project_id=project.id,
                task_id=task.id,
                default_duration_minutes=60,
                default_notes="Test notes",
                tags="test,template",
                billable=True,
            )
            db.session.add(template)
            db.session.commit()

            template_dict = template.to_dict()

            assert template_dict["id"] == template.id
            assert template_dict["user_id"] == user.id
            assert template_dict["name"] == "Test Template"
            assert template_dict["description"] == "Test description"
            assert template_dict["project_id"] == project.id
            assert template_dict["project_name"] == project.name
            assert template_dict["task_id"] == task.id
            assert template_dict["task_name"] == task.name
            assert template_dict["default_duration"] == 1.0
            assert template_dict["default_duration_minutes"] == 60
            assert template_dict["default_notes"] == "Test notes"
            assert template_dict["tags"] == "test,template"
            assert template_dict["billable"] is True
            assert template_dict["usage_count"] == 0
            assert "created_at" in template_dict
            assert "updated_at" in template_dict

    def test_template_relationships(self, app, user, project, task):
        """Test template relationships with user, project, and task"""
        with app.app_context():
            # Get IDs before context
            user_id = user.id
            project_id = project.id
            task_id = task.id

            template = TimeEntryTemplate(user_id=user_id, name="Test Template", project_id=project_id, task_id=task_id)
            db.session.add(template)
            db.session.commit()

            # Test relationships by ID
            assert template.user_id == user_id
            assert template.project_id == project_id
            assert template.task_id == task_id

            # Test relationship objects exist
            assert template.user is not None
            assert template.project is not None
            assert template.task is not None

            # Test relationship IDs match
            assert template.user.id == user_id
            assert template.project.id == project_id
            assert template.task.id == task_id

    def test_template_repr(self, app, user):
        """Test template __repr__ method"""
        with app.app_context():
            template = TimeEntryTemplate(user_id=user.id, name="Test Template")
            db.session.add(template)
            db.session.commit()

            assert repr(template) == "<TimeEntryTemplate Test Template>"


# ============================================================================
# Route Tests
# ============================================================================


@pytest.mark.routes
class TestTimeEntryTemplateRoutes:
    """Test time entry template routes"""

    def test_list_templates_authenticated(self, authenticated_client, user):
        """Test accessing templates list page when authenticated"""
        response = authenticated_client.get("/templates")
        assert response.status_code == 200
        assert b"Time Entry Templates" in response.data

    def test_list_templates_unauthenticated(self, client):
        """Test accessing templates list page without authentication"""
        response = client.get("/templates", follow_redirects=False)
        assert response.status_code == 302  # Redirect to login

    @pytest.mark.smoke
    def test_list_templates_with_usage_data(self, authenticated_client, user, project):
        """Test templates list page renders correctly with templates that have usage data"""
        # Create a template with usage data (last_used_at set)
        from datetime import datetime, timezone
        from app.models import TimeEntryTemplate
        from app import db

        template = TimeEntryTemplate(
            user_id=user.id,
            name="Used Template",
            project_id=project.id,
            default_duration_minutes=60,
            usage_count=5,
            last_used_at=datetime.now(timezone.utc),
        )
        db.session.add(template)
        db.session.commit()

        # Access the list page
        response = authenticated_client.get("/templates")
        assert response.status_code == 200
        assert b"Used Template" in response.data
        # Verify that timeago filter is working (should show "just now" or similar)
        assert b"ago" in response.data or b"just now" in response.data

    def test_create_template_page_get(self, authenticated_client):
        """Test accessing create template page"""
        response = authenticated_client.get("/templates/create")
        assert response.status_code == 200
        assert b"Create Time Entry Template" in response.data
        assert b"Template Name" in response.data

    def test_create_template_success(self, authenticated_client, user, project):
        """Test creating a new template successfully"""
        response = authenticated_client.post(
            "/templates/create",
            data={
                "name": "New Template",
                "project_id": project.id,
                "default_duration": "1.5",
                "default_notes": "Test notes",
                "tags": "test,new",
            },
            follow_redirects=True,
        )

        assert response.status_code == 200
        assert b"created successfully" in response.data

        # Verify template was created
        template = TimeEntryTemplate.query.filter_by(user_id=user.id, name="New Template").first()
        assert template is not None
        assert template.project_id == project.id
        assert template.default_duration == 1.5
        assert template.default_notes == "Test notes"
        assert template.tags == "test,new"

    def test_create_template_without_name(self, authenticated_client):
        """Test creating a template without a name fails"""
        response = authenticated_client.post(
            "/templates/create", data={"name": "", "default_notes": "Test notes"}, follow_redirects=True
        )

        assert response.status_code == 200
        assert b"required" in response.data or b"error" in response.data

    def test_create_template_duplicate_name(self, authenticated_client, user):
        """Test creating a template with duplicate name fails"""
        # Create first template
        template = TimeEntryTemplate(user_id=user.id, name="Duplicate Test")
        db.session.add(template)
        db.session.commit()

        # Try to create another with same name
        response = authenticated_client.post(
            "/templates/create", data={"name": "Duplicate Test", "default_notes": "Test notes"}, follow_redirects=True
        )

        assert response.status_code == 200
        assert b"already exists" in response.data

    def test_edit_template_page_get(self, authenticated_client, user):
        """Test accessing edit template page"""
        # Create a template
        template = TimeEntryTemplate(user_id=user.id, name="Edit Test")
        db.session.add(template)
        db.session.commit()

        response = authenticated_client.get(f"/templates/{template.id}/edit")
        assert response.status_code == 200
        assert b"Edit Test" in response.data

    def test_edit_template_success(self, authenticated_client, user):
        """Test editing a template successfully"""
        # Create a template
        template = TimeEntryTemplate(user_id=user.id, name="Original Name")
        db.session.add(template)
        db.session.commit()
        template_id = template.id

        # Edit the template
        response = authenticated_client.post(
            f"/templates/{template_id}/edit",
            data={"name": "Updated Name", "default_notes": "Updated notes"},
            follow_redirects=True,
        )

        assert response.status_code == 200
        assert b"updated successfully" in response.data

        # Verify update
        updated_template = TimeEntryTemplate.query.get(template_id)
        assert updated_template.name == "Updated Name"
        assert updated_template.default_notes == "Updated notes"

    def test_delete_template_success(self, authenticated_client, user):
        """Test deleting a template successfully"""
        # Create a template
        template = TimeEntryTemplate(user_id=user.id, name="Delete Test")
        db.session.add(template)
        db.session.commit()
        template_id = template.id

        # Delete the template
        response = authenticated_client.post(f"/templates/{template_id}/delete", follow_redirects=True)

        assert response.status_code == 200
        assert b"deleted successfully" in response.data

        # Verify deletion
        deleted_template = TimeEntryTemplate.query.get(template_id)
        assert deleted_template is None

    # View template test skipped - view.html template doesn't exist yet
    # def test_view_template(self, authenticated_client, user):
    #     """Test viewing a single template"""
    #     template = TimeEntryTemplate(
    #         user_id=user.id,
    #         name='View Test',
    #         default_notes='Test notes'
    #     )
    #     db.session.add(template)
    #     db.session.commit()
    #
    #     response = authenticated_client.get(f'/templates/{template.id}')
    #     assert response.status_code == 200
    #     assert b'View Test' in response.data
    #     assert b'Test notes' in response.data


# ============================================================================
# API Tests
# ============================================================================


@pytest.mark.api
class TestTimeEntryTemplateAPI:
    """Test time entry template API endpoints"""

    def test_get_templates_api(self, authenticated_client, user):
        """Test getting templates via API"""
        # Create some templates
        for i in range(3):
            template = TimeEntryTemplate(user_id=user.id, name=f"Template {i}")
            db.session.add(template)
        db.session.commit()

        response = authenticated_client.get("/api/templates")
        assert response.status_code == 200
        data = response.get_json()
        assert "templates" in data
        assert len(data["templates"]) >= 3

    def test_get_single_template_api(self, authenticated_client, user):
        """Test getting a single template via API"""
        template = TimeEntryTemplate(user_id=user.id, name="API Test", default_notes="Test notes")
        db.session.add(template)
        db.session.commit()

        response = authenticated_client.get(f"/api/templates/{template.id}")
        assert response.status_code == 200
        data = response.get_json()
        assert data["name"] == "API Test"
        assert data["default_notes"] == "Test notes"

    def test_use_template_api(self, authenticated_client, user):
        """Test marking template as used via API"""
        template = TimeEntryTemplate(user_id=user.id, name="Use Test")
        db.session.add(template)
        db.session.commit()
        template_id = template.id

        response = authenticated_client.post(f"/api/templates/{template_id}/use")
        assert response.status_code == 200
        data = response.get_json()
        assert data["success"] is True

        # Verify usage was recorded
        updated_template = TimeEntryTemplate.query.get(template_id)
        assert updated_template.usage_count == 1
        assert updated_template.last_used_at is not None


# ============================================================================
# Smoke Tests
# ============================================================================


@pytest.mark.smoke
class TestTimeEntryTemplatesSmoke:
    """Smoke tests for time entry templates feature"""

    def test_templates_page_renders(self, authenticated_client):
        """Smoke test: templates page renders without errors"""
        response = authenticated_client.get("/templates")
        assert response.status_code == 200
        assert b"Time Entry Templates" in response.data

    def test_create_template_page_renders(self, authenticated_client):
        """Smoke test: create template page renders without errors"""
        response = authenticated_client.get("/templates/create")
        assert response.status_code == 200
        assert b"Create" in response.data

    def test_template_crud_workflow(self, authenticated_client, user, project):
        """Smoke test: complete CRUD workflow for templates"""
        # Create
        response = authenticated_client.post(
            "/templates/create",
            data={"name": "Smoke Test Template", "project_id": project.id, "default_notes": "Smoke test"},
            follow_redirects=True,
        )
        assert response.status_code == 200

        # Read
        template = TimeEntryTemplate.query.filter_by(user_id=user.id, name="Smoke Test Template").first()
        assert template is not None

        # View test skipped - view.html doesn't exist yet
        # response = authenticated_client.get(f'/templates/{template.id}')
        # assert response.status_code == 200

        # Update
        response = authenticated_client.post(
            f"/templates/{template.id}/edit",
            data={"name": "Smoke Test Template Updated", "default_notes": "Updated notes"},
            follow_redirects=True,
        )
        assert response.status_code == 200

        # Delete
        response = authenticated_client.post(f"/templates/{template.id}/delete", follow_redirects=True)
        assert response.status_code == 200


# ============================================================================
# Integration Tests
# ============================================================================


@pytest.mark.integration
class TestTimeEntryTemplateIntegration:
    """Integration tests for time entry templates with other features"""

    def test_start_timer_from_template(self, authenticated_client, user, project):
        """Test starting a timer directly from a template"""
        # Create a template with project
        template = TimeEntryTemplate(
            user_id=user.id,
            name="Timer Test",
            project_id=project.id,
            default_notes="Test timer notes",
            tags="test,timer",
        )
        db.session.add(template)
        db.session.commit()
        template_id = template.id

        # Start timer from template
        response = authenticated_client.get(f"/timer/start/from-template/{template_id}", follow_redirects=True)
        assert response.status_code == 200
        assert b"Timer started" in response.data

        # Verify timer was created
        timer = TimeEntry.query.filter_by(user_id=user.id, end_time=None).first()
        assert timer is not None
        assert timer.project_id == project.id
        assert timer.notes == "Test timer notes"
        assert timer.tags == "test,timer"

        # Verify usage was tracked
        updated_template = TimeEntryTemplate.query.get(template_id)
        assert updated_template.usage_count == 1
        assert updated_template.last_used_at is not None

    def test_start_timer_from_template_without_project(self, authenticated_client, user):
        """Test that starting timer from template without project fails"""
        # Create template without project
        template = TimeEntryTemplate(user_id=user.id, name="No Project Template")
        db.session.add(template)
        db.session.commit()

        response = authenticated_client.get(f"/timer/start/from-template/{template.id}", follow_redirects=True)
        assert response.status_code == 200
        assert b"must have a project" in response.data or b"error" in response.data

    def test_start_timer_from_template_with_active_timer(self, authenticated_client, user, project):
        """Test that starting timer from template fails when user has active timer"""
        from datetime import datetime
        from app.models.time_entry import local_now

        # Create an active timer
        from factories import TimeEntryFactory

        active_timer = TimeEntryFactory(
            user_id=user.id, project_id=project.id, start_time=local_now(), end_time=None, source="auto"
        )

        # Create a template
        template = TimeEntryTemplate(user_id=user.id, name="Test Template", project_id=project.id)
        db.session.add(template)
        db.session.commit()

        # Try to start timer from template
        response = authenticated_client.get(f"/timer/start/from-template/{template.id}", follow_redirects=True)
        assert response.status_code == 200
        assert b"already have an active timer" in response.data

    def test_manual_entry_with_template_prefill(self, authenticated_client, user, project, task):
        """Test manual entry page pre-fills from template"""
        # Create a template
        template = TimeEntryTemplate(
            user_id=user.id,
            name="Manual Entry Test",
            project_id=project.id,
            task_id=task.id,
            default_notes="Prefilled notes",
            tags="prefill,test",
        )
        db.session.add(template)
        db.session.commit()

        # Access manual entry page with template parameter
        response = authenticated_client.get(f"/timer/manual?template={template.id}")
        assert response.status_code == 200
        # The page should render (full verification would require parsing HTML)
        assert b"Manual Entry" in response.data or b"manual" in response.data

    def test_start_timer_with_template_id(self, authenticated_client, user, project):
        """Test starting timer with template_id in form data"""
        # Create a template
        template = TimeEntryTemplate(
            user_id=user.id, name="Timer Form Test", project_id=project.id, default_notes="Template notes"
        )
        db.session.add(template)
        db.session.commit()

        # Start timer with template_id
        response = authenticated_client.post(
            "/timer/start",
            data={"template_id": template.id, "notes": ""},  # Should use template notes if empty
            follow_redirects=True,
        )
        assert response.status_code == 200

        # Verify timer was created (may fail if project validation fails)
        # This is a partial test - full integration would require valid form data

    def test_template_with_project_and_task(self, app, user, project, task):
        """Test template integration with projects and tasks"""
        with app.app_context():
            template = TimeEntryTemplate(
                user_id=user.id, name="Integration Test", project_id=project.id, task_id=task.id
            )
            db.session.add(template)
            db.session.commit()

            # Verify relationships work
            assert template.project.name == project.name
            assert template.task.name == task.name

    def test_template_usage_tracking_over_time(self, app, user):
        """Test template usage tracking"""
        with app.app_context():
            template = TimeEntryTemplate(user_id=user.id, name="Usage Tracking Test")
            db.session.add(template)
            db.session.commit()

            # Use template multiple times
            usage_times = []
            for _ in range(5):
                template.record_usage()
                usage_times.append(template.last_used_at)
                db.session.commit()

            assert template.usage_count == 5
            # Last used time should be most recent
            assert template.last_used_at == max(usage_times)

    def test_multiple_users_separate_templates(self, app):
        """Test that templates are user-specific"""
        with app.app_context():
            # Create two users
            user1 = User(username="template_user1", email="user1@test.com")
            user1.is_active = True
            user2 = User(username="template_user2", email="user2@test.com")
            user2.is_active = True
            db.session.add_all([user1, user2])
            db.session.commit()

            # Create templates for each user
            template1 = TimeEntryTemplate(user_id=user1.id, name="User1 Template")
            template2 = TimeEntryTemplate(user_id=user2.id, name="User2 Template")
            db.session.add_all([template1, template2])
            db.session.commit()

            # Verify isolation
            user1_templates = TimeEntryTemplate.query.filter_by(user_id=user1.id).all()
            user2_templates = TimeEntryTemplate.query.filter_by(user_id=user2.id).all()

            assert len(user1_templates) == 1
            assert len(user2_templates) == 1
            assert user1_templates[0].name == "User1 Template"
            assert user2_templates[0].name == "User2 Template"
