"""
Domain lifecycle + PyStator integration example.

Demonstrates:
  1. Load a domain contract (in-memory or from store) with lifecycle binding.
  2. Read state_machine_name and state_field via get_domain_entity_info().
  3. Validate a payload with PyCharter's Validator.
  4. Call PyStator API to process an event (optional; requires pystator api running).

Covers both intraday (order_domain / order_lifecycle) and weekly (optimization_cycle_domain / optimization_cycle) use cases.
Run from repo root: python examples/domain_lifecycle_pystator_example.py
"""

from __future__ import annotations

import os
from typing import Any

# PyCharter only (no pystator import at top level)
from pycharter import (
    Validator,
    get_domain_entity_info,
    get_lifecycle_binding,
)


def make_order_domain_contract() -> dict[str, Any]:
    """Build an order_domain contract dict (schema + metadata with lifecycle)."""
    return {
        "schema": {
            "type": "object",
            "version": "1.0.0",
            "properties": {
                "order_id": {"type": "string", "minLength": 1},
                "symbol": {"type": "string", "minLength": 1},
                "side": {"type": "string", "enum": ["buy", "sell"]},
                "quantity": {"type": "number", "minimum": 0},
                "status": {
                    "type": "string",
                    "enum": ["PENDING_NEW", "OPEN", "PARTIALLY_FILLED", "FILLED", "CANCELED", "REJECTED", "TIMED_OUT", "EXPIRED"],
                },
                "filled_qty": {"type": "number", "minimum": 0},
            },
            "required": ["order_id", "symbol", "side", "quantity", "status"],
        },
        "metadata": {
            "lifecycle": {
                "state_machine_name": "order_lifecycle",
                "machine_version": "1.0.0",
                "state_field": "status",
                "entity_id_field": "order_id",
            }
        },
    }


def make_optimization_cycle_contract() -> dict[str, Any]:
    """Build an optimization_cycle_domain contract dict (aligns with PyStator optimization_cycle FSM)."""
    return {
        "schema": {
            "type": "object",
            "version": "1.0.0",
            "properties": {
                "run_id": {"type": "string", "minLength": 1},
                "portfolio_id": {"type": "string"},
                "status": {
                    "type": "string",
                    "enum": [
                        "DATA_COLLECTION",
                        "OPTIMIZING",
                        "VALIDATION",
                        "APPROVAL_PENDING",
                        "EXECUTING",
                        "SETTLING",
                        "COMPLETED",
                        "FAILED",
                        "CANCELLED",
                    ],
                },
                "started_at": {"type": "string", "format": "date-time"},
                "completed_at": {"type": "string", "format": "date-time"},
            },
            "required": ["run_id", "status"],
        },
        "metadata": {
            "lifecycle": {
                "state_machine_name": "optimization_cycle",
                "machine_version": "1.0.0",
                "state_field": "status",
                "entity_id_field": "run_id",
            }
        },
    }


def call_pystator_entity_event(
    base_url: str,
    entity_id: str,
    machine_name: str,
    trigger: str,
    context: dict[str, Any],
) -> dict[str, Any] | None:
    """POST to PyStator API: process event for entity. Returns response or None on error."""
    try:
        import urllib.request
        import json

        url = f"{base_url.rstrip('/')}/v1/entities/{entity_id}/events"
        data = json.dumps({
            "trigger": trigger,
            "machine_name": machine_name,
            "context": context,
        }).encode("utf-8")
        req = urllib.request.Request(
            url,
            data=data,
            method="POST",
            headers={"Content-Type": "application/json"},
        )
        with urllib.request.urlopen(req, timeout=10) as resp:
            return json.loads(resp.read().decode())
    except Exception as e:
        print(f"  PyStator API call failed: {e}")
        return None


def main() -> None:
    print("=== Domain lifecycle + PyStator example ===\n")

    # ---- Intraday: order domain ----
    print("1. Order domain (intraday)")
    order_contract = make_order_domain_contract()
    info = get_domain_entity_info(order_contract)
    if not info:
        print("   No lifecycle binding found.")
    else:
        print(f"   state_machine_name: {info['state_machine_name']}")
        print(f"   state_field: {info['state_field']}")

    # Validate order payload (Validator accepts contract_dict with "schema" key)
    validator = Validator(contract_dict=order_contract)
    order_payload = {
        "order_id": "ord-001",
        "symbol": "AAPL",
        "side": "buy",
        "quantity": 100,
        "status": "PENDING_NEW",
    }
    result = validator.validate(order_payload)
    print(f"   Validation: {'OK' if result.is_valid else 'FAILED'}")
    if not result.is_valid:
        print(f"   Errors: {result.errors}")

    # ---- Weekly: optimization cycle domain ----
    print("\n2. Optimization cycle domain")
    opt_contract = make_optimization_cycle_contract()
    info_opt = get_domain_entity_info(opt_contract)
    if info_opt:
        print(f"   state_machine_name: {info_opt['state_machine_name']}")
    opt_payload = {
        "run_id": "run-2025-01",
        "portfolio_id": "pf-1",
        "status": "DATA_COLLECTION",
    }
    validator_opt = Validator(contract_dict=opt_contract)
    result_opt = validator_opt.validate(opt_payload)
    print(f"   Validation: {'OK' if result_opt.is_valid else 'FAILED'}")

    # ---- Lifecycle binding from metadata only ----
    print("\n3. get_lifecycle_binding(metadata) only")
    metadata = order_contract.get("metadata", {})
    binding = get_lifecycle_binding(metadata)
    print(f"   Binding: {binding}")

    # ---- Optional: call PyStator API ----
    pystator_url = os.environ.get("PYSTATOR_API_URL", "http://localhost:8004")
    print(f"\n4. PyStator API (optional, base_url={pystator_url})")
    if info:
        entity_id = order_payload["order_id"]
        resp = call_pystator_entity_event(
            base_url=pystator_url,
            entity_id=entity_id,
            machine_name=info["state_machine_name"],
            trigger="submit_order",
            context=order_payload,
        )
        if resp:
            print(f"   Response: success={resp.get('success')}, target_state={resp.get('target_state')}")
        else:
            print("   To run with PyStator: start 'pystator api' and set PYSTATOR_API_URL if needed.")
    print("\nDone.")


if __name__ == "__main__":
    main()
