#!/usr/bin/env python3
"""
SPDX-License-Identifier: Apache-2.0
Copyright Contributors to the ODPi Egeria project.

Scenario-based testing for Community Matters with synthetic data.

This executable test script runs realistic scenarios for community management,
including creating communities, managing members, and organizing resources,
with comprehensive cleanup and detailed error reporting.

Usage:
    python tests/test_community_matters_scenarios.py
    
    Or with pytest:
    pytest tests/test_community_matters_scenarios.py -v -s
"""

import sys
import time
from datetime import datetime
from typing import List, Optional
from dataclasses import dataclass, field

from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, TextColumn

from pyegeria.omvs.community_matters_omvs import CommunityMatters
from pyegeria.core._exceptions import (
    PyegeriaException,
    PyegeriaAPIException,
    PyegeriaTimeoutException,
    print_exception_table,
)

# Configuration
VIEW_SERVER = "qs-view-server"
PLATFORM_URL = "https://localhost:9443"
USER_ID = "peterprofile"
USER_PWD = "secret"

console = Console(width=200)


@dataclass
class TestResult:
    """Track results of test scenarios"""
    scenario_name: str
    status: str  # "PASSED", "FAILED", "WARNING"
    duration: float
    message: str = ""
    error: Optional[Exception] = None
    created_guids: List[str] = field(default_factory=list)
    

@dataclass
class CommunityData:
    """Synthetic community data"""
    qualified_name: str
    display_name: str
    description: str
    mission: str
    guid: Optional[str] = None


class CommunityMattersScenarioTester:
    """Execute realistic community management scenarios"""
    
    def __init__(self):
        self.client: Optional[CommunityMatters] = None
        self.results: List[TestResult] = []
        self.created_communities: List[str] = []
        self.test_run_id = datetime.now().strftime("%Y%m%d_%H%M%S")
        
    def setup(self) -> bool:
        """Initialize connection to Egeria"""
        try:
            console.print("\n[bold cyan]═══ Setting up Community Matters Test Environment ═══[/bold cyan]\n")
            self.client = CommunityMatters(VIEW_SERVER, PLATFORM_URL, user_id=USER_ID, user_pwd=USER_PWD)
            token = self.client.create_egeria_bearer_token(USER_ID, USER_PWD)
            console.print(f"✓ Connected to {PLATFORM_URL}")
            console.print(f"✓ Authenticated as {USER_ID}")
            console.print(f"✓ Test Run ID: {self.test_run_id}\n")
            return True
        except Exception as e:
            console.print(f"[bold red]✗ Setup failed: {str(e)}[/bold red]")
            return False
    
    def teardown(self):
        """Clean up and close connection"""
        if self.client:
            self.client.close_session()
            console.print("\n✓ Session closed")
    
    def cleanup_created_communities(self):
        """Delete all communities created during testing"""
        console.print("\n[bold yellow]═══ Cleaning Up Test Data ═══[/bold yellow]\n")
        
        if not self.created_communities:
            console.print("No communities to clean up")
            return
        
        cleanup_results = {"success": 0, "failed": 0, "not_found": 0}
        
        with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console) as progress:
            task = progress.add_task(f"Cleaning up {len(self.created_communities)} communities...", total=len(self.created_communities))
            
            for guid in self.created_communities:
                try:
                    delete_body = {"class": "DeleteElementRequestBody"}
                    self.client.delete_community(guid, delete_body)
                    cleanup_results["success"] += 1
                except PyegeriaAPIException:
                    cleanup_results["not_found"] += 1
                except Exception as e:
                    console.print(f"  [yellow]⚠ Failed to delete {guid}: {str(e)}[/yellow]")
                    cleanup_results["failed"] += 1
                progress.advance(task)
        
        console.print(f"\n✓ Cleanup complete:")
        console.print(f"  • Successfully deleted: {cleanup_results['success']}")
        console.print(f"  • Not found (already deleted): {cleanup_results['not_found']}")
        console.print(f"  • Failed: {cleanup_results['failed']}")
    
    def print_results_summary(self):
        """Print a summary table of all test results"""
        console.print("\n[bold cyan]═══ Test Results Summary ═══[/bold cyan]\n")
        
        table = Table(show_header=True, header_style="bold magenta", show_lines=True)
        table.add_column("Scenario", style="cyan", width=50)
        table.add_column("Status", justify="center", width=12)
        table.add_column("Duration (s)", justify="right", width=15)
        table.add_column("Message", width=60)
        
        passed = failed = warnings = 0
        
        for result in self.results:
            if result.status == "PASSED":
                status_style = "[green]✓ PASSED[/green]"
                passed += 1
            elif result.status == "FAILED":
                status_style = "[red]✗ FAILED[/red]"
                failed += 1
            else:
                status_style = "[yellow]⚠ WARNING[/yellow]"
                warnings += 1
            
            table.add_row(
                result.scenario_name,
                status_style,
                f"{result.duration:.3f}",
                result.message[:60] if result.message else ""
            )
        
        console.print(table)
        
        # Summary stats
        total = len(self.results)
        console.print(f"\n[bold]Total Scenarios:[/bold] {total}")
        console.print(f"[green]Passed:[/green] {passed}")
        console.print(f"[red]Failed:[/red] {failed}")
        console.print(f"[yellow]Warnings:[/yellow] {warnings}")
        
        if failed > 0:
            console.print("\n[bold red]Some scenarios failed. Check the details above.[/bold red]")
        elif warnings > 0:
            console.print("\n[bold yellow]All scenarios passed with some warnings.[/bold yellow]")
        else:
            console.print("\n[bold green]All scenarios passed successfully! ✓[/bold green]")
    
    def _create_test_community(self, prefix: str = "TestCommunity") -> CommunityData:
        """Helper to create a test community with unique name"""
        ts = datetime.now().strftime("%Y%m%d%H%M%S%f")
        return CommunityData(
            qualified_name=f"{prefix}::{ts}",
            display_name=f"{prefix} {ts}",
            description=f"Test community created at {datetime.now().isoformat()}",
            mission=f"Mission for {prefix}"
        )
    
    def scenario_community_lifecycle(self) -> TestResult:
        """Scenario: Complete community lifecycle - create, read, update, delete"""
        scenario_name = "Community Lifecycle"
        start_time = time.perf_counter()
        created_guids = []
        
        try:
            console.print(f"\n[bold blue]▶ Running: {scenario_name}[/bold blue]")
            
            # CREATE: Create a new community
            console.print("  → Creating community...")
            community_data = self._create_test_community("LifecycleTest")
            
            body = {
                "class": "NewElementRequestBody",
                "typeName": "Community",
                "initialStatus": "ACTIVE",
                "properties": {
                    "class": "CommunityProperties",
                    "qualifiedName": community_data.qualified_name,
                    "displayName": community_data.display_name,
                    "description": community_data.description,
                    "mission": community_data.mission
                }
            }
            
            guid = self.client.create_community(body=body)
            
            if guid:
                created_guids.append(guid)
                self.created_communities.append(guid)
                community_data.guid = guid
                console.print(f"  ✓ Created community: {guid}")
            else:
                raise Exception("Failed to create community - no GUID returned")
            
            # READ: Retrieve the community
            console.print("  → Retrieving community...")
            retrieved = self.client.get_community_by_guid(guid, output_format="JSON")
            
            if isinstance(retrieved, dict):
                console.print(f"  ✓ Retrieved community: {retrieved.get('properties', {}).get('displayName', 'Unknown')}")
            else:
                raise Exception("Failed to retrieve community")
            
            # UPDATE: Update the community
            console.print("  → Updating community...")
            update_body = {
                "class": "UpdateElementRequestBody",
                "properties": {
                    "class": "CommunityProperties",
                    "qualifiedName": community_data.qualified_name,
                    "displayName": f"{community_data.display_name} (Updated)",
                    "description": f"{community_data.description} - Updated",
                    "mission": f"{community_data.mission} - Updated"
                }
            }
            
            self.client.update_community(guid, update_body)
            console.print("  ✓ Updated community")
            
            # VERIFY UPDATE
            updated = self.client.get_community_by_guid(guid, output_format="JSON")
            if isinstance(updated, dict):
                updated_name = updated.get('properties', {}).get('displayName', '')
                if "(Updated)" in updated_name:
                    console.print("  ✓ Verified update")
                else:
                    console.print("  [yellow]⚠ Update verification inconclusive[/yellow]")
            
            # SEARCH: Find the community
            console.print("  → Searching for community...")
            search_results = self.client.find_communities(
                search_string=community_data.display_name[:10],
                output_format="JSON"
            )
            
            if isinstance(search_results, list) and len(search_results) > 0:
                console.print(f"  ✓ Found {len(search_results)} communities in search")
            
            # DELETE: Delete the community
            console.print("  → Deleting community...")
            delete_body = {"class": "DeleteElementRequestBody"}
            self.client.delete_community(guid, delete_body)
            self.created_communities.remove(guid)
            console.print("  ✓ Deleted community")
            
            # VERIFY DELETION
            try:
                after_delete = self.client.get_community_by_guid(guid, output_format="JSON")
                console.print("  [yellow]⚠ Community still exists after deletion[/yellow]")
            except PyegeriaAPIException:
                console.print("  ✓ Verified deletion")
            
            duration = time.perf_counter() - start_time
            return TestResult(
                scenario_name=scenario_name,
                status="PASSED",
                duration=duration,
                message="Complete lifecycle executed successfully",
                created_guids=created_guids
            )
            
        except Exception as e:
            duration = time.perf_counter() - start_time
            if isinstance(e, PyegeriaTimeoutException):
                console.print(f"  [yellow]⚠ Timeout in {scenario_name}; continuing.[/yellow]")
                return TestResult(
                    scenario_name=scenario_name,
                    status="WARNING",
                    duration=duration,
                    message=f"Timeout: {str(e)[:100]}",
                    error=e,
                    created_guids=created_guids
                )
            console.print(f"  [red]✗ Error: {str(e)}[/red]")
            print_exception_table(e) if isinstance(e, PyegeriaException) else console.print_exception()
            return TestResult(
                scenario_name=scenario_name,
                status="FAILED",
                duration=duration,
                message=str(e)[:100],
                error=e,
                created_guids=created_guids
            )
    
    def scenario_multiple_communities(self) -> TestResult:
        """Scenario: Create and manage multiple communities"""
        scenario_name = "Multiple Communities Management"
        start_time = time.perf_counter()
        created_guids = []
        
        try:
            console.print(f"\n[bold blue]▶ Running: {scenario_name}[/bold blue]")
            
            # Create multiple communities
            console.print("  → Creating multiple communities...")
            num_communities = 3
            for i in range(num_communities):
                community_data = self._create_test_community(f"MultiCommunity{i+1}")
                body = {
                    "class": "NewElementRequestBody",
                    "typeName": "Community",
                    "initialStatus": "ACTIVE",
                    "properties": {
                        "class": "CommunityProperties",
                        "qualifiedName": community_data.qualified_name,
                        "displayName": community_data.display_name,
                        "description": community_data.description,
                        "mission": community_data.mission
                    }
                }
                
                guid = self.client.create_community(body=body)
                
                if guid:
                    created_guids.append(guid)
                    self.created_communities.append(guid)
                    console.print(f"  ✓ Created community {i+1}: {guid}")
            
            # Search for communities
            console.print("  → Searching for created communities...")
            search_results = self.client.find_communities(
                search_string="MultiCommunity",
                output_format="JSON"
            )
            
            if isinstance(search_results, list):
                console.print(f"  ✓ Found {len(search_results)} communities matching search")
            
            # Get communities by name
            console.print("  → Getting communities by name...")
            name_results = self.client.get_communities_by_name(
                filter_string="MultiCommunity",
                output_format="JSON"
            )
            
            if isinstance(name_results, list):
                console.print(f"  ✓ Found {len(name_results)} communities by name")
            
            duration = time.perf_counter() - start_time
            return TestResult(
                scenario_name=scenario_name,
                status="PASSED",
                duration=duration,
                message=f"Created and managed {num_communities} communities successfully",
                created_guids=created_guids
            )
            
        except Exception as e:
            duration = time.perf_counter() - start_time
            if isinstance(e, PyegeriaTimeoutException):
                console.print(f"  [yellow]⚠ Timeout in {scenario_name}; continuing.[/yellow]")
                return TestResult(
                    scenario_name=scenario_name,
                    status="WARNING",
                    duration=duration,
                    message=f"Timeout: {str(e)[:100]}",
                    error=e,
                    created_guids=created_guids
                )
            console.print(f"  [red]✗ Error: {str(e)}[/red]")
            print_exception_table(e) if isinstance(e, PyegeriaException) else console.print_exception()
            return TestResult(
                scenario_name=scenario_name,
                status="FAILED",
                duration=duration,
                message=str(e)[:100],
                error=e,
                created_guids=created_guids
            )
    
    def scenario_community_from_template(self) -> TestResult:
        """Scenario: Create community from template"""
        scenario_name = "Community from Template"
        start_time = time.perf_counter()
        created_guids = []
        
        try:
            console.print(f"\n[bold blue]▶ Running: {scenario_name}[/bold blue]")
            
            # First create a template community
            console.print("  → Creating template community...")
            template_data = self._create_test_community("TemplateCommunity")
            template_body = {
                "class": "NewElementRequestBody",
                "typeName": "Community",
                "properties": {
                    "class": "CommunityProperties",
                    "qualifiedName": template_data.qualified_name,
                    "displayName": template_data.display_name,
                    "description": "Template for creating other communities",
                    "mission": "Template mission"
                }
            }
            
            template_guid = self.client.create_community(body=template_body)
            
            if template_guid:
                created_guids.append(template_guid)
                self.created_communities.append(template_guid)
                console.print(f"  ✓ Created template community: {template_guid}")
            else:
                raise Exception("Failed to create template community")
            
            # Now create a community from the template
            console.print("  → Creating community from template...")
            from_template_body = {
                "class": "TemplateRequestBody",
                "templateGUID": template_guid,
                "replacementProperties": {
                    "class": "AssetProperties",
                    "displayName": "Community from Template",
                    "qualifiedName": template_data.qualified_name,
                    "description": template_data.description,
                }
            }

            new_community_guid = self.client.create_community_from_template(body=from_template_body)
            
            if new_community_guid:
                created_guids.append(new_community_guid)
                self.created_communities.append(new_community_guid)
                console.print(f"  ✓ Created community from template: {new_community_guid}")
            else:
                console.print("  [yellow]⚠ Template creation may not be fully supported[/yellow]")
            
            duration = time.perf_counter() - start_time
            return TestResult(
                scenario_name=scenario_name,
                status="PASSED",
                duration=duration,
                message="Template-based community creation tested",
                created_guids=created_guids
            )
            
        except Exception as e:
            duration = time.perf_counter() - start_time
            if isinstance(e, PyegeriaTimeoutException):
                console.print(f"  [yellow]⚠ Timeout in {scenario_name}; continuing.[/yellow]")
                return TestResult(
                    scenario_name=scenario_name,
                    status="WARNING",
                    duration=duration,
                    message=f"Timeout: {str(e)[:100]}",
                    error=e,
                    created_guids=created_guids
                )
            console.print(f"  [red]✗ Error: {str(e)}[/red]")
            print_exception_table(e) if isinstance(e, PyegeriaException) else console.print_exception()
            return TestResult(
                scenario_name=scenario_name,
                status="FAILED",
                duration=duration,
                message=str(e)[:100],
                error=e,
                created_guids=created_guids
            )
    
    def scenario_community_search_variations(self) -> TestResult:
        """Scenario: Test various search methods for communities"""
        scenario_name = "Community Search Variations"
        start_time = time.perf_counter()
        created_guids = []
        
        try:
            console.print(f"\n[bold blue]▶ Running: {scenario_name}[/bold blue]")
            
            # Create a community with specific characteristics
            console.print("  → Creating community for search testing...")
            unique_prefix = f"SearchTest{int(time.time())}"
            community_data = self._create_test_community(unique_prefix)
            
            body = {
                "class": "NewElementRequestBody",
                "typeName": "Community",
                "initialStatus": "ACTIVE",
                "properties": {
                    "class": "CommunityProperties",
                    "qualifiedName": community_data.qualified_name,
                    "displayName": community_data.display_name,
                    "description": community_data.description,
                    "mission": community_data.mission
                }
            }
            
            guid = self.client.create_community(body=body)
            
            if guid:
                created_guids.append(guid)
                self.created_communities.append(guid)
                console.print(f"  ✓ Created community: {guid}")
            
            # Test 1: Search with wildcard
            console.print("  → Testing case-insensitive search...")
            case_results = self.client.find_communities(
                search_string=unique_prefix.lower(),
                ignore_case=True,
                output_format="JSON"
            )
            console.print(
                f"  ✓ Case-insensitive search found {len(case_results) if isinstance(case_results, list) else 0} communities")

            # Test 2: Search with specific string
            console.print("  → Testing specific string search...")
            specific_results = self.client.find_communities(
                search_string=unique_prefix,
                output_format="JSON"
            )
            console.print(f"  ✓ Specific search found {len(specific_results) if isinstance(specific_results, list) else 0} communities")
            
            # Test 3: Get by name filter
            console.print("  → Testing name filter...")
            name_results = self.client.get_communities_by_name(
                filter_string=unique_prefix,
                output_format="JSON"
            )
            console.print(f"  ✓ Name filter found {len(name_results) if isinstance(name_results, list) else 0} communities")
            
            # Test 4: Case-insensitive search
            console.print("  → Testing wildcard search...")
            wildcard_results = self.client.find_communities(
                search_string="Mu",
                output_format="JSON"
            )
            console.print(
                f"  ✓ Wildcard search found {len(wildcard_results) if isinstance(wildcard_results, list) else 0} communities")

            duration = time.perf_counter() - start_time
            return TestResult(
                scenario_name=scenario_name,
                status="PASSED",
                duration=duration,
                message="All search variations tested successfully",
                created_guids=created_guids
            )
            
        except Exception as e:
            duration = time.perf_counter() - start_time
            if isinstance(e, PyegeriaTimeoutException):
                console.print(f"  [yellow]⚠ Timeout in {scenario_name}; continuing.[/yellow]")
                return TestResult(
                    scenario_name=scenario_name,
                    status="WARNING",
                    duration=duration,
                    message=f"Timeout: {str(e)[:100]}",
                    error=e,
                    created_guids=created_guids
                )
            console.print(f"  [red]✗ Error: {str(e)}[/red]")
            print_exception_table(e) if isinstance(e, PyegeriaException) else console.print_exception()
            return TestResult(
                scenario_name=scenario_name,
                status="FAILED",
                duration=duration,
                message=str(e)[:100],
                error=e,
                created_guids=created_guids
            )
    
    def run_all_scenarios(self):
        """Execute all test scenarios"""
        console.print(Panel.fit(
            "[bold cyan]Community Matters Scenario Tests[/bold cyan]\n"
            f"Test Run ID: {self.test_run_id}",
            border_style="cyan"
        ))
        
        if not self.setup():
            console.print("[bold red]Setup failed. Cannot proceed with tests.[/bold red]")
            return False
        
        try:
            # Run all scenarios
            self.results.append(self.scenario_community_lifecycle())
            self.results.append(self.scenario_multiple_communities())
            self.results.append(self.scenario_community_from_template())
            self.results.append(self.scenario_community_search_variations())
            
            # Print summary
            self.print_results_summary()
            
            # Cleanup
            self.cleanup_created_communities()
            
            # Return success if no failures
            return all(r.status != "FAILED" for r in self.results)
            
        finally:
            self.teardown()


def test_community_matters_scenarios():
    """Pytest entry point for community matters scenario tests"""
    tester = CommunityMattersScenarioTester()
    success = tester.run_all_scenarios()
    assert success, "One or more scenarios failed"


if __name__ == "__main__":
    """Direct execution entry point"""
    tester = CommunityMattersScenarioTester()
    success = tester.run_all_scenarios()
    sys.exit(0 if success else 1)
