import socket
from rest_framework.exceptions import ParseError
import json
import re
import time
from django.core.cache import cache
from apps.utils.thread import MyThread
import uuid
import logging
import threading
import requests

myLogger = logging.getLogger('log')

def get_checksum(body_msg):
    return sum(body_msg) & 0xFF

def handle_bytes(arr):
    if len(arr) < 8:
        return f"返回数据长度错误-{arr}"
    
    if arr[0] != 0xEB or arr[1] != 0x90:
        return "数据头不正确"


    # 读取长度信息
    length_arr = arr[2:4][::-1]  # 反转字节
    length = int.from_bytes(length_arr, byteorder='little', signed=True)  # 小端格式
    
    # 提取内容
    body = arr[4:4 + length - 3]

    # 校验和检查
    check_sum = get_checksum(body)
    if check_sum != arr[length + 1]:
        return "校验错误"
        

    # 尾部标识检查
    if arr[length + 2] != 0xFF or arr[length + 3] != 0xFE:
        return "尾错误"

    content = body.decode('utf-8')

    res = json.loads(content)
    
    return res[0]

def get_tyy_data_t(host, port, tid):
    cd_thread_key_id = f"cd_thread_{host}_{port}_id"
    cd_thread_key_val = f"cd_thread_{host}_{port}_val"
    sc = None
    def connect_and_send(retry=1):
        nonlocal sc
        try:
            if sc is None:
                sc = socket.socket()
                sc.connect((host, int(port)))
            sc.sendall(b"R")
        except BrokenPipeError:
            if retry > 0:
                connect_and_send(retry-1)
            else:
                if sc:
                    try:
                        sc.close()
                    except Exception:
                        pass
                sc = None
        except OSError as e:
            sc = None
            cache.set(cd_thread_key_val, {"err_msg": f"采集器连接失败-{str(e)}"})
        except ConnectionResetError:
            sc = None
            cache.set(cd_thread_key_val, {"err_msg": "采集器重置了连接"})
        except socket.timeout:
            sc = None
            cache.set(cd_thread_key_val, {"err_msg": "采集器连接超时"})
        except Exception as e:
            sc = None
            cache.set(cd_thread_key_val, {"err_msg": f"采集器连接失败-{str(e)}"})

    while cache.get(cd_thread_key_id) == tid:
        if cache.get(cd_thread_key_val) == "get":
            cache.set(cd_thread_key_val, "working")
            connect_and_send()
            if sc is None:
                continue
            resp = sc.recv(1024)
            res = handle_bytes(resp)
            if isinstance(res, str):
                cache.set(cd_thread_key_val, {"err_msg": f'采集器返回数据错误-{res}'})
            elif not res:
                cache.set(cd_thread_key_val, {"err_msg": f"采集器返回数据为空-{str(res)}"})
            else:
                myLogger.info(f"采集器返回数据-{res}")
                cache.set(cd_thread_key_val, res)
        time.sleep(0.3)

    if sc:
        try:
            sc.close()
        except Exception:
            pass

def get_tyy_data_2(*args, retry=1):
    host, port = args[0], int(args[1])
    cd_thread_key_id = f"cd_thread_{host}_{port}_id"
    cd_thread_key_val = f"cd_thread_{host}_{port}_val"
    cd_thread_val_id = cache.get(cd_thread_key_id, default=None)
    if cd_thread_val_id is None:
        tid = uuid.uuid4()
        cache.set(cd_thread_key_id, tid, timeout=10800)
        cd_thread = MyThread(target=get_tyy_data_t, args=(host, port, tid), daemon=True)
        cd_thread.start()
    cache.set(cd_thread_key_val, "get")
    num = 0
    get_val = False

    while True:
        num += 1
        if num > 8:
            break
        val = cache.get(cd_thread_key_val)
        if isinstance(val, dict):
            get_val = True
            if "err_msg" in val:
                raise ParseError(val["err_msg"])
            return val
        time.sleep(0.3)

    if not get_val and retry > 0:
        cache.set(cd_thread_key_id, None)
        get_tyy_data_2(*args, retry=retry-1)


sc_all = {}
sc_lock = threading.Lock()

def get_tyy_data_1(*args, retry=1):
    host, port = args[0], int(args[1])
    global sc_all
    sc = None
    
    def connect_and_send(retry=1):
        nonlocal sc
        sc = sc_all.get(f"{host}_{port}", None)
        try:
            if sc is None:
                sc = socket.socket()
                sc.settimeout(5)  # 设置超时
                sc.connect((host, port))
                sc_all[f"{host}_{port}"] = sc
            else:
                # 清空接收缓冲区
                sc.settimeout(0.1)  # 设置短暂超时
                for _ in range(5):
                    try:
                        data = sc.recv(65536)
                        if not data:
                            break
                    except (socket.timeout, BlockingIOError):
                        break
                sc.settimeout(5)  # 恢复原超时设置
            sc.sendall(b"R")
        except BrokenPipeError:
            if retry > 0:
                if sc:
                    try:
                        sc.close()
                    except Exception:
                        pass
                    sc_all.pop(f"{host}_{port}", None)
                return connect_and_send(retry-1)
            else:
                if sc:
                    try:
                        sc.close()
                    except Exception:
                        pass
                    sc_all.pop(f"{host}_{port}", None)
                sc = None
                raise ParseError("采集器连接失败-管道重置")
        except OSError as e:
            if sc:
                try:
                    sc.close()
                except Exception:
                    pass
                sc_all.pop(f"{host}_{port}", None)
            sc = None
            raise ParseError(f"采集器连接失败-{str(e)}")
        except TimeoutError as e:
            if sc:
                try:
                    sc.close()
                except Exception:
                    pass
                sc_all.pop(f"{host}_{port}", None)
            sc = None
            raise ParseError(f"采集器连接超时-{str(e)}")
    
    with sc_lock:
        connect_and_send()
        resp = sc.recv(1024)
        res = handle_bytes(resp)
        # myLogger.error(res)
        if isinstance(res, str):
            raise ParseError(f'采集器返回数据错误-{res}')
        else:
            return res


def get_tyy_data_3(*args, retry=2):
    host, port = args[0], int(args[1])
    for attempt in range(retry):
        try:
            # 每次请求都新建连接（确保无共享状态）
            with socket.create_connection((host, port), timeout=10) as sc:
                sc.sendall(b"R")
                
                # 接收完整响应（避免数据不完整）
                # resp = b""
                # while True:
                #     chunk = sc.recv(4096)
                #     if not chunk:
                #         break
                #     resp += chunk
                resp = sc.recv(4096)
                if not resp:
                    raise ParseError("设备未启动")
                
                res = handle_bytes(resp)
                if isinstance(res, str):
                    raise ParseError(f"采集器返回数据错误: {res}")
                return res
        except (socket.timeout, ConnectionError) as e:
            if attempt == retry - 1:  # 最后一次尝试失败才报错
                raise ParseError(f"采集器连接失败: {str(e)}")
            time.sleep(0.5)  # 失败后等待 1s 再重试
        except ParseError:
            raise
        except Exception as e:
            raise ParseError(f"未知错误: {str(e)}")
        

def _parse_vtfp_line(line):
    """解析 SVS 的 VTFP 应答行，返回 (识别字符, 字段列表)。

    返回格式形如 ``VTFP 0 <用户自定义数据...>``：
    - 第 1 个字段固定为 ``VTFP``
    - 第 2 个字段为状态码（0 成功，非 0 失败，失败时后面无其他数据）
    - 其余字段为用户在程序中自定义输出的数据（即识别出的字符）

    分隔符按协议同时兼容空格和逗号。
    """
    parts = [p for p in re.split(r"[,\s]+", line.strip()) if p != ""]
    if not parts or parts[0].upper() != "VTFP":
        raise ParseError(f"SVS返回格式错误-{line}")
    if len(parts) < 2:
        raise ParseError(f"SVS返回缺少状态码-{line}")
    status = parts[1]
    if status != "0":
        raise ParseError(f"SVS触发失败,状态码-{status}")
    fields = parts[2:]  # 用户自定义数据（识别字符），可能含可选的流程单元号回显
    char = " ".join(fields)
    return char, fields


def get_svs_char(host, port, flow_unit=0, timeout=10):
    """连接 SVS（维视智造 VisionBank），用 VTFP 触发拍照并取回识别字符。

    返回 ``{"char": 识别字符, "fields": [...], "raw": 原始应答行}``。

    不同流程的输出配置可能不一样：有的回 ``VTFP 0 <数据>`` 完整应答，有的只回
    用户自定义的识别内容(如 ``1``)。这里读到第一行完整数据就返回：带 ``VTFP``
    前缀的按协议解析，否则整行当作识别内容。
    """
    cmd = f"VTFP {flow_unit}\r".encode("ascii")  # 该设备只认单 CR 结尾, 带 \n 会无响应
    try:
        with socket.create_connection((host, int(port)), timeout=timeout) as sc:
            buf = bytearray()
            sc.sendall(cmd)
            # 第一次用完整超时等设备响应; 设备返回的识别内容(如 b'1')末尾可能没有
            # 换行符, 因此拿到数据后只用很短的超时把剩余字节收干净(兼容多位数分包),
            # 不再死等换行, 否则会卡到超时
            chunk = sc.recv(1024)
            if chunk:
                buf.extend(chunk)
                sc.settimeout(0.3)
                while b"\n" not in buf and b"\r" not in buf:
                    try:
                        more = sc.recv(1024)
                    except socket.timeout:
                        break
                    if not more:
                        break
                    buf.extend(more)
            text = buf.decode("utf-8", errors="replace")
            for ln in re.split(r"[\r\n]+", text):
                ln = ln.strip()
                if not ln:
                    continue
                if ln.upper().startswith("VTFP"):
                    char, fields = _parse_vtfp_line(ln)
                    return {"char": char, "fields": fields, "raw": ln}
                # 无 VTFP 前缀，设备直接回识别内容
                return {"char": ln, "fields": ln.split(), "raw": ln}
    except ParseError:
        raise
    except (socket.timeout, ConnectionError, OSError) as e:
        raise ParseError(f"SVS连接失败-{str(e)}")
    raise ParseError("SVS未返回有效数据")


def get_tyy_data(*args):
    host, port = args[0], int(args[1])
    r = requests.get(f"http://127.0.0.1:2300?host={host}&port={port}")
    res = r.json()
    if "err_msg" in res:
        raise ParseError(res["err_msg"])
    return res
