from __future__ import annotations

from typing import Any, Callable, Dict, Optional

from oasyce.account_state import resolve_canonical_account_address
from oasyce.config import Config
from oasyce.services.coordination_context import resolve_coordination_context
from oasyce.services.inbox import ConfirmationInbox, InboxError, InboxItem
from oasyce.services.portfolio_ledger import get_portfolio_ledger_store


def _default_execution_actor() -> Dict[str, Any]:
    return resolve_coordination_context(
        default_agent_id="autonomy.executor",
        source="operations.execute",
    )


def _default_facade(data_dir: Optional[str], owner: str, item: InboxItem):
    from oasyce.services.facade import OasyceServiceFacade

    config = Config.from_env(
        owner=owner,
        tags=",".join(item.suggested_tags or []),
        data_dir=data_dir,
    )
    return OasyceServiceFacade(config=config)


def execute_inbox_item(
    item_id: str,
    *,
    inbox: Optional[ConfirmationInbox] = None,
    data_dir: Optional[str] = None,
    actor: Optional[Dict[str, Any]] = None,
    facade: Any = None,
    account_resolver: Callable[..., str] = resolve_canonical_account_address,
) -> InboxItem:
    """Execute an approved inbox item and persist the outcome."""

    inbox = inbox or ConfirmationInbox(data_dir=data_dir)
    item = inbox.get(item_id)
    if item is None:
        raise InboxError(f"Item '{item_id}' not found")
    if item.status != "approved":
        raise InboxError(f"Item '{item_id}' is not approved")
    if item.execution_status == "succeeded":
        return item

    execution_actor = actor or _default_execution_actor()
    ledger = get_portfolio_ledger_store(data_dir)
    account = str(account_resolver(fallback="") or "").strip()
    if not account:
        ledger.record(
            action=item.item_type,
            status="failed",
            item_id=item.item_id,
            account_id="",
            asset_id=item.asset_id,
            file_path=item.file_path,
            actor=execution_actor,
            error="No canonical account is configured on this device.",
        )
        return inbox.record_execution(
            item_id,
            success=False,
            actor=execution_actor,
            error="No canonical account is configured on this device.",
        )

    facade = facade or _default_facade(data_dir, account, item)
    if item.item_type == "register":
        result = facade.register(
            file_path=str(item.file_path or ""),
            owner=account,
            tags=list(item.suggested_tags or []),
            price_model="auto",
            extra_metadata={
                "inbox_item_id": item.item_id,
                **dict(item.coordination or {}),
                "executed_by": execution_actor,
            },
        )
    elif item.item_type == "purchase":
        result = facade.buy(
            asset_id=str(item.asset_id or ""),
            buyer=account,
            amount_oas=float(item.price or 0.0),
        )
    elif item.item_type == "deprecate":
        result = facade.deprecate_asset(
            asset_id=str(item.asset_id or ""),
            owner=account,
            reason=str(item.reason or "superseded"),
            extra_metadata={
                "inbox_item_id": item.item_id,
                **dict(item.coordination or {}),
                "executed_by": execution_actor,
            },
        )
    elif item.item_type == "archive":
        result = facade.archive_asset(
            asset_id=str(item.asset_id or ""),
            owner=account,
            reason=str(item.reason or "archived"),
            extra_metadata={
                "inbox_item_id": item.item_id,
                **dict(item.coordination or {}),
                "executed_by": execution_actor,
            },
        )
    else:
        raise InboxError(f"Unsupported inbox item type: {item.item_type}")

    if not result.success:
        ledger.record(
            action=item.item_type,
            status="failed",
            item_id=item.item_id,
            account_id=account,
            asset_id=item.asset_id,
            file_path=item.file_path,
            actor=execution_actor,
            price_model=(
                (result.data or {}).get("price_model") if isinstance(result.data, dict) else None
            ),
            asset_class=(
                (result.data or {}).get("asset_class") if isinstance(result.data, dict) else None
            ),
            portfolio_policy_reason=(
                (result.data or {}).get("portfolio_policy_reason")
                if isinstance(result.data, dict)
                else None
            ),
            error=result.error,
        )
        return inbox.record_execution(
            item_id,
            success=False,
            actor=execution_actor,
            error=result.error,
        )
    result_data = dict(result.data or {})
    debit_oas = 0.0
    credit_oas = 0.0
    if item.item_type == "purchase":
        debit_oas = float(result_data.get("amount_oas") or item.price or 0.0)
    elif item.item_type == "sell":
        credit_oas = float(result_data.get("amount_oas") or result_data.get("credit_oas") or 0.0)
    ledger.record(
        action=item.item_type,
        status="succeeded",
        item_id=item.item_id,
        account_id=account,
        asset_id=str(result_data.get("asset_id") or item.asset_id or "").strip() or None,
        file_path=item.file_path,
        debit_oas=debit_oas,
        credit_oas=credit_oas,
        actor=execution_actor,
        price_model=str(result_data.get("price_model") or "").strip() or None,
        asset_class=str(result_data.get("asset_class") or "").strip() or None,
        portfolio_policy_reason=str(result_data.get("portfolio_policy_reason") or "").strip()
        or None,
    )
    return inbox.record_execution(
        item_id,
        success=True,
        actor=execution_actor,
        result=result_data,
    )
