"""对冲计划:人工结束 + 未成交不显示 open."""
from __future__ import annotations

import sqlite3
import unittest
from unittest import mock

from lib.hedge_plan.hedge_plan_db import (
    get_plan,
    get_plan_legs,
    init_hedge_plan_tables,
    insert_leg,
    insert_plan,
    legs_contract_summary,
)
from lib.hedge_plan.hedge_plan_orders_lib import (
    execute_manual_end_plan,
    reconcile_unfilled_option_legs,
)


def _mem():
    conn = sqlite3.connect(":memory:")
    conn.row_factory = sqlite3.Row
    init_hedge_plan_tables(conn)
    return conn


class TestHedgePlanEnd(unittest.TestCase):
    def test_legs_summary_marks_unfilled(self):
        s = legs_contract_summary(
            [
                {"leg_role": "option_a", "inst_id": "ETH-C", "status": "pending"},
                {"leg_role": "option_b", "inst_id": "ETH-P", "status": "open"},
                {"leg_role": "option_a", "inst_id": "X", "status": "cancelled"},
            ]
        )
        self.assertIn("(待补)", s)
        self.assertIn("(未成交)", s)

    def test_reconcile_ghost_open_to_pending(self):
        conn = _mem()
        pid = insert_plan(
            conn,
            {
                "plan_type": "options_options",
                "status": "active",
                "underlying": "ETH",
            },
        )
        insert_leg(
            conn,
            {
                "plan_id": pid,
                "leg_role": "option_a",
                "inst_id": "ETH-USD_UM-260720-1870-C",
                "status": "open",
                "exchange_ord_id": "1",
                "avg_open": 0.01,
            },
        )
        insert_leg(
            conn,
            {
                "plan_id": pid,
                "leg_role": "option_b",
                "inst_id": "ETH-USD_UM-260720-1870-P",
                "status": "open",
                "exchange_ord_id": "2",
                "avg_open": 0.02,
            },
        )
        cfg = {"exchange_options": object()}
        with mock.patch(
            "lib.hedge_plan.hedge_plan_orders_lib._live_option_pos_sheets",
            side_effect=lambda ex, inst: 0.0 if "C" in inst else 50.0,
        ), mock.patch(
            "lib.exchange.okx_options_lib.fetch_option_order",
            return_value={"ok": True, "state": "canceled", "acc_fill_sz": 0},
        ):
            notes = reconcile_unfilled_option_legs(cfg, conn, pid)
        self.assertTrue(notes)
        legs = {l["leg_role"]: l for l in get_plan_legs(conn, pid)}
        self.assertEqual(legs["option_a"]["status"], "pending")
        self.assertEqual(legs["option_b"]["status"], "open")
        self.assertEqual(get_plan(conn, pid)["status"], "partial")

    def test_manual_end_no_flat(self):
        conn = _mem()
        pid = insert_plan(
            conn,
            {
                "plan_type": "options_options",
                "status": "partial",
                "underlying": "ETH",
            },
        )
        insert_leg(
            conn,
            {
                "plan_id": pid,
                "leg_role": "option_a",
                "inst_id": "ETH-C",
                "status": "pending",
                "exchange_ord_id": "9",
            },
        )
        insert_leg(
            conn,
            {
                "plan_id": pid,
                "leg_role": "option_b",
                "inst_id": "ETH-P",
                "status": "open",
                "exchange_ord_id": "8",
            },
        )
        cfg = {"exchange_options": object()}
        with mock.patch(
            "lib.hedge_plan.hedge_plan_orders_lib.reconcile_unfilled_option_legs",
            return_value=[],
        ), mock.patch(
            "lib.exchange.okx_options_lib.cancel_option_order",
            return_value={"ok": True},
        ) as cancel, mock.patch(
            "lib.hedge_plan.hedge_plan_notify_lib.notify_plan_end",
            return_value=True,
        ):
            out = execute_manual_end_plan(cfg, conn, pid)
        self.assertTrue(out.get("ok"))
        self.assertEqual(get_plan(conn, pid)["status"], "closed")
        self.assertEqual(get_plan(conn, pid)["close_reason"], "manual")
        legs = {l["leg_role"]: l for l in get_plan_legs(conn, pid)}
        self.assertEqual(legs["option_a"]["status"], "cancelled")
        self.assertEqual(legs["option_b"]["status"], "open")  # 已有持仓不动
        cancel.assert_called()


if __name__ == "__main__":
    unittest.main()
