"""Integration tests for Draft API endpoints.

These tests verify draft configuration endpoints that actually work on staging.
Each test creates its own test data and cleans up after itself.
"""

import logging

from .base_test import BaseRobotTest

logger = logging.getLogger(__name__)


class TestDraftAPI(BaseRobotTest):
    """Test cases for Draft API endpoints (focused on working endpoints)."""

    # Default test scene and factory name for creating test drafts
    # Using DOC_STORE as it has minimal configuration requirements
    DEFAULT_TEST_SCENE = "DOC_STORE"
    DEFAULT_TEST_FACTORY = "PostgresDocStoreDraft"

    def test_list_drafts(self):
        """Test GET /factory/drafts/query - List draft configurations.

        Verifies:
        - Endpoint is accessible
        - Response contains list of drafts
        """
        response = self.client.draft.list_drafts()
        assert isinstance(response, list), "Response should be a list"

    def test_get_draft(self):
        """Test GET /factory/drafts/:settingId - Get single draft.

        Verifies:
        - Endpoint is accessible with settingId parameter
        - Response contains draft details
        """
        # Create a test draft to retrieve
        draft_id = self.create_draft(
            scene=self.DEFAULT_TEST_SCENE,
            name=self.DEFAULT_TEST_FACTORY,
            config={
                "name": "TestGetDraft",
                "description": "Test for get_draft API"
            },
        )

        # Get the draft
        response = self.client.draft.get_draft(draft_id)
        assert isinstance(response, dict), "Response should be a dict"
        # Verify the draft we retrieved has the expected ID
        assert response.get('settingId') == draft_id or response.get('setting_id') == draft_id

    def test_create_draft(self):
        """Test POST /factory/drafts - Create new draft.

        Verifies:
        - Endpoint creates a new draft
        - Response contains draft details
        - teardown auto-cleanup
        """
        # Use base class method (auto-tracked for cleanup)
        draft_id = self.create_draft(
            scene=self.DEFAULT_TEST_SCENE,
            name=self.DEFAULT_TEST_FACTORY,
            config={
                "name": "TestCreateDraft",
                "description": "Test for create_draft API"
            },
        )

        # Verify creation success
        assert draft_id is not None
        assert draft_id > 0

        # Verify the draft was actually created by retrieving it
        retrieved_draft = self.client.draft.get_draft(draft_id)
        assert retrieved_draft is not None

    def test_update_draft(self):
        """Test PUT /factory/drafts/:settingId - Update draft.

        Verifies:
        - Endpoint updates draft configuration
        - Response contains updated draft details
        - teardown auto-cleanup test data
        """
        # Create test draft for update
        draft_id = self.create_draft(
            scene=self.DEFAULT_TEST_SCENE,
            name=self.DEFAULT_TEST_FACTORY,
            config={
                "name": "TestUpdateDraftOriginal",
                "description": "Original description"
            },
        )

        # Execute update
        response = self.client.draft.update_draft(
            setting_id=draft_id,
            setting_name="test_updated",
            config={
                "name": "TestUpdateDraftUpdated",
                "description": "Updated description"
            },
        )

        assert isinstance(response, dict), "Response should be a dict"

        # Verify the update was applied
        retrieved_draft = self.client.draft.get_draft(draft_id)
        assert retrieved_draft.get('settingName') == "test_updated" or retrieved_draft.get('setting_name') == "test_updated"

    def test_delete_draft(self):
        """Test DELETE /factory/drafts/:settingId - Delete draft.

        Verifies:
        - Endpoint deletes draft successfully
        - teardown won't duplicate delete (removed from tracking list)
        """
        # Create test draft for deletion
        draft_id = self.create_draft(
            scene=self.DEFAULT_TEST_SCENE,
            name=self.DEFAULT_TEST_FACTORY,
            config={
                "name": "TestDeleteDraft",
                "description": "Test for delete_draft API"
            },
        )

        # Remove from tracking list (we manually test delete)
        self._resources[self.RESOURCE_DRAFT].remove(draft_id)

        # Delete the draft
        delete_response = self.client.draft.delete_draft(draft_id)
        assert isinstance(delete_response, dict), "Delete response should be a dict"

        # Verify the draft was actually deleted
        # Try to get the deleted draft - should fail or return not found
        try:
            self.client.draft.get_draft(draft_id)
            # If we get here, the draft still exists - this might be OK depending on API behavior
            # Some APIs might return empty or null for deleted resources
        except Exception:
            # Expected - draft should not exist after deletion
            pass

    def test_batch_create_drafts(self):
        """Test POST /factory/drafts/batch - Batch create drafts.

        Verifies:
        - Endpoint handles batch creation
        - Response contains batch result
        - teardown cleans up each created draft
        """
        # Use base class method (auto-tracked for cleanup)
        draft_ids = self.batch_create_drafts([
            {
                "temp_id": -1,
                "draft": {
                    "scene": self.DEFAULT_TEST_SCENE,
                    "name": self.DEFAULT_TEST_FACTORY,
                    "settingName": "test_batch_1",
                    "config": {
                        "name": "TestBatch1",
                        "description": "First batch draft"
                    },
                },
            },
            {
                "temp_id": -2,
                "draft": {
                    "scene": self.DEFAULT_TEST_SCENE,
                    "name": self.DEFAULT_TEST_FACTORY,
                    "settingName": "test_batch_2",
                    "config": {
                        "name": "TestBatch2",
                        "description": "Second batch draft"
                    },
                },
            },
        ])

        # Verify batch creation success
        assert len(draft_ids) == 2
        assert all(did > 0 for did in draft_ids)

        # Verify all drafts were actually created
        for draft_id in draft_ids:
            retrieved_draft = self.client.draft.get_draft(draft_id)
            assert retrieved_draft is not None

    def test_release_draft(self):
        """Test POST /factory/drafts/release - Release all drafts to production.

        Verifies:
        - Endpoint releases drafts to production environment
        - Response contains release confirmation

        Note: This test creates a complete ROBOT configuration (including
        all dependencies like DOC_STORE, MEMORY, LLM, CHAIN, BRAIN, DRIVE)
        and attempts to release it. The release_draft() API releases ALL
        drafts in the system, not just the ones created for this test.
        """
        # Check if ApiFox configs already exist (from test_trigger_online_action_drive)
        # If they exist, we can use them directly for release testing
        all_drafts = self.client.draft.list_drafts()
        apifox_drafts = [d for d in all_drafts if 'ApiFox' in d.get('settingName', '')]

        if apifox_drafts:
            logger.info(f"Found {len(apifox_drafts)} existing ApiFox drafts, using them for release test")
        else:
            # No ApiFox configs, create new ones
            logger.info("No existing ApiFox configs, creating new robot configuration")
            import uuid
            unique_suffix = f"release_test_{uuid.uuid4().hex[:8]}"
            draft_ids = self.create_minimal_robot_config(name_suffix=unique_suffix)

            # Verify ROBOT was created
            assert draft_ids["robot"] is not None
            assert draft_ids["robot"] > 0

        # Attempt to release all drafts to production
        # Note: release_draft() releases ALL drafts, not just the ones we created
        response = self.client.draft.release_draft()
        assert isinstance(response, dict), "Response should be a dict"
        # Response should contain onlineRobotId or similar confirmation
        assert response is not None
        assert 'onlineRobotId' in response or 'online_robot_id' in response, \
            "Response should contain online robot ID"

    def test_save_as_template(self):
        """Test POST /factory/drafts/:draftId/savetemplate - Save draft as template.

        Verifies:
        - Endpoint saves draft as template
        - teardown auto-cleanup template
        """
        # Create a draft to save as template
        draft_id = self.create_draft(
            scene=self.DEFAULT_TEST_SCENE,
            name=self.DEFAULT_TEST_FACTORY,
            config={
                "name": "TestSaveAsTemplate",
                "description": "Test for save_as_template API"
            },
        )

        # Use base class method (auto-tracked for cleanup)
        template_id = self.create_template(draft_id, name="test_template")

        # Verify creation success
        assert template_id is not None
        assert template_id > 0

        # Verify the template was actually created by retrieving it
        retrieved_template = self.client.template.get_template(template_id)
        assert retrieved_template is not None

    def test_trigger_draft_action(self):
        """Test PUT /factory/drafts/:settingId/action/:action - Trigger action.

        Verifies:
        - Endpoint triggers action on draft
        - Action parameters are correctly passed and processed
        - Response data is properly extracted and returned

        Uses PROMPT_TEMPLATE with 'render' action which demonstrates:
        - Template rendering with dynamic parameters
        - Parameter wrapping in {"params": {...}} format
        - Direct return of rendered data (extraction from TFSResponse.data)

        Note: The trigger_draft_action method automatically extracts the 'data' field
        from the TFSResponse, so for render action, it returns the rendered string directly,
        not the full response object with code/message.
        """
        # Create a PROMPT_TEMPLATE draft with a template that uses parameters
        draft_id = self.create_draft(
            scene="PROMPT_TEMPLATE",
            name="FStrTemplateDraft",
            setting_name="未命名",
            config={
                "templates": {
                    "zh": "你好吗{name}"
                },
                "active_language": "zh",
                "params_schema": {
                    "type": "object",
                    "properties": {
                        "name": {
                            "type": "string"
                        }
                    },
                    "required": ["name"]
                }
            }
        )

        # Get the draft to verify it has render action
        draft_detail = self.client.draft.get_draft(draft_id)
        tfs_actions = (draft_detail.get('tfsActions') or
                      draft_detail.get('tfs_actions', {}))

        # Verify the draft has render action
        assert "render" in tfs_actions, "PROMPT_TEMPLATE should have render action"

        # Trigger the render action with parameters
        # Note: render action expects params to be wrapped in a "params" key
        # The backend API expects: {"params": {"name": "张三"}}
        action_name = "render"
        params = {"params": {"name": "张三"}}
        result = self.client.draft.trigger_draft_action(draft_id, action_name, params)

        # Verify the result
        # The trigger_draft_action method extracts TFSResponse.data automatically
        # Original API response: {"data":"你好吗张三","code":200,"message":"Success"}
        # After extraction: result = "你好吗张三" (string)
        assert result is not None, "Action should return a result"
        assert isinstance(result, str), f"Result should be a string, got {type(result)}"
        assert result == "你好吗张三", f"Expected '你好吗张三', got '{result}'"

        # Note: The actual API returns full TFSResponse format:
        # {"data":"你好吗张三","code":200,"message":"Success"}
        # But trigger_draft_action automatically extracts the 'data' field for convenience

    def test_get_draft_scenes(self):
        """Test GET /factory/draft-scenes - Get available draft scenes.

        Verifies:
        - Endpoint returns list of scene names
        - Response contains expected scenes like ROBOT, LLM, CHAIN, etc.
        - Uses correct endpoint path (draft-scenes, not drafts/scenes)

        Note: This endpoint returns 500 error in staging environments.
        The endpoint exists but has validation issues (code 601).
        This is a known issue in the backend API for the staging environment.

        In production environments, this endpoint should return:
        {
          "code": 200,
          "message": "success",
          "data": ["ROBOT", "LLM", "CHAIN", "DOC_STORE", "MEMORY", ...]
        }
        """
        response = self.client.draft.get_draft_scenes()
        assert isinstance(response, list), "Response should be a list"
        # Verify common scenes exist
        assert len(response) > 0, "Should return at least one scene"
        # Check for expected scene names (may vary by environment)
        expected_scenes = ["ROBOT", "LLM", "CHAIN", "DOC_STORE", "MEMORY"]
        found_scenes = [scene for scene in expected_scenes if scene in response]
        assert len(found_scenes) > 0, f"Should contain at least one common scene, got: {response}"

    def test_get_draft_factories(self):
        """Test GET /factory/drafts/scene/:scene/factories - Get factories for a scene.

        Verifies:
        - Endpoint returns factory names for given scene
        - Response contains factory_names field or factory list
        - Uses correct endpoint path (drafts/scene/{scene}/factories)

        Uses DEFAULT_TEST_SCENE (DOC_STORE) for testing.

        Note: This endpoint returns 404 in staging environments.
        The endpoint path exists in production but may not be deployed in staging.
        This is a known limitation of the staging environment.

        In production environments, this endpoint should return:
        {
          "code": 200,
          "message": "success",
          "data": {
            "factory_names": ["Factory1", "Factory2", ...]
          }
        }
        """
        import logging
        logger = logging.getLogger(__name__)

        # Debug: log the request URL
        logger.info(f"Fetching factories for scene: {self.DEFAULT_TEST_SCENE}")

        response = self.client.draft.get_draft_factories(self.DEFAULT_TEST_SCENE)
        assert isinstance(response, dict), "Response should be a dict"
        # Response may have 'factory_names' or 'factoryNames' field
        factory_names = (
            response.get('factory_names') or
            response.get('factoryNames') or
            response.get('factories') or
            []
        )
        assert isinstance(factory_names, list), "factory_names should be a list"
        assert len(factory_names) > 0, f"Should return at least one factory for {self.DEFAULT_TEST_SCENE}"
