from __future__ import annotations

from oasyce.services.facade import ServiceResult
from oasyce.services.inbox import ConfirmationInbox
from oasyce.services.operations_execution import execute_inbox_item
from oasyce.services.portfolio_ledger import get_portfolio_ledger_store


class _FakeFacade:
    def __init__(self):
        self.calls = []

    def register(self, **kwargs):
        self.calls.append(("register", kwargs))
        return ServiceResult(success=True, data={"asset_id": "asset_123"})

    def buy(self, **kwargs):
        self.calls.append(("buy", kwargs))
        return ServiceResult(success=True, data={"tx_id": "tx_123"})

    def deprecate_asset(self, **kwargs):
        self.calls.append(("deprecate", kwargs))
        return ServiceResult(
            success=True,
            data={"asset_id": kwargs["asset_id"], "asset_state": "deprecated"},
        )

    def archive_asset(self, **kwargs):
        self.calls.append(("archive", kwargs))
        return ServiceResult(
            success=True,
            data={"asset_id": kwargs["asset_id"], "asset_state": "archived"},
        )


class _FailingFacade(_FakeFacade):
    def register(self, **kwargs):
        self.calls.append(("register", kwargs))
        return ServiceResult(success=False, error="register failed")

    def deprecate_asset(self, **kwargs):
        self.calls.append(("deprecate", kwargs))
        return ServiceResult(success=False, error="deprecate failed")


def test_execute_register_item_records_success(tmp_path):
    target = tmp_path / "repo" / "notes.md"
    target.parent.mkdir(parents=True)
    target.write_text("hello\n", encoding="utf-8")

    inbox = ConfirmationInbox(data_dir=str(tmp_path))
    item = inbox.add_pending_register(str(target), "Notes", ["docs"])
    inbox.approve(item.item_id, actor={"device_id": "device-a", "agent_id": "dashboard.operator"})

    facade = _FakeFacade()
    executed = execute_inbox_item(
        item.item_id,
        inbox=inbox,
        data_dir=str(tmp_path),
        actor={"device_id": "device-a", "agent_id": "dashboard.operator"},
        facade=facade,
        account_resolver=lambda fallback="": "oasyce1owner",
    )

    assert executed.execution_status == "succeeded"
    assert executed.execution_error is None
    assert executed.execution_result["asset_id"] == "asset_123"
    assert executed.coordination["executed_by"]["agent_id"] == "dashboard.operator"
    call_name, payload = facade.calls[0]
    assert call_name == "register"
    assert payload["owner"] == "oasyce1owner"
    assert payload["file_path"] == str(target)
    assert payload["extra_metadata"]["resolved_by"]["agent_id"] == "dashboard.operator"
    ledger = get_portfolio_ledger_store(str(tmp_path)).summary()
    assert ledger["totals"]["assets_created"] == 1
    assert ledger["totals"]["spent_oas"] == 0.0
    assert ledger["recent"][0]["action"] == "register"


def test_execute_purchase_item_uses_recommended_price(tmp_path):
    inbox = ConfirmationInbox(data_dir=str(tmp_path))
    item = inbox.add_pending_purchase("asset_001", 7.5, "cheap enough")
    inbox.approve(item.item_id, actor={"device_id": "device-a", "agent_id": "dashboard.operator"})

    facade = _FakeFacade()
    executed = execute_inbox_item(
        item.item_id,
        inbox=inbox,
        data_dir=str(tmp_path),
        actor={"device_id": "device-a", "agent_id": "dashboard.operator"},
        facade=facade,
        account_resolver=lambda fallback="": "oasyce1buyer",
    )

    assert executed.execution_status == "succeeded"
    call_name, payload = facade.calls[0]
    assert call_name == "buy"
    assert payload["asset_id"] == "asset_001"
    assert payload["buyer"] == "oasyce1buyer"
    assert payload["amount_oas"] == 7.5
    ledger = get_portfolio_ledger_store(str(tmp_path)).summary()
    assert ledger["totals"]["purchases"] == 1
    assert ledger["totals"]["spent_oas"] == 7.5
    assert ledger["recent"][0]["action"] == "purchase"


def test_execute_register_item_records_failure(tmp_path):
    target = tmp_path / "repo" / "notes.md"
    target.parent.mkdir(parents=True)
    target.write_text("hello\n", encoding="utf-8")

    inbox = ConfirmationInbox(data_dir=str(tmp_path))
    item = inbox.add_pending_register(str(target), "Notes", ["docs"])
    inbox.approve(item.item_id, actor={"device_id": "device-a", "agent_id": "dashboard.operator"})

    executed = execute_inbox_item(
        item.item_id,
        inbox=inbox,
        data_dir=str(tmp_path),
        actor={"device_id": "device-a", "agent_id": "dashboard.operator"},
        facade=_FailingFacade(),
        account_resolver=lambda fallback="": "oasyce1owner",
    )

    assert executed.execution_status == "failed"
    assert executed.execution_error == "register failed"
    assert executed.coordination["executed_by"]["agent_id"] == "dashboard.operator"
    ledger = get_portfolio_ledger_store(str(tmp_path)).summary()
    assert ledger["totals"]["failed"] == 1
    assert ledger["recent"][0]["status"] == "failed"


def test_execute_deprecate_item_records_success(tmp_path):
    inbox = ConfirmationInbox(data_dir=str(tmp_path))
    item = inbox.add_pending_deprecate("asset_001", "superseded")
    inbox.approve(item.item_id, actor={"device_id": "device-a", "agent_id": "dashboard.operator"})

    facade = _FakeFacade()
    executed = execute_inbox_item(
        item.item_id,
        inbox=inbox,
        data_dir=str(tmp_path),
        actor={"device_id": "device-a", "agent_id": "dashboard.operator"},
        facade=facade,
        account_resolver=lambda fallback="": "oasyce1owner",
    )

    assert executed.execution_status == "succeeded"
    assert executed.execution_result["asset_state"] == "deprecated"
    assert executed.coordination["executed_by"]["agent_id"] == "dashboard.operator"
    call_name, payload = facade.calls[0]
    assert call_name == "deprecate"
    assert payload["asset_id"] == "asset_001"
    assert payload["owner"] == "oasyce1owner"
    ledger = get_portfolio_ledger_store(str(tmp_path)).summary()
    assert ledger["recent"][0]["action"] == "deprecate"
    assert ledger["recent"][0]["status"] == "succeeded"


def test_execute_archive_item_records_success(tmp_path):
    inbox = ConfirmationInbox(data_dir=str(tmp_path))
    item = inbox.add_pending_archive("asset_002", "archived")
    inbox.approve(item.item_id, actor={"device_id": "device-a", "agent_id": "dashboard.operator"})

    facade = _FakeFacade()
    executed = execute_inbox_item(
        item.item_id,
        inbox=inbox,
        data_dir=str(tmp_path),
        actor={"device_id": "device-a", "agent_id": "dashboard.operator"},
        facade=facade,
        account_resolver=lambda fallback="": "oasyce1owner",
    )

    assert executed.execution_status == "succeeded"
    assert executed.execution_result["asset_state"] == "archived"
    call_name, payload = facade.calls[0]
    assert call_name == "archive"
    assert payload["asset_id"] == "asset_002"
    ledger = get_portfolio_ledger_store(str(tmp_path)).summary()
    assert ledger["recent"][0]["action"] == "archive"
    assert ledger["recent"][0]["status"] == "succeeded"


def test_execute_deprecate_item_records_failure(tmp_path):
    inbox = ConfirmationInbox(data_dir=str(tmp_path))
    item = inbox.add_pending_deprecate("asset_003", "superseded")
    inbox.approve(item.item_id, actor={"device_id": "device-a", "agent_id": "dashboard.operator"})

    executed = execute_inbox_item(
        item.item_id,
        inbox=inbox,
        data_dir=str(tmp_path),
        actor={"device_id": "device-a", "agent_id": "dashboard.operator"},
        facade=_FailingFacade(),
        account_resolver=lambda fallback="": "oasyce1owner",
    )

    assert executed.execution_status == "failed"
    assert executed.execution_error == "deprecate failed"
    ledger = get_portfolio_ledger_store(str(tmp_path)).summary()
    assert ledger["recent"][0]["action"] == "deprecate"
    assert ledger["recent"][0]["status"] == "failed"


def test_execute_records_missing_account_failure_in_ledger(tmp_path):
    target = tmp_path / "repo" / "notes.md"
    target.parent.mkdir(parents=True)
    target.write_text("hello\n", encoding="utf-8")

    inbox = ConfirmationInbox(data_dir=str(tmp_path))
    item = inbox.add_pending_register(str(target), "Notes", ["docs"])
    inbox.approve(item.item_id, actor={"device_id": "device-a", "agent_id": "dashboard.operator"})

    executed = execute_inbox_item(
        item.item_id,
        inbox=inbox,
        data_dir=str(tmp_path),
        actor={"device_id": "device-a", "agent_id": "dashboard.operator"},
        facade=_FakeFacade(),
        account_resolver=lambda fallback="": "",
    )

    assert executed.execution_status == "failed"
    ledger = get_portfolio_ledger_store(str(tmp_path)).summary()
    assert ledger["totals"]["failed"] == 1
    assert "canonical account" in (ledger["recent"][0]["error"] or "")
