"""kiwoom_order.submit 응답 처리 회귀 테스트.

명세 (PDF 2026-05-07 검증):
- kt10000/kt10001 응답 필드: ord_no, dmst_stex_tp, return_code, return_msg
- 정상: return_code == 0
- ord_seq_no, rt_cd 같은 키는 명세에 없음
"""

from __future__ import annotations

import unittest
from unittest import mock

from orders import kiwoom_order, ledger, sidecar


class SubmitResponseTests(unittest.TestCase):
    def setUp(self):
        mock.patch.object(sidecar, 'guard_or_raise', return_value=None).start()
        mock.patch.object(ledger, 'append', return_value=None).start()
        mock.patch.object(ledger, 'find_recent_idempotency', return_value=False).start()
        # idempotency_hash 는 순수 함수라 mock 불필요
        self.kc_post = mock.patch('orders.kiwoom_order.kc._http_post_json').start()
        mock.patch('orders.kiwoom_order.kc.auth_headers', return_value={}).start()
        mock.patch('orders.kiwoom_order.kc.base_url', return_value='https://api.kiwoom.com').start()
        self.addCleanup(mock.patch.stopall)

    def _submit(self):
        return kiwoom_order.submit(
            account_label='일반', side='BUY', symbol='005930', qty=1,
            price=None, order_type='MARKET', routing_suffix='_AL',
            dry_run=False, card_id='TEST',
        )

    def test_normal_response_succeeds(self):
        """명세 정상 응답 — return_code=0, ord_no 추출."""
        self.kc_post.return_value = {
            'ord_no': '0000138',
            'dmst_stex_tp': 'KRX',
            'return_code': 0,
            'return_msg': '매수주문이 완료되었습니다.',
        }
        res = self._submit()
        self.assertTrue(res['ok'])
        self.assertEqual(res['ord_no'], '0000138')

    def test_broker_reject_when_return_code_nonzero(self):
        """return_code != 0 → BROKER_REJECT."""
        self.kc_post.return_value = {
            'return_code': 1,
            'return_msg': '주문불가종목',
        }
        res = self._submit()
        self.assertFalse(res['ok'])
        self.assertEqual(res['reason'], 'BROKER_REJECT')

    def test_unexpected_response_type_falls_to_network(self):
        """응답이 dict 아닌 경우 — try 안의 .get 호출 AttributeError → NETWORK."""
        self.kc_post.return_value = 'unexpected string'
        res = self._submit()
        self.assertFalse(res['ok'])
        self.assertEqual(res['reason'], 'NETWORK')

    def test_ord_seq_no_not_used(self):
        """명세에 없는 ord_seq_no fallback은 더 이상 동작하지 않음.

        ord_no 없고 ord_seq_no만 있는 (가짜) 응답 → ord_no 빈 문자열.
        """
        self.kc_post.return_value = {
            'ord_seq_no': '0000999',  # 명세에 없는 키
            'return_code': 0,
        }
        res = self._submit()
        self.assertTrue(res['ok'])
        self.assertEqual(res['ord_no'], '')  # ord_seq_no fallback 제거됨

    def test_rt_cd_not_used(self):
        """명세에 없는 rt_cd 키는 무시. return_code 누락 시 None != 0 → BROKER_REJECT."""
        self.kc_post.return_value = {
            'ord_no': '0000138',
            'rt_cd': '0',  # 명세에 없는 키 — 이걸로는 정상 판정 안 됨
        }
        res = self._submit()
        self.assertFalse(res['ok'])
        self.assertEqual(res['reason'], 'BROKER_REJECT')

    def test_8005_token_retry_then_success(self):
        """첫 호출 8005 토큰 만료 → 재발급 → 두 번째 호출 정상."""
        self.kc_post.side_effect = [
            {'return_code': 1, 'return_msg': '8005 Token이 유효하지 않습니다'},
            {'ord_no': '0000200', 'return_code': 0, 'return_msg': '정상'},
        ]
        with mock.patch('orders.kiwoom_order.kc.issue_token', return_value=None):
            res = self._submit()
        self.assertTrue(res['ok'])
        self.assertEqual(res['ord_no'], '0000200')


if __name__ == '__main__':
    unittest.main(verbosity=2)
