"""Stage 01: bootstrap policy + channels + alert rules for live OpenAI governance demo."""

from __future__ import annotations

from typing import Any

from sentinos import SentinosClient

if __package__ in (None, ""):
    import os
    import sys

    sys.path.append(os.path.dirname(__file__))

from common import LiveE2EConfig, info, load_config, load_state, save_state, write_artifact, build_sentinos_client  # noqa: E402


def _rego_for_openai_policy(config: LiveE2EConfig) -> str:
    return f"""
package {config.policy_package}

default decision = {{"decision":"ALLOW","reason":"default allow for governed OpenAI traffic"}}

is_target if {{
  input.action == "{config.target_tool}"
}}

prompt_text := lower(concat(" ", [msg.content |
  msg := input.input.request.messages[_]
  is_string(msg.content)
]))

decision = {{"decision":"DENY","reason":"blocked data exfiltration request"}} if {{
  is_target
  contains(prompt_text, "exfiltrate")
}}

decision = {{"decision":"ESCALATE","reason":"human approval required for wire transfer request"}} if {{
  is_target
  contains(prompt_text, "wire transfer")
}}

decision = {{"decision":"SHADOW","reason":"shadow monitor refund workflows"}} if {{
  is_target
  contains(prompt_text, "refund")
  not contains(prompt_text, "wire transfer")
  not contains(prompt_text, "exfiltrate")
}}
""".strip()


def _create_channels(client: SentinosClient, config: LiveE2EConfig) -> list[str]:
    ids: list[str] = []
    seed = config.run_id.replace("-", "")

    channel_specs: list[dict[str, Any]] = [
        {
            "kind": "SLACK",
            "name": f"live-e2e-slack-{seed}",
            "config": {"webhook_url": "https://example.com/hooks/sentinos/live-e2e/slack"},
        },
        {
            "kind": "WEBHOOK",
            "name": f"live-e2e-webhook-{seed}",
            "config": {"url": "https://example.com/hooks/sentinos/live-e2e/webhook", "method": "POST"},
        },
        {
            "kind": "EMAIL",
            "name": f"live-e2e-email-{seed}",
            "config": {
                "provider": "smtp",
                "from": "alerts@sentinos.dev",
                "to": ["soc@sentinos.dev"],
                "host": "smtp.example.com",
                "port": 587,
            },
        },
    ]

    for spec in channel_specs:
        created = client.kernel.create_notification_channel(
            kind=spec["kind"],
            name=spec["name"],
            config=spec["config"],
            enabled=True,
        )
        channel_id = str(created.get("channel_id") or "").strip()
        if channel_id:
            ids.append(channel_id)
            info(f"Created channel {spec['kind']} -> {channel_id}")
    return ids


def _create_rules(client: SentinosClient, config: LiveE2EConfig, channel_ids: list[str]) -> dict[str, str]:
    seed = config.run_id.replace("-", "")
    rules: dict[str, str] = {}

    payloads: list[tuple[str, dict[str, Any]]] = [
        (
            "deny_threshold",
            {
                "name": f"Live E2E Deny Threshold {seed}",
                "description": "Triggers on deny decisions generated by live E2E OpenAI traffic",
                "rule_type": "THRESHOLD",
                "severity": "HIGH",
                "enabled": True,
                "metric_key": "decision.deny_count",
                "comparator": ">=",
                "threshold_value": 1,
                "evaluation_window_sec": 300,
                "cooldown_sec": 60,
                "notification_channels": channel_ids,
                "metadata": {"run_id": config.run_id, "suite": "live_e2e"},
            },
        ),
        (
            "escalate_threshold",
            {
                "name": f"Live E2E Escalate Threshold {seed}",
                "description": "Triggers on escalate decisions generated by live E2E OpenAI traffic",
                "rule_type": "THRESHOLD",
                "severity": "CRITICAL",
                "enabled": True,
                "metric_key": "decision.escalate_count",
                "comparator": ">=",
                "threshold_value": 1,
                "evaluation_window_sec": 300,
                "cooldown_sec": 60,
                "notification_channels": channel_ids,
                "metadata": {"run_id": config.run_id, "suite": "live_e2e"},
            },
        ),
        (
            "pattern_exfiltrate",
            {
                "name": f"Live E2E Exfiltration Pattern {seed}",
                "description": "Pattern rule for exfiltration-style prompts",
                "rule_type": "PATTERN",
                "severity": "CRITICAL",
                "enabled": True,
                "pattern": "exfiltrate",
                "threshold_value": 1,
                "evaluation_window_sec": 300,
                "cooldown_sec": 60,
                "notification_channels": channel_ids,
                "metadata": {"run_id": config.run_id, "suite": "live_e2e"},
            },
        ),
        (
            "compliance_deny",
            {
                "name": f"Live E2E Compliance Deny {seed}",
                "description": "Compliance signal for deny outcomes",
                "rule_type": "COMPLIANCE",
                "severity": "HIGH",
                "enabled": True,
                "compliance_control": "deny",
                "threshold_value": 1,
                "evaluation_window_sec": 300,
                "cooldown_sec": 60,
                "notification_channels": channel_ids,
                "metadata": {"run_id": config.run_id, "suite": "live_e2e"},
            },
        ),
    ]

    for key, payload in payloads:
        created = client.alerts.create_rule(rule=payload)
        rules[key] = created.rule_id
        info(f"Created alert rule {key} -> {created.rule_id}")

    return rules


def _is_live_e2e_rule(rule: Any) -> bool:
    metadata = getattr(rule, "metadata", None) or {}
    if isinstance(metadata, dict) and str(metadata.get("suite") or "").strip().lower() == "live_e2e":
        return True
    name = str(getattr(rule, "name", "")).strip().lower()
    return name.startswith("live e2e ")


def _cleanup_previous_live_e2e_resources(client: SentinosClient, config: LiveE2EConfig) -> None:
    info("Cleaning up previous live-e2e alert rules and channels")

    deleted_rules = 0
    for rule in client.alerts.list_rules(limit=500):
        if not _is_live_e2e_rule(rule):
            continue
        rule_id = str(getattr(rule, "rule_id", "")).strip()
        if not rule_id:
            continue
        try:
            client.alerts.delete_rule(rule_id)
            deleted_rules += 1
        except Exception as exc:  # noqa: BLE001
            info(f"WARN: failed to delete prior live-e2e rule {rule_id}: {exc}")

    deleted_channels = 0
    for channel in client.kernel.list_notification_channels(limit=500):
        name = str((channel or {}).get("name") or "").strip().lower()
        if not name.startswith("live-e2e-"):
            continue
        channel_id = str((channel or {}).get("channel_id") or "").strip()
        if not channel_id:
            continue
        try:
            client.kernel.delete_notification_channel(channel_id)
            deleted_channels += 1
        except Exception as exc:  # noqa: BLE001
            info(f"WARN: failed to delete prior live-e2e channel {channel_id}: {exc}")

    info(f"Cleanup complete: deleted_rules={deleted_rules} deleted_channels={deleted_channels}")


def run_stage(config: LiveE2EConfig) -> dict[str, Any]:
    client = build_sentinos_client(config)
    tenant_id = (config.tenant_id or client.config.tenant_id or "").strip()
    if not tenant_id:
        raise RuntimeError("tenant_id is required for stage_01_setup")
    state = load_state(config)
    rego = _rego_for_openai_policy(config)
    _cleanup_previous_live_e2e_resources(client, config)

    info(f"Upserting policy {config.policy_id}@{config.policy_version}")
    client.arbiter.create_policy(
        policy_id=config.policy_id,
        rego=rego,
        version=config.policy_version,
        target_tools=[config.target_tool],
        tenants=[tenant_id],
        status="draft",
    )
    client.arbiter.promote_policy(
        policy_id=config.policy_id,
        version=config.policy_version,
        target="prod",
    )

    simulation = client.arbiter.simulate_policy(
        candidate_rego=rego,
        trace_limit=200,
        policy_id=config.policy_id,
        version=config.policy_version,
    )
    channel_ids = _create_channels(client, config)
    rule_ids = _create_rules(client, config, channel_ids)

    state.update(
        {
            "run_id": config.run_id,
            "tenant_id": tenant_id,
            "policy_id": config.policy_id,
            "policy_version": config.policy_version,
            "target_tool": config.target_tool,
            "channel_ids": channel_ids,
            "rule_ids": rule_ids,
        }
    )
    save_state(config, state)
    artifact = {
        "stage": "stage_01_setup",
        "run_id": config.run_id,
        "tenant_id": tenant_id,
        "policy_id": config.policy_id,
        "policy_version": config.policy_version,
        "target_tool": config.target_tool,
        "channel_ids": channel_ids,
        "rule_ids": rule_ids,
        "simulation": simulation.to_dict(),
    }
    artifact_path = write_artifact(config, "stage_01_setup", artifact)
    info(f"Stage 01 complete -> {artifact_path}")
    return artifact


def main() -> None:
    config = load_config(require_openai_key=False)
    run_stage(config)


if __name__ == "__main__":
    main()
