# -*- coding: utf-8 -*-
"""rpa_030_save.py 测试：mock 掉 pymssql / CosS3Client / urlopen，验证 API/source 分支与回调payload"""
import sys, os, json, tempfile, threading, contextlib
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'python'))

import rpa_030_save as m

ok = True
def check(name, got, expected):
    global ok
    if got != expected:
        ok = False
        print("[FAIL] %s: got %r, expected %r" % (name, got, expected))
    else:
        print("[PASS] %s" % name)

# ---------- 1. post_json 对本地 mock HTTP 服务器 ----------
from http.server import BaseHTTPRequestHandler, HTTPServer
received = {}
class Handler(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers.get('Content-Length', 0))
        body = self.rfile.read(length).decode('utf-8')
        received['body'] = json.loads(body)
        received['content_type'] = self.headers.get('Content-Type')
        received['path'] = self.path
        self.send_response(200)
        self.send_header('Content-Type', 'application/json')
        self.end_headers()
        self.wfile.write(b'{"code":200}')
    def log_message(self, *a):
        pass

server = HTTPServer(('127.0.0.1', 0), Handler)
threading.Thread(target=server.serve_forever, daemon=True).start()
port = server.server_address[1]

result = m.post_json('http://127.0.0.1:%d/delivery/rpa/callback' % port, {'a': 1})
check("post_json-success", (result['success'], result['http_code']), (True, 200))
check("post_json-content-type", received['content_type'], 'application/json; charset=utf-8')
check("post_json-path", received['path'], '/delivery/rpa/callback')
check("post_json-body", received['body'], {'a': 1})
server.shutdown()

# ---------- 2. notify_api_result payload 结构 ----------
captured = {}
class FakeHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers.get('Content-Length', 0))
        captured['body'] = json.loads(self.rfile.read(length).decode('utf-8'))
        self.send_response(200)
        self.end_headers()
        self.wfile.write(b'{}')
    def log_message(self, *a):
        pass

server = HTTPServer(('127.0.0.1', 0), FakeHandler)
threading.Thread(target=server.serve_forever, daemon=True).start()
url = 'http://127.0.0.1:%d/delivery/rpa/callback' % server.server_address[1]

biz = {'BusinessSerialNumber': 'SN123', 'BusinessId': 888}
m.notify_api_result(42, url, True,
                    file_urls={'pdf030_url': 'https://cos/030.pdf', 'file1': 'key1',
                               'cos_key': 'key1', 'cos_url': 'https://cos/merged.pdf'},
                    biz_param=biz)
b = captured['body']
check("success-code", b['code'], 200)
check("success-msg", b['msg'], 'success')
check("success-mode", b['ProcessMode'], 'async')
check("success-status", b['data']['status'], 'success')
check("success-taskid", b['data']['task_id'], 42)
check("success-pdf030_url", b['data']['pdf030_url'], 'https://cos/030.pdf')
check("success-file1", b['data']['file1'], 'key1')
check("success-bizParam", b['bizParam'], biz)

m.notify_api_result(42, url, False, error='文件不存在', biz_param=biz)
b = captured['body']
check("fail-code", b['code'], 500)
check("fail-msg", b['msg'], 'failed')
check("fail-status", b['data']['status'], 'failed')
check("fail-error", b['data']['error'], '文件不存在')
check("fail-no-url", 'pdf030_url' in b['data'], False)
server.shutdown()

# ---------- 3. parse_biz_param / parse_result_data / query_task_info ----------
ti = {'data_source': 'api',
      'task_data': json.dumps({'bizParam': {'BusinessSerialNumber': 'SN1'}, 'request_data': {}}, ensure_ascii=False),
      'result_data': json.dumps({'file1': 'k1', 'cos_key': 'k1', 'cos_url': 'https://c/m.pdf'})}
check("parse-bizParam", m.parse_biz_param(ti), {'BusinessSerialNumber': 'SN1'})
check("parse-result", m.parse_result_data(ti)['cos_url'], 'https://c/m.pdf')
check("parse-bizParam-invalid", m.parse_biz_param({'task_data': 'not-json'}), None)
check("parse-result-invalid", m.parse_result_data(None), {})

class FakeCursor:
    def __init__(self, raise_prog=False):
        self.raise_prog = raise_prog
    def execute(self, sql, params):
        if self.raise_prog:
            raise m.pymssql.ProgrammingError('Invalid column name DataSource')
        return self
    def fetchone(self):
        return ('api', '{"bizParam":{}}', '{"file1":"k"}')

check("query-task-info", m.query_task_info(FakeCursor(), 1)['data_source'], 'api')
check("query-task-info-207-fallback", m.query_task_info(FakeCursor(raise_prog=True), 1), None)

# ---------- 4. 分支逻辑：mock pymssql + CosS3Client + urlopen ----------
class FakeDb:
    def __init__(self):
        self.committed = 0
        self.rolled = 0
        self.executed = []
    def cursor(self):
        return FakeCursor2(self)
    def commit(self):
        self.committed += 1
    def rollback(self):
        self.rolled += 1
    def close(self):
        pass

class FakeCursor2:
    def __init__(self, db):
        self.db = db
        self.rowcount = 0
    def execute(self, sql, params=None):
        self.db.executed.append((sql, params))
        return self
    def fetchone(self):
        sql = self.db.executed[-1][0] if self.db.executed else ''
        params = self.db.executed[-1][1] if self.db.executed else None
        task_id = params[0] if params else None
        if 'DataSource' in sql:
            if task_id == 44:  # source 流程任务
                return ('source', '{}', '{}')
            return ('api', '{"bizParam":{"BusinessSerialNumber":"SN9"}}',
                    '{"file1":"k1","cos_key":"k1","cos_url":"https://c/m.pdf"}')
        return None
    def close(self):
        pass

real_connect = m.pymssql.connect
real_urlopen = m.urllib.request.urlopen
notify_payloads = []

def fake_urlopen(req, timeout=None):
    notify_payloads.append(json.loads(req.data.decode('utf-8')))
    class R:
        status = 200
        def read(self):
            return b'{}'
    return contextlib.nullcontext(R())

def fake_upload(self, **kw):
    return {}

def fake_connect(server, port, user, password, database, charset='utf8'):
    return FakeDb()

m.pymssql.connect = fake_connect
m.urllib.request.urlopen = fake_urlopen
m.CosS3Client = lambda config: type('C', (), {'upload_file': fake_upload})()

tmp = tempfile.NamedTemporaryFile(suffix='.pdf', delete=False)
tmp.write(b'%PDF-1.4')
tmp.close()

# API 流程成功：不应连接 vat_db，应通知成功
r = m.upload_to_cos_and_update_db('ip', 1433, 'u', 'p', 'vat_db', 'rpa',
                                  'sid', 'skey', 'ap-guangzhou', 'bucket', 'vat/spain/',
                                  tmp.name, 888, 42)
check("api-success", r['success'], True)
check("api-callback-success", r['callback_success'], True)
check("api-success-no-file_id", 'file_id' in r, False)
check("api-payload-code", notify_payloads[-1]['code'], 200)
check("api-payload-status", notify_payloads[-1]['data']['status'], 'success')
check("api-payload-biz", notify_payloads[-1]['bizParam'], {'BusinessSerialNumber': 'SN9'})
check("api-payload-030url",
      notify_payloads[-1]['data']['pdf030_url'].startswith('https://bucket.cos.ap-guangzhou.myqcloud.com/'), True)
check("api-payload-hagueurl", notify_payloads[-1]['data']['cos_url'], 'https://c/m.pdf')

# API 流程文件缺失 → 失败通知
r = m.upload_to_cos_and_update_db('ip', 1433, 'u', 'p', 'vat_db', 'rpa',
                                  'sid', 'skey', 'ap-guangzhou', 'bucket', 'vat/spain/',
                                  '/nonexistent/file.pdf', 888, 43)
check("api-file-missing", r['success'], False)
check("api-file-missing-callback", notify_payloads[-1]['code'], 500)
check("api-file-missing-status", notify_payloads[-1]['data']['status'], 'failed')
check("api-file-missing-error", '本地文件不存在' in notify_payloads[-1]['data']['error'], True)

# source 流程：应连接 vat_db（两次 connect），走附件表逻辑
conns = []
def fake_connect2(server, port, user, password, database, charset='utf8'):
    conns.append(database)
    return FakeDb()
m.pymssql.connect = fake_connect2
r = m.upload_to_cos_and_update_db('ip', 1433, 'u', 'p', 'vat_db', 'rpa',
                                  'sid', 'skey', 'ap-guangzhou', 'bucket', 'vat/spain/',
                                  tmp.name, 888, 44)
check("source-success", r['success'], True)
check("source-two-dbs", conns, ['rpa', 'vat_db'])
check("source-has-file_id", 'file_id' in r, True)

os.unlink(tmp.name)
m.pymssql.connect = real_connect
m.urllib.request.urlopen = real_urlopen

print("\nALL PASS" if ok else "\nSOME FAILED")
sys.exit(0 if ok else 1)
