from decimal import Decimal
import json

import binance_quant.auto_sell as auto_sell
from binance_quant.models import SymbolMetadata
from binance_quant.storage import Storage


METADATA = SymbolMetadata(
    "BTCUSDT",
    "BTC",
    "USDT",
    Decimal("5"),
    Decimal("0.00001"),
    Decimal("0.00001"),
    Decimal("0.01"),
    Decimal("0.01"),
)
CANDIDATE = auto_sell.AutoSellCandidate(
    "BTCUSDT",
    Decimal("4.5"),
    Decimal("50000"),
    Decimal("5"),
    ("negative momentum", "bearish volume"),
)


class FakeExchange:
    def __init__(self, payload=None, error: Exception | None = None):
        self.payload = payload
        self.error = error
        self.calls = []

    def create_market_sell_quantity(self, symbol, quantity, client_order_id):
        self.calls.append(("sell", symbol, quantity, client_order_id))
        if self.error:
            raise self.error
        if self.payload is not None and not isinstance(self.payload, dict):
            return self.payload
        if self.payload is not None:
            return {"clientOrderId": client_order_id} | self.payload
        return {
            "symbol": symbol,
            "side": "SELL",
            "type": "MARKET",
            "status": "FILLED",
            "clientOrderId": client_order_id,
            "executedQty": str(quantity),
        }


def storage(tmp_path):
    audit = Storage(tmp_path / "audit.sqlite3")
    audit.initialize()
    return audit


def test_successful_auto_sell_submits_market_sell_and_records_audit(tmp_path):
    audit = storage(tmp_path)
    exchange = FakeExchange()

    result = auto_sell.AutoSellExecutionEngine(exchange, audit).execute(CANDIDATE, METADATA, Decimal("0.001"))

    assert result.status == "sold"
    assert result.symbol == "BTCUSDT"
    assert result.reason == "market sell filled"
    assert exchange.calls[0][:3] == ("sell", "BTCUSDT", Decimal("0.001"))
    assert exchange.calls[0][3].startswith("bqas-sell-")
    [run] = audit.list_auto_sell_runs()
    assert run["id"] == result.run_id
    assert run["status"] == "sold"
    assert run["quantity"] == "0.001"
    [event] = audit.list_auto_sell_events(result.run_id)
    assert event["event_type"] == "market_sell"
    assert json.loads(event["raw_json"])["status"] == "FILLED"


def test_auto_sell_exception_records_sanitized_failed_run(tmp_path):
    audit = storage(tmp_path)
    sensitive_text = "sell rejected https://internal.example?signature=secret&apiKey=abc"
    exchange = FakeExchange(error=RuntimeError(sensitive_text))

    result = auto_sell.AutoSellExecutionEngine(exchange, audit).execute(CANDIDATE, METADATA, Decimal("0.001"))

    assert result.status == "sell_failed"
    assert result.reason == "sell submission failed"
    [run] = audit.list_auto_sell_runs()
    assert run["status"] == "sell_failed"
    [event] = audit.list_auto_sell_events(result.run_id)
    assert event["event_type"] == "sell_failed"
    assert event["status"] == "CRITICAL"
    raw_json = event["raw_json"]
    assert json.loads(raw_json) == {"message": "sell submission failed"}
    assert "internal.example" not in raw_json
    assert "signature" not in raw_json
    assert "apiKey" not in raw_json


def test_invalid_auto_sell_response_records_failed_run(tmp_path):
    audit = storage(tmp_path)
    exchange = FakeExchange({"symbol": "BTCUSDT", "status": "NEW", "executedQty": "0.001"})

    result = auto_sell.AutoSellExecutionEngine(exchange, audit).execute(CANDIDATE, METADATA, Decimal("0.001"))

    assert result.status == "sell_failed"
    assert result.reason == "invalid sell response"
    [event] = audit.list_auto_sell_events(result.run_id)
    assert event["event_type"] == "market_sell"
    assert event["status"] == "NEW"
