```python
#!/usr/bin/env python3
"""HDX policy baseline validator for secure virtual app sessions.

This script is a vendor-neutral helper for planning and validating a secure
virtual app session policy baseline. It does not connect to any platform by
itself and performs no destructive actions.

Use it to:
- Define target security controls for a pilot scope
- Compare current vs. target policy values
- Produce a validation checklist for test sessions
- Export a human-readable report for change management

Examples:
    python hdx_policy_baseline_validator.py --policy-file baseline.json
    python hdx_policy_baseline_validator.py --interactive
    python hdx_policy_baseline_validator.py --policy-file baseline.json --export report.md
"""

from __future__ import annotations

import argparse
import json
import sys
from dataclasses import dataclass, asdict
from pathlib import Path
from typing import Any, Dict, List, Optional


ALLOWED_CONTROLS = {
    "clipboard",
    "client_drive_mapping",
    "printer_redirection",
    "usb_redirection",
    "com_port_redirection",
    "audio_redirection",
    "browser_content_redirection",
    "session_timeout_minutes",
    "disconnect_timeout_minutes",
    "reconnect_allowed",
    "graphics_bandwidth_limit",
}


@dataclass
class PolicyControl:
    control: str
    current_state: str
    target_state: str
    exception_scope: str = "none"
    validation_method: str = "manual session test"
    owner: str = ""
    business_reason: str = ""


@dataclass
class PolicyBaseline:
    title: str
    scope: str
    pilot_group: str
    rollback_plan: str
    controls: List[PolicyControl]


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Validate and document a secure HDX policy baseline."
    )
    parser.add_argument(
        "--policy-file",
        type=Path,
        help="Path to a JSON file containing the policy baseline.",
    )
    parser.add_argument(
        "--interactive",
        action="store_true",
        help="Build a policy baseline interactively from prompts.",
    )
    parser.add_argument(
        "--export",
        type=Path,
        help="Optional path to write a Markdown report.",
    )
    return parser.parse_args()


def require_non_empty(prompt: str) -> str:
    value = input(prompt).strip()
    while not value:
        print("Value cannot be empty.")
        value = input(prompt).strip()
    return value


def validate_control_name(name: str) -> None:
    if name not in ALLOWED_CONTROLS:
        raise ValueError(
            f"Unsupported control '{name}'. Allowed controls: {', '.join(sorted(ALLOWED_CONTROLS))}"
        )


def load_policy_file(path: Path) -> PolicyBaseline:
    if not path.exists():
        raise FileNotFoundError(f"Policy file not found: {path}")

    data = json.loads(path.read_text(encoding="utf-8"))
    controls = []
    for item in data.get("controls", []):
        validate_control_name(item["control"])
        controls.append(
            PolicyControl(
                control=item["control"],
                current_state=item.get("current_state", ""),
                target_state=item.get("target_state", ""),
                exception_scope=item.get("exception_scope", "none"),
                validation_method=item.get("validation_method", "manual session test"),
                owner=item.get("owner", ""),
                business_reason=item.get("business_reason", ""),
            )
        )

    return PolicyBaseline(
        title=data.get("title", "HDX Policy Baseline"),
        scope=data.get("scope", ""),
        pilot_group=data.get("pilot_group", ""),
        rollback_plan=data.get("rollback_plan", ""),
        controls=controls,
    )


def build_interactive() -> PolicyBaseline:
    print("Create a secure HDX policy baseline")
    title = require_non_empty("Baseline title: ")
    scope = require_non_empty("Policy scope (e.g., pilot delivery group): ")
    pilot_group = require_non_empty("Pilot group name: ")
    rollback_plan = require_non_empty("Rollback plan summary: ")

    controls: List[PolicyControl] = []
    print("\nEnter controls one by one. Type 'done' when finished.")
    print(f"Allowed controls: {', '.join(sorted(ALLOWED_CONTROLS))}")

    while True:
        control = input("Control name: ").strip()
        if control.lower() == "done":
            break
        validate_control_name(control)
        current_state = require_non_empty("Current state: ")
        target_state = require_non_empty("Target state: ")
        exception_scope = input("Exception scope [none]: ").strip() or "none"
        validation_method = input("Validation method [manual session test]: ").strip() or "manual session test"
        owner = input("Owner: ").strip()
        business_reason = input("Business reason: ").strip()
        controls.append(
            PolicyControl(
                control=control,
                current_state=current_state,
                target_state=target_state,
                exception_scope=exception_scope,
                validation_method=validation_method,
                owner=owner,
                business_reason=business_reason,
            )
        )
        print("Control added.\n")

    if not controls:
        raise ValueError("At least one control is required.")

    return PolicyBaseline(
        title=title,
        scope=scope,
        pilot_group=pilot_group,
        rollback_plan=rollback_plan,
        controls=controls,
    )


def summarize(baseline: PolicyBaseline) -> str:
    lines = [
        f"# {baseline.title}",
        "",
        f"- **Scope:** {baseline.scope}",
        f"- **Pilot group:** {baseline.pilot_group}",
        f"- **Rollback plan:** {baseline.rollback_plan}",
        "",
        "## Controls",
        "",
        "| Control | Current state | Target state | Exception scope | Validation method | Owner | Business reason |",
        "| --- | --- | --- | --- | --- | --- | --- |",
    ]

    for c in baseline.controls:
        lines.append(
            f"| {c.control} | {c.current_state} | {c.target_state} | {c.exception_scope} | {c.validation_method} | {c.owner} | {c.business_reason} |"
        )

    lines.extend(
        [
            "",
            "## Validation checklist",
            "",
            "- Confirm the policy applies only to the pilot scope.",
            "- Verify effective precedence or assignment order.",
            "- Test clipboard behavior in both directions if restricted.",
            "- Confirm drive and file redirection behavior matches the target state.",
            "- Confirm printer, USB, COM port, and audio redirection settings behave as intended.",
            "- Validate timeout and reconnect behavior with a real session.",
            "- Record observed results, exceptions, and workaround details.",
            "",
            "## Production readiness check",
            "",
            "- Business owner approved each exception.",
            "- A rollback path is documented and tested.",
            "- Pilot validation matches expected behavior.",
            "- No unintended access paths remain in the session.",
        ]
    )
    return "\n".join(lines)


def validate_baseline(baseline: PolicyBaseline) -> List[str]:
    issues: List[str] = []
    if not baseline.scope:
        issues.append("Scope is missing.")
    if not baseline.pilot_group:
        issues.append("Pilot group is missing.")
    if not baseline.rollback_plan:
        issues.append("Rollback plan is missing.")
    if not baseline.controls:
        issues.append("At least one control must be defined.")

    for idx, control in enumerate(baseline.controls, start=1):
        if not control.control:
            issues.append(f"Control #{idx} has no name.")
        if control.control not in ALLOWED_CONTROLS:
            issues.append(f"Control #{idx} uses unsupported control '{control.control}'.")
        if not control.current_state:
            issues.append(f"Control '{control.control}' is missing current_state.")
        if not control.target_state:
            issues.append(f"Control '{control.control}' is missing target_state.")
        if not control.business_reason:
            issues.append(f"Control '{control.control}' is missing business_reason.")

    return issues


def to_dict(baseline: PolicyBaseline) -> Dict[str, Any]:
    return {
        "title": baseline.title,
        "scope": baseline.scope,
        "pilot_group": baseline.pilot_group,
        "rollback_plan": baseline.rollback_plan,
        "controls": [asdict(c) for c in baseline.controls],
    }


def main() -> int:
    args = parse_args()

    if args.interactive == args.policy_file is None:
        print("Specify either --policy-file or --interactive.", file=sys.stderr)
        return 2

    try:
        if args.interactive:
            baseline = build_interactive()
        else:
            baseline = load_policy_file(args.policy_file)

        issues = validate_baseline(baseline)
        report = summarize(baseline)

        print(report)
        print("\n## Validation status")
        if issues:
            print("Issues found:")
            for issue in issues:
                print(f"- {issue}")
            exit_code = 1
        else:
            print("Baseline structure looks complete.")
            exit_code = 0

        if args.export:
            args.export.write_text(report + "\n", encoding="utf-8")
            print(f"\nReport written to: {args.export}")

        return exit_code

    except (FileNotFoundError, ValueError, json.JSONDecodeError) as exc:
        print(f"Error: {exc}", file=sys.stderr)
        return 1


if __name__ == "__main__":
    raise SystemExit(main())
```