#!/usr/bin/env python3
"""
Test Scenario Runner for SQLFluff Schemachange Templater.

This script runs comprehensive tests on all scenarios generated by test_generator.py.
It validates that SQLFluff can properly lint and render all the generated test files.
"""

import json
import subprocess
import sys
from pathlib import Path
from typing import Any, Dict, List


class TestScenarioRunner:
    """Run tests on all generated scenarios."""

    def __init__(self, temp_dir: str = "temp"):
        self.temp_dir = Path(temp_dir)
        self.results: Dict[str, Dict] = {}
        self.failures: List[str] = []

    def run_sqlfluff_test(self, test_dir: Path, test_name: str) -> Dict[str, Any]:
        """Run SQLFluff tests on a specific test directory."""
        result: Dict[str, Any] = {
            "test_name": test_name,
            "directory": str(test_dir),
            "lint_tests": [],
            "render_tests": [],
            "success": True,
            "errors": [],
        }

        try:
            # Find all SQL files in the test directory
            sql_files = list(test_dir.rglob("*.sql"))

            if not sql_files:
                result["errors"].append("No SQL files found")
                result["success"] = False
                return result

            # Test linting
            for sql_file in sql_files:
                try:
                    # Run SQLFluff lint
                    lint_result = subprocess.run(
                        [
                            "sqlfluff",
                            "lint",
                            str(sql_file),
                            "--dialect",
                            "snowflake",
                            "--format",
                            "json",
                        ],
                        capture_output=True,
                        text=True,
                        timeout=60,
                    )

                    result["lint_tests"].append(
                        {
                            "file": str(sql_file),
                            "returncode": lint_result.returncode,
                            "output": lint_result.stdout,
                            "errors": lint_result.stderr,
                        }
                    )

                    if lint_result.returncode != 0:
                        result["errors"].append(f"Linting failed for {sql_file}")
                        result["success"] = False

                except subprocess.TimeoutExpired:
                    result["errors"].append(f"Linting timed out for {sql_file}")
                    result["success"] = False
                except Exception as e:
                    result["errors"].append(f"Linting error for {sql_file}: {e}")
                    result["success"] = False

            # Test rendering
            for sql_file in sql_files:
                try:
                    # Run SQLFluff render
                    render_result = subprocess.run(
                        ["sqlfluff", "render", str(sql_file), "--dialect", "snowflake"],
                        capture_output=True,
                        text=True,
                        timeout=60,
                    )

                    result["render_tests"].append(
                        {
                            "file": str(sql_file),
                            "returncode": render_result.returncode,
                            "output": render_result.stdout,
                            "errors": render_result.stderr,
                        }
                    )

                    if render_result.returncode != 0:
                        result["errors"].append(f"Rendering failed for {sql_file}")
                        result["success"] = False

                except subprocess.TimeoutExpired:
                    result["errors"].append(f"Rendering timed out for {sql_file}")
                    result["success"] = False
                except Exception as e:
                    result["errors"].append(f"Rendering error for {sql_file}: {e}")
                    result["success"] = False

        except Exception as e:
            result["errors"].append(f"General error: {e}")
            result["success"] = False

        return result

    def run_schemachange_comparison(
        self, test_dir: Path, test_name: str
    ) -> Dict[str, Any]:
        """Run schemachange render tests for comparison."""
        result: Dict[str, Any] = {
            "test_name": f"{test_name}_schemachange",
            "directory": str(test_dir),
            "render_tests": [],
            "success": True,
            "errors": [],
        }

        try:
            # Find all SQL files in the test directory
            sql_files = list(test_dir.rglob("*.sql"))

            if not sql_files:
                result["errors"].append("No SQL files found")
                result["success"] = False
                return result

            # Test schemachange rendering
            for sql_file in sql_files:
                try:
                    # Run schemachange render
                    render_result = subprocess.run(
                        [
                            "schemachange",
                            "render",
                            str(sql_file),
                            "--config-folder",
                            str(test_dir),
                            "--dry-run",
                        ],
                        capture_output=True,
                        text=True,
                        timeout=60,
                    )

                    result["render_tests"].append(
                        {
                            "file": str(sql_file),
                            "returncode": render_result.returncode,
                            "output": render_result.stdout,
                            "errors": render_result.stderr,
                        }
                    )

                    # Note: schemachange may fail due to missing env vars,
                    # which is expected
                    if render_result.returncode != 0:
                        s = str(sql_file)
                        msg = f"Schema failed: {s}"
                        result["errors"].append(msg)

                except subprocess.TimeoutExpired:
                    msg = f"Schemachange rendering timed out for {sql_file}"
                    result["errors"].append(msg)
                except Exception as e:
                    msg = f"Schemachange error for {sql_file}: {e}"
                    result["errors"].append(msg)

        except Exception as e:
            result["errors"].append(f"General schemachange error: {e}")

        return result

    def run_all_tests(self):
        """Run tests on all generated scenarios."""
        print("🧪 Running comprehensive tests on generated scenarios...")
        print()

        if not self.temp_dir.exists():
            print(f"❌ Test directory {self.temp_dir} not found!")
            print("Run 'python test_generator.py' first to generate test scenarios.")
            return

        # Get all test directories (exclude existing_examples)
        test_dirs = [
            d
            for d in self.temp_dir.iterdir()
            if d.is_dir() and d.name != "existing_examples"
        ]

        if not test_dirs:
            print("❌ No test directories found!")
            return

        print(f"📁 Found {len(test_dirs)} test directories:")
        for test_dir in test_dirs:
            print(f"   • {test_dir.name}")

        print()
        print("=" * 60)

        # Test each directory
        for test_dir in test_dirs:
            test_name = test_dir.name
            print(f"\n🔍 Testing: {test_name}")
            print("-" * 40)

            # Run SQLFluff tests
            sqlfluff_result = self.run_sqlfluff_test(test_dir, test_name)
            self.results[test_name] = sqlfluff_result

            # Run schemachange comparison tests
            schemachange_result = self.run_schemachange_comparison(test_dir, test_name)
            self.results[f"{test_name}_schemachange"] = schemachange_result

            # Print results
            if sqlfluff_result["success"]:
                print(f"✅ {test_name}: SQLFluff tests passed")
            else:
                print(f"❌ {test_name}: SQLFluff tests failed")
                self.failures.append(test_name)

            # Print summary
            lint_count = len(sqlfluff_result["lint_tests"])
            render_count = len(sqlfluff_result["render_tests"])
            error_count = len(sqlfluff_result["errors"])

            print(
                f"   📊 Lint tests: {lint_count}, "
                f"Render tests: {render_count}, Errors: {error_count}"
            )

            if error_count > 0:
                for error in sqlfluff_result["errors"][:3]:  # Show first 3 errors
                    print(f"   ⚠️  {error}")

        print("\n" + "=" * 60)
        self.generate_report()

    def generate_report(self):
        """Generate a comprehensive test report."""
        total_tests = len(self.results)
        successful_tests = sum(1 for r in self.results.values() if r["success"])
        failed_tests = total_tests - successful_tests

        print("\n📊 Test Summary:")
        print(f"   Total tests: {total_tests}")
        print(f"   Successful: {successful_tests}")
        print(f"   Failed: {failed_tests}")
        print(f"   Success rate: {(successful_tests/total_tests)*100:.1f}%")

        if self.failures:
            print("\n❌ Failed tests:")
            for failure in self.failures:
                print(f"   • {failure}")

        # Save detailed report
        report_path = Path("test_scenario_report.json")
        with open(report_path, "w", encoding="utf-8") as f:
            json.dump(self.results, f, indent=2, default=str)

        print(f"\n📄 Detailed report saved to: {report_path}")

        # Generate markdown report
        self.generate_markdown_report()

    def generate_markdown_report(self):
        """Generate a markdown test report."""
        report_lines = [
            "# Test Scenario Report",
            "",
            f"Generated: {Path().cwd()}",
            "",
            "## Summary",
            f"- Total test scenarios: {len(self.results)}",
            f"- Successful: {sum(1 for r in self.results.values() if r['success'])}",
            f"- Failed: {sum(1 for r in self.results.values() if not r['success'])}",
            "",
            "## Detailed Results",
            "",
        ]

        for test_name, result in self.results.items():
            status = "✅ PASS" if result["success"] else "❌ FAIL"
            report_lines.extend(
                [
                    f"### {test_name} {status}",
                    f"- Directory: `{result['directory']}`",
                    f"- Lint tests: {len(result.get('lint_tests', []))}",
                    f"- Render tests: {len(result.get('render_tests', []))}",
                    f"- Errors: {len(result.get('errors', []))}",
                    "",
                ]
            )

            if result.get("errors"):
                report_lines.append("#### Errors:")
                for error in result["errors"]:
                    report_lines.append(f"- {error}")
                report_lines.append("")

        report_path = Path("test_scenario_report.md")
        with open(report_path, "w", encoding="utf-8") as f:
            f.write("\n".join(report_lines))

        print(f"📝 Markdown report saved to: {report_path}")


def main():
    """Main entry point."""
    if len(sys.argv) > 1 and sys.argv[1] == "--help":
        print("Test Scenario Runner")
        print("Usage: python test_scenario_runner.py")
        print()
        print("This script tests all scenarios generated by test_generator.py")
        print("Make sure to run 'python test_generator.py' first!")
        return

    runner = TestScenarioRunner()
    runner.run_all_tests()


if __name__ == "__main__":
    main()
