"""
Tests for MarkiTect user profile CLI commands.

This module tests the command-line interface for user profile management
including creation, listing, updating, and template variable extraction.
"""

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

from markitect.profile.commands import profile_commands
from markitect.profile.manager import ProfileManager
from markitect.profile.schema import ProfileData, ContactInfo, Organization


class TestProfileCLICommands:
    """Test suite for profile management CLI commands."""

    @pytest.fixture
    def temp_db(self):
        """Create temporary database for testing."""
        fd, path = tempfile.mkstemp(suffix='.db')
        os.close(fd)
        yield path
        os.unlink(path)

    @pytest.fixture
    def setup_test_profile(self, temp_db):
        """Setup test database with a sample profile."""
        profile_manager = ProfileManager(temp_db)
        profile_data = ProfileData(
            first_name="John",
            last_name="Doe",
            contact=ContactInfo(email="john@example.com", phone="555-0123"),
            organization=Organization(name="Tech Corp", position="Developer")
        )
        profile_id = profile_manager.create_profile("test_profile", profile_data, "Test profile")
        return temp_db, profile_id

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

    def test_profile_create_basic(self, runner, temp_db):
        """Test basic profile creation."""
        result = runner.invoke(profile_commands, [
            'create', 'basic_profile',
            '--first-name', 'Alice',
            '--last-name', 'Smith',
            '--email', 'alice@example.com',
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "✅ Created profile 'basic_profile'" in result.output

        # Verify profile was created
        profile_manager = ProfileManager(temp_db)
        profile = profile_manager.get_profile('basic_profile')
        assert profile.first_name == "Alice"
        assert profile.contact.email == "alice@example.com"

    def test_profile_create_with_organization(self, runner, temp_db):
        """Test profile creation with organization info."""
        result = runner.invoke(profile_commands, [
            'create', 'work_profile',
            '--first-name', 'Bob',
            '--organization', 'ACME Corp',
            '--position', 'Manager',
            '--city', 'New York',
            '--description', 'My work profile',
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "✅ Created profile 'work_profile'" in result.output

        # Verify organization details
        profile_manager = ProfileManager(temp_db)
        profile = profile_manager.get_profile('work_profile')
        assert profile.organization.name == "ACME Corp"
        assert profile.organization.position == "Manager"
        assert profile.address.city == "New York"

    def test_profile_create_set_default(self, runner, temp_db):
        """Test creating profile and setting as default."""
        result = runner.invoke(profile_commands, [
            'create', 'default_profile',
            '--first-name', 'Default',
            '--set-default',
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "✅ Created profile 'default_profile'" in result.output
        assert "🎯 Set 'default_profile' as default profile" in result.output

        # Verify it's set as default
        profile_manager = ProfileManager(temp_db)
        default_profile = profile_manager.get_default_profile()
        assert default_profile.first_name == "Default"

    def test_profile_create_duplicate_name(self, runner, setup_test_profile):
        """Test creating profile with duplicate name fails."""
        temp_db, _ = setup_test_profile

        result = runner.invoke(profile_commands, [
            'create', 'test_profile',  # Same name as existing
            '--first-name', 'Duplicate',
            '--database', temp_db
        ])

        assert result.exit_code == 1
        assert "already exists" in result.output

    def test_profile_show_table_format(self, runner, setup_test_profile):
        """Test showing profile in table format."""
        temp_db, _ = setup_test_profile

        result = runner.invoke(profile_commands, [
            'show', 'test_profile',
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "👤 Profile: test_profile" in result.output
        assert "First Name: John" in result.output
        assert "📞 Contact Information" in result.output
        assert "Email: john@example.com" in result.output
        assert "🏢 Organization" in result.output
        assert "Organization: Tech Corp" in result.output

    def test_profile_show_json_format(self, runner, setup_test_profile):
        """Test showing profile in JSON format."""
        temp_db, _ = setup_test_profile

        result = runner.invoke(profile_commands, [
            'show', 'test_profile',
            '--format', 'json',
            '--database', temp_db
        ])

        assert result.exit_code == 0

        # Parse JSON output
        data = json.loads(result.output)
        assert "profile_info" in data
        assert "profile_data" in data
        assert data["profile_info"]["name"] == "test_profile"
        assert data["profile_data"]["first_name"] == "John"

    def test_profile_show_nonexistent(self, runner, temp_db):
        """Test showing non-existent profile."""
        result = runner.invoke(profile_commands, [
            'show', 'nonexistent',
            '--database', temp_db
        ])

        assert result.exit_code == 1
        assert "not found" in result.output

    def test_profile_list_empty(self, runner, temp_db):
        """Test listing profiles when none exist."""
        result = runner.invoke(profile_commands, [
            'list',
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "No profiles found" in result.output

    def test_profile_list_with_profiles(self, runner, temp_db):
        """Test listing profiles."""
        # Create multiple profiles
        profile_manager = ProfileManager(temp_db)
        profile_manager.create_profile("profile1", ProfileData(first_name="User1"), "First profile")
        profile_manager.create_profile("profile2", ProfileData(first_name="User2"), set_as_default=True)

        result = runner.invoke(profile_commands, [
            'list',
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "👤 User Profiles" in result.output
        assert "profile1" in result.output
        assert "profile2" in result.output
        assert "Total: 2 profiles" in result.output

        # Check default indicator
        lines = result.output.split('\n')
        profile2_line = [line for line in lines if 'profile2' in line][0]
        assert "Yes" in profile2_line  # Default column should show "Yes"

    def test_profile_list_include_inactive(self, runner, temp_db):
        """Test listing profiles including inactive ones."""
        profile_manager = ProfileManager(temp_db)
        profile_manager.create_profile("active", ProfileData(first_name="Active"))
        profile_manager.create_profile("inactive", ProfileData(first_name="Inactive"))
        profile_manager.delete_profile("inactive", hard_delete=False)  # Soft delete

        # List without inactive
        result = runner.invoke(profile_commands, [
            'list',
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "active" in result.output
        assert "inactive" not in result.output

        # List with inactive
        result = runner.invoke(profile_commands, [
            'list',
            '--include-inactive',
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "active" in result.output
        assert "inactive" in result.output

    def test_profile_list_json_format(self, runner, setup_test_profile):
        """Test listing profiles in JSON format."""
        temp_db, _ = setup_test_profile

        result = runner.invoke(profile_commands, [
            'list',
            '--format', 'json',
            '--database', temp_db
        ])

        assert result.exit_code == 0

        # Parse JSON output
        data = json.loads(result.output)
        assert isinstance(data, list)
        assert len(data) == 1
        assert data[0]["name"] == "test_profile"

    def test_profile_update_basic_fields(self, runner, setup_test_profile):
        """Test updating basic profile fields."""
        temp_db, _ = setup_test_profile

        result = runner.invoke(profile_commands, [
            'update', 'test_profile',
            '--first-name', 'Johnny',
            '--email', 'johnny@example.com',
            '--organization', 'New Corp',
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "✅ Updated profile 'test_profile'" in result.output

        # Verify updates
        profile_manager = ProfileManager(temp_db)
        profile = profile_manager.get_profile('test_profile')
        assert profile.first_name == "Johnny"
        assert profile.contact.email == "johnny@example.com"
        assert profile.organization.name == "New Corp"

    def test_profile_update_nonexistent(self, runner, temp_db):
        """Test updating non-existent profile."""
        result = runner.invoke(profile_commands, [
            'update', 'nonexistent',
            '--first-name', 'New',
            '--database', temp_db
        ])

        assert result.exit_code == 1
        assert "not found" in result.output

    def test_profile_delete_soft(self, runner, setup_test_profile):
        """Test soft delete (deactivate) profile."""
        temp_db, _ = setup_test_profile

        result = runner.invoke(profile_commands, [
            'delete', 'test_profile',
            '--yes',  # Skip confirmation
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "✅ Profile 'test_profile' deactivated" in result.output

        # Verify profile is deactivated
        profile_manager = ProfileManager(temp_db)
        with pytest.raises(Exception):  # ProfileNotFoundError
            profile_manager.get_profile('test_profile')

    def test_profile_delete_hard(self, runner, setup_test_profile):
        """Test hard delete (permanent) profile."""
        temp_db, _ = setup_test_profile

        result = runner.invoke(profile_commands, [
            'delete', 'test_profile',
            '--hard',
            '--yes',
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "✅ Profile 'test_profile' deleted permanently" in result.output

        # Verify profile is completely gone
        profile_manager = ProfileManager(temp_db)
        all_profiles = profile_manager.list_profiles(include_inactive=True)
        profile_names = [p["name"] for p in all_profiles]
        assert "test_profile" not in profile_names

    def test_profile_delete_with_confirmation(self, runner, setup_test_profile):
        """Test profile deletion with confirmation prompt."""
        temp_db, _ = setup_test_profile

        # Test declining confirmation
        result = runner.invoke(profile_commands, [
            'delete', 'test_profile',
            '--database', temp_db
        ], input='n\n')

        assert result.exit_code == 0
        assert "Deletion cancelled" in result.output

        # Profile should still exist
        profile_manager = ProfileManager(temp_db)
        profile = profile_manager.get_profile('test_profile')
        assert profile.first_name == "John"

    def test_profile_set_default(self, runner, temp_db):
        """Test setting profile as default."""
        # Create multiple profiles
        profile_manager = ProfileManager(temp_db)
        profile_manager.create_profile("profile1", ProfileData(first_name="User1"))
        profile_manager.create_profile("profile2", ProfileData(first_name="User2"))

        result = runner.invoke(profile_commands, [
            'set-default', 'profile2',
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "✅ Set 'profile2' as default profile" in result.output

        # Verify default was set
        default_profile = profile_manager.get_default_profile()
        assert default_profile.first_name == "User2"

    def test_profile_set_default_nonexistent(self, runner, temp_db):
        """Test setting non-existent profile as default."""
        result = runner.invoke(profile_commands, [
            'set-default', 'nonexistent',
            '--database', temp_db
        ])

        assert result.exit_code == 1
        assert "not found" in result.output

    def test_profile_export_json(self, runner, setup_test_profile):
        """Test exporting profile to JSON."""
        temp_db, _ = setup_test_profile

        result = runner.invoke(profile_commands, [
            'export', 'test_profile',
            '--format', 'json',
            '--database', temp_db
        ])

        assert result.exit_code == 0

        # Parse output as JSON
        data = json.loads(result.output)
        assert "profile_info" in data
        assert "profile_data" in data
        assert data["profile_data"]["first_name"] == "John"

    def test_profile_export_to_file(self, runner, setup_test_profile):
        """Test exporting profile to file."""
        temp_db, _ = setup_test_profile

        with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as f:
            output_file = f.name

        try:
            result = runner.invoke(profile_commands, [
                'export', 'test_profile',
                '--output', output_file,
                '--database', temp_db
            ])

            assert result.exit_code == 0
            assert f"✅ Exported profile 'test_profile' to {output_file}" in result.output

            # Verify file contents
            data = json.loads(Path(output_file).read_text())
            assert data["profile_data"]["first_name"] == "John"

        finally:
            os.unlink(output_file)

    def test_profile_import_json(self, runner, temp_db):
        """Test importing profile from JSON file."""
        import_data = {
            "profile_info": {"description": "Imported profile"},
            "profile_data": {
                "first_name": "Imported",
                "last_name": "User",
                "contact": {"email": "imported@example.com"}
            }
        }

        with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as f:
            json.dump(import_data, f)
            import_file = f.name

        try:
            result = runner.invoke(profile_commands, [
                'import', 'imported_profile', import_file,
                '--database', temp_db
            ])

            assert result.exit_code == 0
            assert "✅ Created profile 'imported_profile'" in result.output

            # Verify imported data
            profile_manager = ProfileManager(temp_db)
            profile = profile_manager.get_profile('imported_profile')
            assert profile.first_name == "Imported"
            assert profile.contact.email == "imported@example.com"

        finally:
            os.unlink(import_file)

    def test_profile_import_nonexistent_file(self, runner, temp_db):
        """Test importing from non-existent file."""
        result = runner.invoke(profile_commands, [
            'import', 'test', '/nonexistent/file.json',
            '--database', temp_db
        ])

        assert result.exit_code == 1
        assert "not found" in result.output

    def test_profile_import_overwrite(self, runner, setup_test_profile):
        """Test importing with overwrite flag."""
        temp_db, _ = setup_test_profile

        import_data = {
            "profile_data": {
                "first_name": "Overwritten",
                "contact": {"email": "overwritten@example.com"}
            }
        }

        with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as f:
            json.dump(import_data, f)
            import_file = f.name

        try:
            result = runner.invoke(profile_commands, [
                'import', 'test_profile', import_file,
                '--overwrite',
                '--database', temp_db
            ])

            assert result.exit_code == 0
            assert "✅ Updated profile 'test_profile'" in result.output

            # Verify overwrite
            profile_manager = ProfileManager(temp_db)
            profile = profile_manager.get_profile('test_profile')
            assert profile.first_name == "Overwritten"

        finally:
            os.unlink(import_file)

    def test_profile_variables_table_format(self, runner, setup_test_profile):
        """Test showing template variables in table format."""
        temp_db, _ = setup_test_profile

        result = runner.invoke(profile_commands, [
            'variables',
            '--profile', 'test_profile',
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "📋 Template Variables - test_profile" in result.output
        assert "first_name" in result.output
        assert "John" in result.output
        assert "contact.email" in result.output
        assert "organization.name" in result.output

    def test_profile_variables_json_format(self, runner, setup_test_profile):
        """Test showing template variables in JSON format."""
        temp_db, _ = setup_test_profile

        result = runner.invoke(profile_commands, [
            'variables',
            '--profile', 'test_profile',
            '--format', 'json',
            '--database', temp_db
        ])

        assert result.exit_code == 0

        # Parse JSON output
        data = json.loads(result.output)
        assert data["first_name"] == "John"
        assert data["contact.email"] == "john@example.com"
        assert data["organization.name"] == "Tech Corp"

    def test_profile_variables_default_profile(self, runner, temp_db):
        """Test showing variables from default profile."""
        # Create and set default profile
        profile_manager = ProfileManager(temp_db)
        profile_data = ProfileData(first_name="Default", last_name="User")
        profile_manager.create_profile("default", profile_data, set_as_default=True)

        result = runner.invoke(profile_commands, [
            'variables',  # No --profile specified, should use default
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "📋 Template Variables - (default)" in result.output
        assert "first_name" in result.output
        assert "Default" in result.output

    def test_profile_variables_no_default(self, runner, temp_db):
        """Test showing variables when no default profile set."""
        result = runner.invoke(profile_commands, [
            'variables',
            '--database', temp_db
        ])

        assert result.exit_code == 0
        assert "No default profile set" in result.output

    def test_profile_help_commands(self, runner):
        """Test help output for profile commands."""
        # Test main profile help
        result = runner.invoke(profile_commands, ['--help'])
        assert result.exit_code == 0
        assert "User profile management commands" in result.output

        # Test create help
        result = runner.invoke(profile_commands, ['create', '--help'])
        assert result.exit_code == 0
        assert "Create a new user profile" in result.output

        # Test show help
        result = runner.invoke(profile_commands, ['show', '--help'])
        assert result.exit_code == 0
        assert "Show profile details" in result.output

    def test_profile_commands_missing_database(self, runner):
        """Test profile commands without database specification."""
        # These should use default config path
        result = runner.invoke(profile_commands, [
            'list'
        ])

        # Should succeed with default database configuration
        assert result.exit_code == 0

    def test_complex_profile_workflow(self, runner, temp_db):
        """Test complex workflow with multiple operations."""
        # Create profile
        result = runner.invoke(profile_commands, [
            'create', 'workflow_test',
            '--first-name', 'Workflow',
            '--last-name', 'Test',
            '--email', 'workflow@example.com',
            '--organization', 'Test Corp',
            '--description', 'Workflow test profile',
            '--database', temp_db
        ])
        assert result.exit_code == 0

        # Update profile
        result = runner.invoke(profile_commands, [
            'update', 'workflow_test',
            '--first-name', 'Updated',
            '--position', 'Manager',
            '--database', temp_db
        ])
        assert result.exit_code == 0

        # Set as default
        result = runner.invoke(profile_commands, [
            'set-default', 'workflow_test',
            '--database', temp_db
        ])
        assert result.exit_code == 0

        # Show variables
        result = runner.invoke(profile_commands, [
            'variables',
            '--database', temp_db
        ])
        assert result.exit_code == 0
        assert "Updated" in result.output  # Updated name
        assert "Manager" in result.output  # New position

        # Export profile
        result = runner.invoke(profile_commands, [
            'export', 'workflow_test',
            '--database', temp_db
        ])
        assert result.exit_code == 0

        # Verify export contains updates
        data = json.loads(result.output)
        assert data["profile_data"]["first_name"] == "Updated"
        assert data["profile_data"]["organization"]["position"] == "Manager"