"""FlowRunner — execute parsed flow definitions in a real browser.

Takes a FlowDefinition, resolves variables, drives the ActionExecutor step
by step, runs assertions, and collects a structured FlowResult.
"""

from __future__ import annotations

import logging
import time
from dataclasses import dataclass, field
from typing import Any

from playwright.sync_api import Page

from antiqa.browser.actions import ActionExecutor, ActionResult
from antiqa.browser.health import HealthIssue, HealthMonitor
from antiqa.config.schema import AntiQAConfig

from .assertions import AssertionResult, run_assertion
from .parser import FlowDefinition, FlowStep
from .variables import VariableResolver

log = logging.getLogger("antiqa.flows")


@dataclass
class StepResult:
    step_number: int
    action: str
    description: str
    action_result: ActionResult
    passed: bool


@dataclass
class FlowResult:
    flow_name: str
    passed: bool
    steps: list[StepResult] = field(default_factory=list)
    assertions: list[AssertionResult] = field(default_factory=list)
    health_issues: list[HealthIssue] = field(default_factory=list)
    visual_issues: list[Any] = field(default_factory=list)
    duration_ms: float = 0.0
    error: str | None = None

    @property
    def failed_steps(self) -> list[StepResult]:
        return [s for s in self.steps if not s.passed]

    @property
    def failed_assertions(self) -> list[AssertionResult]:
        return [a for a in self.assertions if not a.passed]

    @property
    def critical_health_issues(self) -> list[HealthIssue]:
        from antiqa.browser.health import IssueSeverity
        return [i for i in self.health_issues if i.severity == IssueSeverity.CRITICAL]

    def summary(self) -> dict[str, Any]:
        return {
            "flow": self.flow_name,
            "passed": self.passed,
            "steps_total": len(self.steps),
            "steps_passed": sum(1 for s in self.steps if s.passed),
            "assertions_total": len(self.assertions),
            "assertions_passed": sum(1 for a in self.assertions if a.passed),
            "health_issues": len(self.health_issues),
            "duration_ms": round(self.duration_ms, 1),
            "error": self.error,
        }


# Maps flow YAML action names to ActionExecutor method names
_ACTION_MAP = {
    "navigate": "navigate",
    "click": "click",
    "fill": "fill",
    "select": "select",
    "hover": "hover",
    "press_key": "press_key",
    "scroll": "scroll",
    "wait_for": "wait_for",
    "screenshot": "screenshot",
    "get_text": "get_text",
    "get_attribute": "get_attribute",
}


class FlowRunner:
    """Execute a FlowDefinition on a Playwright page."""

    def __init__(
        self,
        page: Page,
        config: AntiQAConfig | None = None,
        screenshots_dir: str = "logs/screenshots",
    ) -> None:
        self.page = page
        self.config = config
        self.screenshots_dir = screenshots_dir
        self.actions = ActionExecutor(page, screenshots_dir=screenshots_dir, config=config)

    def run(
        self,
        flow: FlowDefinition,
        params: dict[str, str] | None = None,
        stop_on_failure: bool = True,
    ) -> FlowResult:
        """Execute all steps in *flow* and return a FlowResult."""
        merged_params = {**flow.params, **(params or {})}
        resolver = VariableResolver(config=self.config, params=merged_params)

        result = FlowResult(flow_name=flow.name, passed=True)
        start = time.perf_counter()

        # Start health monitoring
        monitor = HealthMonitor(self.page, screenshots_dir=self.screenshots_dir)
        monitor.start()

        log.info("Running flow: %s (%d steps, %d assertions)", flow.name, len(flow.steps), len(flow.assertions))

        for i, step in enumerate(flow.steps, 1):
            step_result = self._run_step(i, step, resolver)
            result.steps.append(step_result)

            if not step_result.passed:
                result.passed = False
                log.warning(
                    "Step %d failed: %s %s",
                    i, step.action, step_result.action_result.error or "",
                )
                if stop_on_failure:
                    result.error = f"Step {i} ({step.action}) failed"
                    break

        # Take a perf snapshot after all steps
        monitor.take_snapshot()

        if result.passed or not stop_on_failure:
            for assertion_def in flow.assertions:
                resolved_def = resolver.resolve_value(assertion_def.model_dump(exclude_none=True))
                ar = run_assertion(self.page, resolved_def)
                result.assertions.append(ar)
                if not ar.passed:
                    result.passed = False
                    log.warning("Assertion failed: %s — %s", ar.type, ar.message)

        # Collect health issues
        monitor.stop()
        result.health_issues = list(monitor.issues)

        if monitor.has_critical:
            log.warning(
                "Flow %s: %d critical health issue(s) detected",
                flow.name, len(monitor.critical_issues),
            )

        result.duration_ms = (time.perf_counter() - start) * 1000
        status = "PASSED" if result.passed else "FAILED"
        log.info("Flow %s %s (%.0fms, %d health issues)", flow.name, status, result.duration_ms, len(result.health_issues))
        return result

    def _run_step(self, step_num: int, step: FlowStep, resolver: VariableResolver) -> StepResult:
        """Execute a single FlowStep."""
        method_name = _ACTION_MAP.get(step.action)
        if not method_name:
            ar = ActionResult(action=step.action, success=False, duration_ms=0, error=f"Unknown action: '{step.action}'")
            return StepResult(step_number=step_num, action=step.action, description=step.description, action_result=ar, passed=False)

        method = getattr(self.actions, method_name)
        kwargs = self._build_kwargs(step, resolver)

        desc = step.description or f"{step.action} {step.selector or step.url or ''}"
        log.debug("Step %d: %s", step_num, desc)

        ar = method(**kwargs)
        return StepResult(
            step_number=step_num,
            action=step.action,
            description=desc,
            action_result=ar,
            passed=ar.success,
        )

    def _build_kwargs(self, step: FlowStep, resolver: VariableResolver) -> dict[str, Any]:
        """Build keyword arguments for the ActionExecutor method from a FlowStep."""
        kwargs: dict[str, Any] = {}

        if step.action == "navigate":
            kwargs["url"] = resolver.resolve_string(step.url or "")
            if step.wait_until:
                kwargs["wait_until"] = step.wait_until

        elif step.action in ("click", "hover", "get_text"):
            kwargs["selector"] = resolver.resolve_string(step.selector or "")

        elif step.action in ("fill", "select"):
            kwargs["selector"] = resolver.resolve_string(step.selector or "")
            kwargs["value"] = resolver.resolve_string(step.value or "")

        elif step.action == "press_key":
            kwargs["key"] = resolver.resolve_string(step.key or step.value or "")
            if step.selector:
                kwargs["selector"] = resolver.resolve_string(step.selector)

        elif step.action == "scroll":
            kwargs["direction"] = step.direction or "down"
            if step.amount:
                kwargs["amount"] = step.amount

        elif step.action == "wait_for":
            if step.selector:
                kwargs["selector"] = resolver.resolve_string(step.selector)
            elif step.url:
                kwargs["url"] = resolver.resolve_string(step.url)
            if step.timeout_ms:
                kwargs["timeout_ms"] = step.timeout_ms

        elif step.action == "screenshot":
            kwargs["name"] = resolver.resolve_string(step.name or "")
            kwargs["full_page"] = step.full_page

        elif step.action == "get_attribute":
            kwargs["selector"] = resolver.resolve_string(step.selector or "")
            kwargs["attribute"] = resolver.resolve_string(step.attribute or "")

        return kwargs
