"""
End-to-end workflow execution for OOXML template testing.

Handles complete E2E workflow execution including template processing,
screenshot capture, and result validation.
"""

import hashlib
import json
import subprocess
import sys
import tempfile
import time
from pathlib import Path
from typing import Dict, List, Any, Optional

import structlog

from .before_after import BeforeAfterComparator
from .screenshot_capture import (
    capture_template_screenshot,
    convert_template_to_pdf as capture_convert_template_to_pdf,
    convert_pdf_to_png as capture_convert_pdf_to_png,
    validate_libreoffice_availability,
)

try:
    from PIL import Image
except ImportError:
    Image = None

logger = structlog.get_logger(__name__)


def execute_complete_workflow(workspace_path: str) -> bool:
    """
    Execute complete E2E workflow from start to finish.

    Args:
        workspace_path: Path to workspace directory.

    Returns:
        bool: True if workflow successful, False otherwise.
    """
    try:
        logger.info(
            "Starting complete E2E workflow execution", workspace=workspace_path
        )

        workflow_steps = [
            ("validate_workspace", _validate_workspace),
            ("setup_directories", _setup_test_directories),
            ("process_templates", _process_all_templates),
            ("capture_screenshots", _capture_template_screenshots),
            ("run_comparisons", _run_baseline_comparisons),
            ("generate_reports", _generate_test_reports),
        ]

        overall_success = True

        for step_name, step_func in workflow_steps:
            logger.debug("Executing workflow step", step=step_name)

            try:
                success = step_func(workspace_path)
            except Exception as e:
                logger.error("Error in workflow step", step=step_name, error=str(e))
                success = False

            if not success:
                logger.error("Workflow step failed", step=step_name)
                overall_success = False

        if overall_success:
            logger.info("Complete E2E workflow execution successful")
        else:
            logger.error("Complete E2E workflow execution failed")

        return overall_success

    except Exception as e:
        logger.error("Error during complete workflow execution", error=str(e))
        return False


def test_libreoffice_e2e_setup() -> bool:
    """
    Test LibreOffice E2E setup and functionality.

    Returns:
        bool: True if setup test successful, False otherwise.
    """
    try:
        logger.info("Testing LibreOffice E2E setup")

        from .libreoffice_setup import (
            validate_headless_mode,
            test_libreoffice_conversion,
        )

        if not validate_libreoffice_availability():
            logger.error("LibreOffice not available for E2E setup test")
            return False

        if not validate_headless_mode():
            logger.error("LibreOffice headless validation failed")
            return False

        if not test_libreoffice_conversion():
            logger.error("LibreOffice conversion test failed")
            return False

        logger.info("LibreOffice E2E setup test completed successfully")
        return True

    except Exception as e:
        logger.error("Error in LibreOffice E2E setup test", error=str(e))
        return False


def generate_workflow_artifacts(artifacts_dir: str) -> bool:
    """
    Generate workflow artifacts for testing.

    Args:
        artifacts_dir: Directory to store artifacts.

    Returns:
        bool: True if artifact generation successful, False otherwise.
    """
    try:
        logger.info("Generating workflow artifacts", artifacts_dir=artifacts_dir)

        workspace = Path(artifacts_dir)
        if not workspace.exists():
            logger.error("Workspace directory does not exist", path=artifacts_dir)
            return False

        if not execute_complete_workflow(str(workspace)):
            logger.error("E2E workflow failed during artifact generation")
            return False

        expected_files = [
            _manifest_path(workspace),
            _comparison_report_path(workspace),
            _summary_report_path(workspace),
        ]
        missing = [path for path in expected_files if not path.exists()]
        if missing:
            logger.error(
                "Expected reports missing", missing=[str(path) for path in missing]
            )
            return False

        screenshots_dir = _reports_dirs(workspace)["screenshots"]
        screenshots = list(screenshots_dir.glob("*.png"))
        if not screenshots:
            logger.error("No screenshots produced during artifact generation")
            return False

        logger.info(
            "Workflow artifacts generated successfully", screenshots=len(screenshots)
        )
        return True

    except Exception as e:
        logger.error("Error generating workflow artifacts", error=str(e))
        return False


def test_failure_recovery() -> bool:
    """
    Test workflow failure recovery mechanisms.

    Returns:
        bool: True if recovery test successful, False otherwise.
    """
    try:
        logger.info("Testing failure recovery mechanisms")

        with tempfile.TemporaryDirectory() as temp_dir:
            temp_workspace = Path(temp_dir)
            temp_workspace.mkdir(parents=True, exist_ok=True)

            # Expect failure when no templates are available.
            failure_detected = not execute_complete_workflow(str(temp_workspace))
            if not failure_detected:
                logger.error("Expected workflow failure was not detected")
                return False

        logger.info("Failure detection test completed successfully")
        return True

    except Exception as e:
        logger.error("Error in failure recovery test", error=str(e))
        return False


def measure_workflow_performance(workspace_path: str) -> Dict[str, int]:
    """
    Measure workflow performance metrics.

    Returns:
        Dictionary with performance metrics.
    """
    try:
        logger.info("Measuring workflow performance")

        workspace = Path(workspace_path)
        if not workspace.exists():
            logger.error("Workspace does not exist", workspace=workspace_path)
            return {}

        setup_start = time.time()
        setup_ok = _setup_test_directories(workspace_path)
        setup_time = int(time.time() - setup_start)

        exec_start = time.time()
        execution_ok = (
            _process_all_templates(workspace_path)
            and _capture_template_screenshots(workspace_path)
            and _run_baseline_comparisons(workspace_path)
        )
        execution_time = int(time.time() - exec_start)

        cleanup_start = time.time()
        report_ok = _generate_test_reports(workspace_path)
        cleanup_time = int(time.time() - cleanup_start)

        total_time = setup_time + execution_time + cleanup_time
        metrics = {
            "setup_time": setup_time,
            "execution_time": execution_time,
            "cleanup_time": cleanup_time,
            "total_time": total_time,
            "setup_ok": setup_ok,
            "execution_ok": execution_ok,
            "report_ok": report_ok,
        }

        logger.info("Performance measurement completed", metrics=metrics)
        return metrics

    except Exception as e:
        logger.error("Error measuring workflow performance", error=str(e))
        return {}


def execute_matrix_strategy(
    matrix_config: Dict[str, List[str]],
    workspace_path: Optional[str] = None,
    output_dir: Optional[str] = None,
) -> List[Dict[str, Any]]:
    """
    Execute matrix strategy testing.

    Args:
        matrix_config: Matrix configuration.

    Returns:
        List of matrix execution results.
    """
    try:
        workspace = Path(workspace_path) if workspace_path else Path.cwd()
        output_root = (
            Path(output_dir) if output_dir else _reports_dirs(workspace)["screenshots"]
        )

        logger.info(
            "Executing matrix strategy", config=matrix_config, workspace=str(workspace)
        )

        from .screenshot_capture import (
            execute_matrix_strategy as capture_matrix_strategy,
        )

        return capture_matrix_strategy(matrix_config, str(workspace), str(output_root))

    except Exception as e:
        logger.error("Error executing matrix strategy", error=str(e))
        return []


def test_notification_system(scenario: Dict[str, str]) -> bool:
    """
    Test notification system with different scenarios.

    Args:
        scenario: Notification scenario to test.

    Returns:
        bool: True if notification test successful, False otherwise.
    """
    try:
        logger.info("Testing notification system", scenario=scenario)

        notification_type = scenario.get("type")
        message = scenario.get("message")

        if notification_type and message:
            logger.info(
                "Notification processed", type=notification_type, message=message
            )
            return True
        else:
            logger.error("Invalid notification scenario")
            return False

    except Exception as e:
        logger.error("Error testing notification system", error=str(e))
        return False


def test_resource_cleanup(workspace_path: str) -> bool:
    """
    Test resource cleanup functionality.

    Args:
        workspace_path: Path to workspace for cleanup testing.

    Returns:
        bool: True if cleanup test successful, False otherwise.
    """
    try:
        logger.info("Testing resource cleanup", workspace=workspace_path)

        workspace = Path(workspace_path)
        if not workspace.exists():
            logger.error("Workspace does not exist for cleanup test")
            return False

        # Check for and clean up temporary resources
        temp_dirs = ["temp_screenshots", "temp_pdfs", "temp_logs"]
        for temp_dir in temp_dirs:
            temp_path = workspace / temp_dir
            if temp_path.exists():
                logger.debug("Removing temporary directory", path=temp_dir)
                import shutil

                shutil.rmtree(temp_path)

        logger.info("Resource cleanup test completed successfully")
        return True

    except Exception as e:
        logger.error("Error testing resource cleanup", error=str(e))
        return False


def simulate_user_workflow() -> Dict[str, Dict[str, Any]]:
    """
    Simulate complete user workflow from template change to result.

    Returns:
        Dictionary with workflow step results.
    """
    try:
        logger.info("Executing complete user workflow")

        workflow_steps = [
            "template_change",
            "workflow_trigger",
            "environment_setup",
            "libreoffice_install",
            "template_processing",
            "screenshot_capture",
            "baseline_comparison",
            "result_reporting",
        ]

        start_time = time.time()
        success = execute_complete_workflow(str(Path.cwd()))
        duration = int(time.time() - start_time)
        status = "completed" if success else "failed"

        results = {
            step: {
                "status": status,
                "duration": duration,
                "timestamp": time.time(),
            }
            for step in workflow_steps
        }

        logger.info(
            "User workflow execution completed", steps=len(results), status=status
        )
        return results

    except Exception as e:
        logger.error("Error simulating user workflow", error=str(e))
        return {}


# Helper functions


def _reports_root(workspace: Path) -> Path:
    return workspace / "reports" / "e2e"


def _reports_dirs(workspace: Path) -> Dict[str, Path]:
    root = _reports_root(workspace)
    screenshots_dir = root / "screenshots"
    diffs_dir = root / "diffs"
    reports_dir = root / "reports"
    for directory in (screenshots_dir, diffs_dir, reports_dir):
        directory.mkdir(parents=True, exist_ok=True)
    return {
        "root": root,
        "screenshots": screenshots_dir,
        "diffs": diffs_dir,
        "reports": reports_dir,
    }


def _manifest_path(workspace: Path) -> Path:
    return _reports_dirs(workspace)["reports"] / "screenshot_manifest.json"


def _comparison_report_path(workspace: Path) -> Path:
    return _reports_dirs(workspace)["reports"] / "comparison_report.json"


def _summary_report_path(workspace: Path) -> Path:
    return _reports_dirs(workspace)["reports"] / "e2e_summary.json"


def _discover_templates(workspace: Path) -> List[Path]:
    search_roots = [
        workspace / "generated" / "templates",
        workspace / "generated",
    ]
    extensions = (".potx", ".dotx", ".xltx")
    templates: List[Path] = []
    seen: set[Path] = set()

    for root in search_roots:
        if not root.exists():
            continue
        for ext in extensions:
            for template_path in root.rglob(f"*{ext}"):
                if not template_path.is_file():
                    continue
                resolved = template_path.resolve()
                if resolved in seen:
                    continue
                seen.add(resolved)
                templates.append(template_path)

    templates.sort(key=lambda path: str(path))
    return templates


def _template_output_name(template_path: Path, workspace: Path) -> str:
    try:
        relative = template_path.relative_to(workspace)
        token = str(relative)
    except ValueError:
        token = str(template_path)
    digest = hashlib.sha1(token.encode("utf-8")).hexdigest()[:8]
    return f"{template_path.stem}-{digest}"


def _write_json_file(path: Path, payload: Dict[str, Any]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")


def _load_json_file(path: Path) -> Optional[Dict[str, Any]]:
    if not path.exists():
        return None
    try:
        return json.loads(path.read_text(encoding="utf-8"))
    except json.JSONDecodeError:
        logger.error("Failed to parse JSON report", path=str(path))
        return None


def _validate_template_structure(template_path: Path, workspace: Path) -> bool:
    validator_path = workspace / "bin" / "validate-template"
    if not validator_path.exists():
        logger.error("Template validator script not found", path=str(validator_path))
        return False

    cmd = [
        sys.executable,
        str(validator_path),
        "--template",
        str(template_path),
        "--format",
        "ooxml",
        "--extract",
    ]
    result = subprocess.run(
        cmd,
        cwd=str(workspace),
        capture_output=True,
        text=True,
    )

    if result.returncode != 0:
        logger.error(
            "Template validation failed",
            template=str(template_path),
            returncode=result.returncode,
            stderr=result.stderr.strip(),
        )
        return False

    return True


def _find_baseline_image(baselines_root: Path, template_stem: str) -> Optional[Path]:
    matches = list(baselines_root.rglob(f"{template_stem}.png"))
    if not matches:
        logger.error("Baseline image missing", template=template_stem)
        return None
    if len(matches) > 1:
        logger.error(
            "Multiple baseline images found",
            template=template_stem,
            matches=[str(path) for path in matches],
        )
        return None
    return matches[0]


def _validate_workspace(workspace_path: str) -> bool:
    """Validate workspace directory structure."""
    try:
        workspace = Path(workspace_path)
        return workspace.exists() and workspace.is_dir()
    except Exception:
        return False


def _setup_test_directories(workspace_path: str) -> bool:
    """Set up test directories."""
    try:
        workspace = Path(workspace_path)
        _reports_dirs(workspace)
        (workspace / "tests" / "baselines").mkdir(parents=True, exist_ok=True)
        return True
    except Exception as e:
        logger.error("Failed to set up test directories", error=str(e))
        return False


def _process_all_templates(workspace_path: str) -> bool:
    """Process all templates in workspace."""
    try:
        workspace = Path(workspace_path)
        templates = _discover_templates(workspace)
        if not templates:
            logger.error("No templates found for processing", workspace=workspace_path)
            return False

        logger.debug("Processing templates", count=len(templates))

        all_successful = True
        for template_path in templates:
            if not _validate_template_structure(template_path, workspace):
                all_successful = False

        return all_successful
    except Exception as e:
        logger.error("Error processing templates", error=str(e))
        return False


def _capture_template_screenshots(workspace_path: str) -> bool:
    """Capture screenshots of processed templates."""
    try:
        if not validate_libreoffice_availability():
            logger.error("LibreOffice not available for screenshot capture")
            return False

        workspace = Path(workspace_path)
        templates = _discover_templates(workspace)
        if not templates:
            logger.error(
                "No templates found for screenshot capture", workspace=workspace_path
            )
            return False

        directories = _reports_dirs(workspace)
        manifest_entries: List[Dict[str, Any]] = []

        all_successful = True
        for template_path in templates:
            output_name = _template_output_name(template_path, workspace)
            screenshot_path = directories["screenshots"] / f"{output_name}.png"

            success = capture_template_screenshot(
                str(template_path),
                str(screenshot_path),
            )

            if not success or not _validate_screenshot(str(screenshot_path)):
                logger.error("Screenshot capture failed", template=str(template_path))
                all_successful = False

            manifest_entries.append(
                {
                    "template": str(template_path),
                    "template_stem": template_path.stem,
                    "screenshot": str(screenshot_path),
                }
            )

        manifest = {
            "workspace": workspace_path,
            "generated_at": time.time(),
            "templates": manifest_entries,
        }
        _write_json_file(_manifest_path(workspace), manifest)

        return all_successful
    except Exception as e:
        logger.error("Error capturing screenshots", error=str(e))
        return False


def _run_baseline_comparisons(workspace_path: str) -> bool:
    """Run baseline comparisons."""
    try:
        workspace = Path(workspace_path)
        manifest = _load_json_file(_manifest_path(workspace))
        if not manifest:
            logger.error("Screenshot manifest missing for baseline comparison")
            return False

        baselines_root = workspace / "tests" / "baselines"
        if not baselines_root.exists():
            logger.error("Baselines directory not found", path=str(baselines_root))
            return False

        directories = _reports_dirs(workspace)
        comparator = BeforeAfterComparator(artifacts_dir=directories["diffs"])

        results: List[Dict[str, Any]] = []
        all_passed = True

        for entry in manifest.get("templates", []):
            template_stem = entry.get("template_stem")
            screenshot_path = entry.get("screenshot")
            if not template_stem or not screenshot_path:
                all_passed = False
                results.append(
                    {
                        "template": entry.get("template"),
                        "status": "error",
                        "error": "Manifest entry missing template stem or screenshot",
                    }
                )
                continue

            baseline_path = _find_baseline_image(baselines_root, template_stem)
            if baseline_path is None:
                all_passed = False
                results.append(
                    {
                        "template": entry.get("template"),
                        "status": "error",
                        "error": "Baseline image not found",
                    }
                )
                continue

            try:
                report = comparator.compare_paths(baseline_path, screenshot_path)
                passed = report.result.passed
                if not passed:
                    all_passed = False
                results.append(
                    {
                        "template": entry.get("template"),
                        "baseline": str(baseline_path),
                        "screenshot": screenshot_path,
                        "status": "passed" if passed else "failed",
                        "similarity": report.result.similarity,
                        "pixel_diff_percentage": report.result.pixel_diff_percentage,
                        "diff_path": str(report.diff_path)
                        if report.diff_path
                        else None,
                    }
                )
            except Exception as e:
                logger.error(
                    "Baseline comparison error",
                    template=entry.get("template"),
                    error=str(e),
                )
                all_passed = False
                results.append(
                    {
                        "template": entry.get("template"),
                        "status": "error",
                        "error": str(e),
                    }
                )

        report_payload = {
            "workspace": workspace_path,
            "generated_at": time.time(),
            "results": results,
        }
        _write_json_file(_comparison_report_path(workspace), report_payload)

        return all_passed
    except Exception as e:
        logger.error("Error running baseline comparisons", error=str(e))
        return False


def _generate_test_reports(workspace_path: str) -> bool:
    """Generate test reports."""
    try:
        workspace = Path(workspace_path)
        manifest = _load_json_file(_manifest_path(workspace)) or {}
        comparison_report = _load_json_file(_comparison_report_path(workspace)) or {}

        results = comparison_report.get("results", [])
        failed = sum(1 for result in results if result.get("status") != "passed")
        total = len(results) if results else len(manifest.get("templates", []))

        status = "passed" if total > 0 and failed == 0 else "failed"

        summary = {
            "workspace": workspace_path,
            "generated_at": time.time(),
            "templates_processed": len(manifest.get("templates", [])),
            "comparisons_run": total,
            "comparisons_failed": failed,
            "status": status,
        }
        _write_json_file(_summary_report_path(workspace), summary)

        return True
    except Exception as e:
        logger.error("Error generating test reports", error=str(e))
        return False


def _convert_template_to_pdf(template_path: str, output_dir: str) -> str:
    """Convert template to PDF using LibreOffice."""
    try:
        logger.debug(
            "Converting template to PDF", template=template_path, output=output_dir
        )
        if not capture_convert_template_to_pdf(template_path, output_dir):
            return ""

        pdf_path = Path(output_dir) / f"{Path(template_path).stem}.pdf"
        if not pdf_path.exists():
            logger.error("Expected PDF not found after conversion", path=str(pdf_path))
            return ""

        return str(pdf_path)
    except Exception as e:
        logger.error("Template to PDF conversion error", error=str(e))
        return ""


def _convert_pdf_to_png(pdf_path: str, output_dir: str) -> str:
    """Convert PDF to PNG screenshot."""
    try:
        logger.debug("Converting PDF to PNG", pdf=pdf_path, output=output_dir)
        output_path = Path(output_dir) / f"{Path(pdf_path).stem}.png"
        if not capture_convert_pdf_to_png(pdf_path, str(output_path)):
            return ""
        if not output_path.exists():
            logger.error(
                "Expected PNG not found after conversion", path=str(output_path)
            )
            return ""
        return str(output_path)
    except Exception as e:
        logger.error("PDF to PNG conversion error", error=str(e))
        return ""


def _validate_screenshot(screenshot_path: str) -> bool:
    """Validate screenshot file."""
    try:
        screenshot = Path(screenshot_path)
        if not screenshot.exists():
            logger.error("Screenshot file missing", screenshot=screenshot_path)
            return False
        if screenshot.stat().st_size == 0:
            logger.error("Screenshot file is empty", screenshot=screenshot_path)
            return False

        if Image is not None:
            with Image.open(screenshot) as image:
                if image.size[0] == 0 or image.size[1] == 0:
                    logger.error(
                        "Screenshot has invalid dimensions", screenshot=screenshot_path
                    )
                    return False
        else:
            logger.warning("Pillow not available; skipping image content validation")

        return True
    except Exception as e:
        logger.error("Error validating screenshot", error=str(e))
        return False
