"""
Tests for Table operations
"""

import pytest
from unittest.mock import MagicMock


class TestTableOperations:
    """Test NoSQL table operations"""

    @pytest.mark.asyncio
    async def test_create_table(self, client, mock_httpx_client, sample_project_id):
        """Test table creation"""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "table_id": "770e8400-e29b-41d4-a716-446655440001",
            "table_name": "users",
            "status": "created",
            "created_at": "2025-01-14T10:00:00Z"
        }
        mock_httpx_client.post.return_value = mock_response

        result = await client.tables.create(
            project_id=sample_project_id,
            table_name="users",
            schema_definition={
                "user_id": "string",
                "email": "string",
                "age": "integer",
                "tags": "array"
            },
            indexes=["user_id", "email"]
        )

        assert result["status"] == "created"
        assert result["table_name"] == "users"
        assert "table_id" in result

    @pytest.mark.asyncio
    async def test_create_table_empty_name(self, client, sample_project_id):
        """Test create table with empty name"""
        with pytest.raises(ValueError, match="table_name cannot be empty"):
            await client.tables.create(
                project_id=sample_project_id,
                table_name="",
                schema_definition={"field": "string"}
            )

    @pytest.mark.asyncio
    async def test_create_table_empty_schema(self, client, sample_project_id):
        """Test create table with empty schema"""
        with pytest.raises(ValueError, match="schema_definition cannot be empty"):
            await client.tables.create(
                project_id=sample_project_id,
                table_name="test_table",
                schema_definition={}
            )

    @pytest.mark.asyncio
    async def test_list_tables(self, client, mock_httpx_client, sample_project_id):
        """Test list tables"""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "tables": [
                {
                    "table_id": "770e8400-e29b-41d4-a716-446655440001",
                    "table_name": "users",
                    "row_count": 1500,
                    "created_at": "2025-01-14T10:00:00Z"
                },
                {
                    "table_id": "770e8400-e29b-41d4-a716-446655440002",
                    "table_name": "products",
                    "row_count": 350,
                    "created_at": "2025-01-14T11:00:00Z"
                }
            ],
            "total": 2
        }
        mock_httpx_client.post.return_value = mock_response

        result = await client.tables.list(
            project_id=sample_project_id,
            limit=100,
            offset=0
        )

        assert len(result["tables"]) == 2
        assert result["total"] == 2
        assert result["tables"][0]["table_name"] == "users"
        assert result["tables"][0]["row_count"] == 1500

    @pytest.mark.asyncio
    async def test_list_tables_pagination(self, client, mock_httpx_client, sample_project_id):
        """Test list tables with pagination"""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "tables": [],
            "total": 100
        }
        mock_httpx_client.post.return_value = mock_response

        result = await client.tables.list(
            project_id=sample_project_id,
            limit=50,
            offset=50
        )

        assert result["total"] == 100

    @pytest.mark.asyncio
    async def test_get_table(self, client, mock_httpx_client, sample_project_id):
        """Test get table details"""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "table_id": "770e8400-e29b-41d4-a716-446655440001",
            "table_name": "users",
            "schema": {
                "user_id": "string",
                "email": "string",
                "age": "integer"
            },
            "indexes": ["user_id", "email"],
            "row_count": 1500,
            "created_at": "2025-01-14T10:00:00Z",
            "updated_at": "2025-01-14T12:00:00Z"
        }
        mock_httpx_client.post.return_value = mock_response

        result = await client.tables.get(
            project_id=sample_project_id,
            table_name="users"
        )

        assert result["table_name"] == "users"
        assert result["row_count"] == 1500
        assert "user_id" in result["schema"]

    @pytest.mark.asyncio
    async def test_delete_table(self, client, mock_httpx_client, sample_project_id):
        """Test delete table"""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "status": "success",
            "rows_deleted": 1500
        }
        mock_httpx_client.post.return_value = mock_response

        result = await client.tables.delete(
            project_id=sample_project_id,
            table_name="old_table"
        )

        assert result["status"] == "success"
        assert result["rows_deleted"] == 1500

    @pytest.mark.asyncio
    async def test_insert_rows(self, client, mock_httpx_client, sample_project_id):
        """Test insert rows into table"""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "inserted": 2,
            "failed": 0,
            "row_ids": ["row1", "row2"]
        }
        mock_httpx_client.post.return_value = mock_response

        rows = [
            {"user_id": "u1", "email": "user1@example.com", "age": 25},
            {"user_id": "u2", "email": "user2@example.com", "age": 30}
        ]

        result = await client.tables.insert_rows(
            project_id=sample_project_id,
            table_name="users",
            rows=rows
        )

        assert result["inserted"] == 2
        assert result["failed"] == 0
        assert len(result["row_ids"]) == 2

    @pytest.mark.asyncio
    async def test_insert_rows_empty(self, client, sample_project_id):
        """Test insert with empty rows"""
        with pytest.raises(ValueError, match="rows cannot be empty"):
            await client.tables.insert_rows(
                project_id=sample_project_id,
                table_name="users",
                rows=[]
            )

    @pytest.mark.asyncio
    async def test_insert_rows_too_many(self, client, sample_project_id):
        """Test insert with too many rows"""
        rows = [{"id": i} for i in range(1001)]

        with pytest.raises(ValueError, match="Maximum 1000 rows per request"):
            await client.tables.insert_rows(
                project_id=sample_project_id,
                table_name="users",
                rows=rows
            )

    @pytest.mark.asyncio
    async def test_query_rows(self, client, mock_httpx_client, sample_project_id):
        """Test query rows with filters"""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "rows": [
                {"user_id": "u1", "email": "user1@example.com", "age": 25},
                {"user_id": "u2", "email": "user2@example.com", "age": 30}
            ],
            "total": 2,
            "limit": 100,
            "offset": 0
        }
        mock_httpx_client.post.return_value = mock_response

        result = await client.tables.query_rows(
            project_id=sample_project_id,
            table_name="users",
            filters={"age": {"$gte": 18}},
            sort_by="age",
            order="desc",
            limit=100,
            offset=0
        )

        assert len(result["rows"]) == 2
        assert result["total"] == 2
        assert result["rows"][0]["age"] == 25

    @pytest.mark.asyncio
    async def test_query_rows_no_filters(self, client, mock_httpx_client, sample_project_id):
        """Test query rows without filters"""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "rows": [],
            "total": 0,
            "limit": 100,
            "offset": 0
        }
        mock_httpx_client.post.return_value = mock_response

        result = await client.tables.query_rows(
            project_id=sample_project_id,
            table_name="users"
        )

        assert result["total"] == 0
        assert result["rows"] == []

    @pytest.mark.asyncio
    async def test_query_rows_complex_filters(self, client, mock_httpx_client, sample_project_id):
        """Test query rows with complex MongoDB-style filters"""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "rows": [
                {"user_id": "u1", "email": "user1@example.com", "age": 25, "status": "active"}
            ],
            "total": 1,
            "limit": 100,
            "offset": 0
        }
        mock_httpx_client.post.return_value = mock_response

        result = await client.tables.query_rows(
            project_id=sample_project_id,
            table_name="users",
            filters={
                "age": {"$gte": 18, "$lte": 30},
                "status": "active"
            },
            limit=100
        )

        assert result["total"] == 1

    @pytest.mark.asyncio
    async def test_update_rows(self, client, mock_httpx_client, sample_project_id):
        """Test update rows matching filters"""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "updated": 5
        }
        mock_httpx_client.post.return_value = mock_response

        result = await client.tables.update_rows(
            project_id=sample_project_id,
            table_name="users",
            filters={"age": {"$gte": 18}},
            updates={"verified": True, "updated_at": "2025-01-14T12:00:00Z"}
        )

        assert result["updated"] == 5

    @pytest.mark.asyncio
    async def test_update_rows_single_field(self, client, mock_httpx_client, sample_project_id):
        """Test update single field"""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "updated": 1
        }
        mock_httpx_client.post.return_value = mock_response

        result = await client.tables.update_rows(
            project_id=sample_project_id,
            table_name="users",
            filters={"user_id": "u1"},
            updates={"email": "newemail@example.com"}
        )

        assert result["updated"] == 1

    @pytest.mark.asyncio
    async def test_delete_rows(self, client, mock_httpx_client, sample_project_id):
        """Test delete rows matching filters"""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "deleted": 3
        }
        mock_httpx_client.post.return_value = mock_response

        result = await client.tables.delete_rows(
            project_id=sample_project_id,
            table_name="users",
            filters={"active": False}
        )

        assert result["deleted"] == 3

    @pytest.mark.asyncio
    async def test_delete_rows_with_date_filter(self, client, mock_httpx_client, sample_project_id):
        """Test delete rows with date filter"""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "deleted": 10
        }
        mock_httpx_client.post.return_value = mock_response

        result = await client.tables.delete_rows(
            project_id=sample_project_id,
            table_name="users",
            filters={"last_login": {"$lt": "2024-01-01T00:00:00Z"}}
        )

        assert result["deleted"] == 10


class TestTableOperationsIntegration:
    """Integration tests for table operations workflow"""

    @pytest.mark.asyncio
    async def test_complete_table_workflow(self, client, mock_httpx_client, sample_project_id):
        """Test complete table workflow: create, insert, query, update, delete"""
        # Mock responses for the workflow
        create_response = MagicMock()
        create_response.status_code = 200
        create_response.json.return_value = {
            "table_id": "770e8400-e29b-41d4-a716-446655440001",
            "table_name": "test_workflow",
            "status": "created"
        }

        insert_response = MagicMock()
        insert_response.status_code = 200
        insert_response.json.return_value = {
            "inserted": 2,
            "failed": 0,
            "row_ids": ["row1", "row2"]
        }

        query_response = MagicMock()
        query_response.status_code = 200
        query_response.json.return_value = {
            "rows": [
                {"id": "1", "name": "Test User", "status": "active"}
            ],
            "total": 1
        }

        update_response = MagicMock()
        update_response.status_code = 200
        update_response.json.return_value = {"updated": 1}

        delete_response = MagicMock()
        delete_response.status_code = 200
        delete_response.json.return_value = {"deleted": 1}

        # Set up mock to return different responses
        mock_httpx_client.post.side_effect = [
            create_response,
            insert_response,
            query_response,
            update_response,
            delete_response
        ]

        # 1. Create table
        table = await client.tables.create(
            project_id=sample_project_id,
            table_name="test_workflow",
            schema_definition={"id": "string", "name": "string", "status": "string"}
        )
        assert table["status"] == "created"

        # 2. Insert rows
        insert_result = await client.tables.insert_rows(
            project_id=sample_project_id,
            table_name="test_workflow",
            rows=[
                {"id": "1", "name": "Test User", "status": "active"},
                {"id": "2", "name": "Another User", "status": "inactive"}
            ]
        )
        assert insert_result["inserted"] == 2

        # 3. Query rows
        query_result = await client.tables.query_rows(
            project_id=sample_project_id,
            table_name="test_workflow",
            filters={"status": "active"}
        )
        assert query_result["total"] == 1

        # 4. Update rows
        update_result = await client.tables.update_rows(
            project_id=sample_project_id,
            table_name="test_workflow",
            filters={"id": "1"},
            updates={"status": "verified"}
        )
        assert update_result["updated"] == 1

        # 5. Delete rows
        delete_result = await client.tables.delete_rows(
            project_id=sample_project_id,
            table_name="test_workflow",
            filters={"status": "inactive"}
        )
        assert delete_result["deleted"] == 1

    @pytest.mark.asyncio
    async def test_batch_insert_workflow(self, client, mock_httpx_client, sample_project_id):
        """Test batch insert workflow with multiple batches"""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "inserted": 100,
            "failed": 0,
            "row_ids": [f"row{i}" for i in range(100)]
        }
        mock_httpx_client.post.return_value = mock_response

        # Insert first batch
        batch1 = [{"id": f"user{i}", "email": f"user{i}@example.com"} for i in range(100)]
        result1 = await client.tables.insert_rows(
            project_id=sample_project_id,
            table_name="users",
            rows=batch1
        )
        assert result1["inserted"] == 100

        # Insert second batch
        batch2 = [{"id": f"user{i}", "email": f"user{i}@example.com"} for i in range(100, 200)]
        result2 = await client.tables.insert_rows(
            project_id=sample_project_id,
            table_name="users",
            rows=batch2
        )
        assert result2["inserted"] == 100
