"""
CLI Integration Tests for Issue #40: Associated Files Management.

Tests the enhanced CLI commands that work with associated files.
"""

import json
import pytest
from pathlib import Path
from tempfile import TemporaryDirectory
from click.testing import CliRunner

from markitect.cli import cli


class TestIssue40CLIIntegration:
    """Test CLI integration for associated files management."""

    @pytest.fixture
    def runner(self):
        """Create CLI test runner."""
        return CliRunner()

    @pytest.fixture
    def temp_dir(self):
        """Create a temporary directory for testing."""
        with TemporaryDirectory() as temp_dir:
            yield Path(temp_dir)

    def test_schema_generate_with_explicit_associated_path(self, runner, temp_dir):
        """schema-generate can use explicit associated .json file path."""
        md_file = temp_dir / "document.md"
        md_file.write_text("# Document\n\n## Introduction\n\nContent here.")

        # Explicitly specify associated file path
        expected_schema = temp_dir / "document.json"
        result = runner.invoke(cli, [
            'schema-generate', str(md_file), '--output', str(expected_schema)
        ])

        assert result.exit_code == 0

        # Should create associated schema file
        assert expected_schema.exists()

        # Verify it's valid JSON
        schema_content = json.loads(expected_schema.read_text())
        assert schema_content['type'] == 'object'

    def test_schema_generate_interactive_mode_defaults_to_associated_path(self, runner, temp_dir):
        """schema-generate in interactive mode should default to associated path."""
        md_file = temp_dir / "document.md"
        md_file.write_text("# Document\n\n## Introduction\n\nContent here.")

        # Run schema-generate without --output in interactive mode
        result = runner.invoke(cli, [
            'schema-generate', str(md_file)
        ], env={'MARKITECT_MODE': 'interactive'})

        assert result.exit_code == 0

        # Should create associated schema file
        expected_schema = temp_dir / "document.json"
        assert expected_schema.exists()

        # Verify it's valid JSON
        schema_content = json.loads(expected_schema.read_text())
        assert schema_content['type'] == 'object'

    def test_generate_stub_with_explicit_associated_path(self, runner, temp_dir):
        """generate-stub can use explicit associated .md file path."""
        schema_file = temp_dir / "template.json"
        schema_content = {
            "$schema": "http://json-schema.org/draft-07/schema#",
            "type": "object",
            "title": "Template Schema",
            "properties": {
                "headings": {
                    "type": "object",
                    "properties": {
                        "level_1": {"type": "array", "minItems": 1, "maxItems": 1}
                    }
                }
            }
        }
        schema_file.write_text(json.dumps(schema_content))

        # Explicitly specify associated file path
        expected_md = temp_dir / "template.md"
        result = runner.invoke(cli, [
            'generate-stub', str(schema_file), '--output', str(expected_md)
        ])

        assert result.exit_code == 0

        # Should create associated markdown file
        assert expected_md.exists()

        # Verify it's valid markdown with schema reference
        md_content = expected_md.read_text()
        # Content should include schema reference metadata and heading
        assert '<!-- Generated from schema:' in md_content
        assert '# Template Schema' in md_content

    def test_generate_stub_interactive_mode_defaults_to_associated_path(self, runner, temp_dir):
        """generate-stub in interactive mode should default to associated path."""
        schema_file = temp_dir / "template.json"
        schema_content = {
            "$schema": "http://json-schema.org/draft-07/schema#",
            "type": "object",
            "title": "Template Schema",
            "properties": {
                "headings": {
                    "type": "object",
                    "properties": {
                        "level_1": {"type": "array", "minItems": 1, "maxItems": 1}
                    }
                }
            }
        }
        schema_file.write_text(json.dumps(schema_content))

        # Run generate-stub without --output in interactive mode
        result = runner.invoke(cli, [
            'generate-stub', str(schema_file)
        ], env={'MARKITECT_MODE': 'interactive'})

        assert result.exit_code == 0

        # Should create associated markdown file
        expected_md = temp_dir / "template.md"
        assert expected_md.exists()

        # Verify it's valid markdown with schema reference
        md_content = expected_md.read_text()
        # Content should include schema reference metadata and heading
        assert '<!-- Generated from schema:' in md_content
        assert '# Template Schema' in md_content

    def test_validate_auto_discovers_associated_schema(self, runner, temp_dir):
        """validate should auto-discover associated schema when not specified."""
        md_file = temp_dir / "article.md"
        schema_file = temp_dir / "article.json"

        md_file.write_text("# Article\n\n## Introduction\n\nContent.")
        schema_file.write_text('{"$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "title": "Article Schema"}')

        # Run validate with only markdown file (should find associated schema)
        result = runner.invoke(cli, [
            'validate', str(md_file)
        ])

        assert result.exit_code == 0
        # Should indicate successful validation using auto-discovered schema

    def test_new_associated_files_list_command(self, runner, temp_dir):
        """Should have new command to list associated file pairs."""
        # Create some file pairs
        (temp_dir / "doc1.md").write_text("# Doc 1")
        (temp_dir / "doc1.json").write_text('{"type": "object"}')

        (temp_dir / "doc2.md").write_text("# Doc 2")
        (temp_dir / "doc2.json").write_text('{"type": "object"}')

        # Create orphaned files
        (temp_dir / "orphan.md").write_text("# Orphan")

        result = runner.invoke(cli, [
            'associated-files', 'list', str(temp_dir)
        ])

        assert result.exit_code == 0
        assert 'doc1' in result.output
        assert 'doc2' in result.output
        # Should show paired status

    def test_new_associated_files_info_command(self, runner, temp_dir):
        """Should have command to show info about associated files."""
        md_file = temp_dir / "example.md"
        schema_file = temp_dir / "example.json"

        md_file.write_text("# Example\n\nContent here.")
        schema_file.write_text('{"type": "object", "title": "Example"}')

        result = runner.invoke(cli, [
            'associated-files', 'info', str(md_file)
        ])

        assert result.exit_code == 0
        assert 'example' in result.output
        assert 'paired' in result.output.lower()

    def test_new_associated_files_create_command(self, runner, temp_dir):
        """Should have command to create missing associated files."""
        md_file = temp_dir / "lonely.md"
        md_file.write_text("# Lonely Document\n\n## Section\n\nContent.")

        # Create associated schema
        result = runner.invoke(cli, [
            'associated-files', 'create-schema', str(md_file)
        ])

        assert result.exit_code == 0

        expected_schema = temp_dir / "lonely.json"
        assert expected_schema.exists()

        # Verify it's a valid schema
        schema = json.loads(expected_schema.read_text())
        assert schema['type'] == 'object'

    def test_new_associated_files_create_stub_command(self, runner, temp_dir):
        """Should have command to create stub from existing schema."""
        schema_file = temp_dir / "template.json"
        schema_content = {
            "$schema": "http://json-schema.org/draft-07/schema#",
            "type": "object",
            "title": "Template",
            "properties": {
                "headings": {
                    "type": "object",
                    "properties": {
                        "level_1": {"type": "array", "minItems": 1}
                    }
                }
            }
        }
        schema_file.write_text(json.dumps(schema_content))

        # Create associated markdown stub
        result = runner.invoke(cli, [
            'associated-files', 'create-stub', str(schema_file)
        ])

        assert result.exit_code == 0

        expected_md = temp_dir / "template.md"
        assert expected_md.exists()

        # Verify it's valid markdown
        md_content = expected_md.read_text()
        assert md_content.startswith('# ')

    def test_associated_files_status_command(self, runner, temp_dir):
        """Should show status of associated files in directory."""
        # Create mixed scenarios
        (temp_dir / "paired.md").write_text("# Paired")
        (temp_dir / "paired.json").write_text('{"type": "object"}')

        (temp_dir / "lonely.md").write_text("# Lonely")
        (temp_dir / "orphan.json").write_text('{"type": "object"}')

        result = runner.invoke(cli, [
            'associated-files', 'status', str(temp_dir)
        ])

        assert result.exit_code == 0
        assert 'paired' in result.output.lower() or 'orphaned' in result.output.lower()

    def test_enhanced_commands_preserve_explicit_output(self, runner, temp_dir):
        """Enhanced commands should still respect explicit --output options."""
        md_file = temp_dir / "source.md"
        md_file.write_text("# Source\n\n## Content")

        custom_output = temp_dir / "custom_name.json"

        # Use explicit output path (should override associated file logic)
        result = runner.invoke(cli, [
            'schema-generate', str(md_file), '--output', str(custom_output)
        ])

        assert result.exit_code == 0
        assert custom_output.exists()
        assert not (temp_dir / "source.json").exists()

    def test_associated_files_help_commands(self, runner):
        """Associated files commands should provide helpful usage information."""
        result = runner.invoke(cli, ['associated-files', '--help'])
        assert result.exit_code == 0
        assert 'associated' in result.output.lower()
        assert 'files' in result.output.lower()

        # Test subcommand help
        result = runner.invoke(cli, ['associated-files', 'list', '--help'])
        assert result.exit_code == 0

    def test_error_handling_for_non_existent_files(self, runner, temp_dir):
        """Should handle non-existent files gracefully."""
        non_existent = temp_dir / "does_not_exist.md"

        result = runner.invoke(cli, [
            'associated-files', 'info', str(non_existent)
        ])

        assert result.exit_code != 0
        assert 'not found' in result.output.lower() or 'error' in result.output.lower()

    def test_workflow_integration_complete_cycle(self, runner, temp_dir):
        """Test complete workflow with associated files."""
        original_md = temp_dir / "workflow.md"
        original_md.write_text("# Workflow Example\n\n## Introduction\n\nTest content.")

        # Step 1: Generate schema explicitly to workflow.json
        schema_file = temp_dir / "workflow.json"
        result1 = runner.invoke(cli, [
            'schema-generate', str(original_md), '--output', str(schema_file)
        ])
        assert result1.exit_code == 0
        assert schema_file.exists()

        # Step 2: Generate stub with different name to avoid conflict
        stub_name = temp_dir / "workflow_template.md"
        result2 = runner.invoke(cli, [
            'generate-stub', str(schema_file), '--output', str(stub_name)
        ])
        assert result2.exit_code == 0
        assert stub_name.exists()

        # Step 3: Validate original against its schema
        result3 = runner.invoke(cli, [
            'validate', str(original_md)
        ])
        assert result3.exit_code == 0

        # Step 4: List associated files
        result4 = runner.invoke(cli, [
            'associated-files', 'status', str(temp_dir)
        ])
        assert result4.exit_code == 0
        assert 'paired' in result4.output.lower() or 'orphaned' in result4.output.lower()