# -*- coding: utf-8 -*-
"""二维码处理核心类 - 基于 OpenCV QRCodeDetector

本模块使用 OpenCV 的 QRCodeDetector 进行二维码识别，
配合多种图像预处理策略，确保高识别率和跨平台兼容性。
"""
import base64
import io
from typing import Optional, Callable, Tuple

import cv2
import httpx
import numpy as np
import qrcode
from PIL import Image


class QRCodeProcessor:
    """二维码处理器 - 负责二维码的生成、解析和图像预处理"""

    # 预处理策略映射
    PREPROCESSING_STRATEGIES = {
        "original": ("原图", lambda img, gray: img),
        "grayscale": ("灰度图", lambda img, gray: cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)),
        "clahe": ("CLAHE增强", lambda img, gray: cv2.cvtColor(
            cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)).apply(gray),
            cv2.COLOR_GRAY2BGR
        )),
        "adaptive_threshold": ("自适应二值化", lambda img, gray: cv2.cvtColor(
            cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2),
            cv2.COLOR_GRAY2BGR
        )),
        "otsu": ("Otsu二值化", lambda img, gray: cv2.cvtColor(
            cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1],
            cv2.COLOR_GRAY2BGR
        )),
        "denoise": ("去噪+二值化", lambda img, gray: cv2.cvtColor(
            cv2.threshold(
                cv2.fastNlMeansDenoising(gray, None, 10, 7, 21),
                0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU
            )[1],
            cv2.COLOR_GRAY2BGR
        )),
        "sharpen": ("锐化", lambda img, gray: cv2.cvtColor(
            cv2.filter2D(gray, -1, np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]])),
            cv2.COLOR_GRAY2BGR
        )),
        "morphology": ("形态学处理", lambda img, gray: cv2.cvtColor(
            cv2.morphologyEx(
                cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2),
                cv2.MORPH_CLOSE,
                cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
            ),
            cv2.COLOR_GRAY2BGR
        )),
        "scale_0.5": ("0.5x缩放", lambda img, gray: cv2.resize(
            img, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA
        )),
        "scale_1.5": ("1.5x缩放", lambda img, gray: cv2.resize(
            img, None, fx=1.5, fy=1.5, interpolation=cv2.INTER_CUBIC
        )),
        "scale_2.0": ("2.0x缩放", lambda img, gray: cv2.resize(
            img, None, fx=2.0, fy=2.0, interpolation=cv2.INTER_CUBIC
        )),
    }

    # 策略组合映射
    STRATEGY_MAP = {
        "basic": ["original", "grayscale"],
        "enhanced": ["original", "grayscale", "clahe", "adaptive_threshold", "otsu", "denoise", "sharpen", "morphology"],
        "all": ["original", "grayscale", "clahe", "adaptive_threshold", "otsu", "denoise", "sharpen", "morphology", "scale_0.5", "scale_1.5", "scale_2.0"],
    }

    @staticmethod
    def generate(
        content: str,
        size: int = 300,
        error_correction: str = "H",
        box_size: int = 10,
        border: int = 4
    ) -> str:
        """
        生成二维码

        Args:
            content: 二维码内容
            size: 图片大小（像素）
            error_correction: 纠错级别 (L/M/Q/H)
            box_size: 每个格子的像素大小
            border: 边框大小（格子数）

        Returns:
            base64编码的图片数据 (data:image/png;base64,...)

        Raises:
            ValueError: 参数错误时抛出异常
        """
        # 验证纠错级别
        error_levels = {
            "L": qrcode.constants.ERROR_CORRECT_L,  # 7% 容错
            "M": qrcode.constants.ERROR_CORRECT_M,  # 15% 容错
            "Q": qrcode.constants.ERROR_CORRECT_Q,  # 25% 容错
            "H": qrcode.constants.ERROR_CORRECT_H,  # 30% 容错
        }

        if error_correction not in error_levels:
            raise ValueError(f"无效的纠错级别: {error_correction}，支持: L/M/Q/H")

        # 创建二维码对象
        qr = qrcode.QRCode(
            version=None,  # 自动确定版本
            error_correction=error_levels[error_correction],
            box_size=box_size,
            border=border,
        )

        # 添加数据并生成
        qr.add_data(content)
        qr.make(fit=True)

        # 生成图片
        img = qr.make_image(fill_color="black", back_color="white")

        # 调整到指定大小
        img = img.resize((size, size), Image.Resampling.LANCZOS)

        # 转换为base64
        buffer = io.BytesIO()
        img.save(buffer, format="PNG")
        img_base64 = base64.b64encode(buffer.getvalue()).decode()

        return f"data:image/png;base64,{img_base64}"

    @staticmethod
    async def load_image(image_source: str) -> np.ndarray:
        """
        加载图片（支持URL、base64、本地路径）

        Args:
            image_source: 图片来源
                - URL: http://... 或 https://...
                - base64: data:image/...;base64,...
                - 本地路径: /path/to/image.png

        Returns:
            OpenCV图片对象 (BGR格式)

        Raises:
            ValueError: 图片加载失败时抛出异常
        """
        try:
            # 检查是否为base64
            if image_source.startswith("data:image"):
                # 提取base64数据
                base64_data = image_source.split(",")[1]
                img_data = base64.b64decode(base64_data)
                img_array = np.frombuffer(img_data, np.uint8)
                img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)

            elif image_source.startswith("http://") or image_source.startswith("https://"):
                # 下载图片
                async with httpx.AsyncClient(timeout=30.0) as client:
                    response = await client.get(image_source)
                    response.raise_for_status()
                    img_array = np.frombuffer(response.content, np.uint8)
                    img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)

            else:
                # 本地文件
                img = cv2.imread(image_source)

            if img is None:
                raise ValueError("无法解析图片数据")

            return img

        except Exception as e:
            raise ValueError(f"图片加载失败: {str(e)}")

    @staticmethod
    async def search_qrcode(img: np.ndarray)-> list[np.ndarray]:
        """
        搜索二维码，只搜索不解析

        Args:
            img: OpenCV图像对象

        Returns:
            二维码列表
        """
        detector = cv2.QRCodeDetector()
        imgs = detector.detect(img)

    @staticmethod
    def decode(img: np.ndarray) -> Optional[str]:
        """
        使用 OpenCV QRCodeDetector 解码二维码

        Args:
            img: OpenCV图像对象

        Returns:
            二维码内容，如果没有检测到返回None
        """
        detector = cv2.QRCodeDetector()
        data, bbox, _ = detector.detectAndDecode(img)

        if data:
            return data

        return None

    @staticmethod
    async def parse(
        image_source: str,
        strategy: str = "auto"
    ) -> dict:
        """
        解析二维码（使用 OpenCV + 按需预处理策略）

        解码策略：
        1. 根据 strategy 参数选择预处理步骤列表
        2. 按需应用每种预处理算法并立即尝试解码
        3. 一旦成功立即返回，避免不必要的计算

        Args:
            image_source: 图片来源（URL/base64/本地路径）
            strategy: 预处理策略
                - basic: 原图 + 灰度图（2种）
                - enhanced: basic + 6种增强算法（8种）
                - all: auto + 多尺度（11种）

        Returns:
            解析结果字典:
            {
                "success": bool,
                "content": str or None,
                "strategy_used": str,  # 使用的预处理策略名称
                "preprocessing_index": int,  # 预处理索引
                "total_attempts": int,
                "message": str  # 仅失败时有
            }
        """
        # 验证策略参数
        if strategy not in QRCodeProcessor.STRATEGY_MAP:
            raise ValueError(f"无效的策略: {strategy}，支持: {list(QRCodeProcessor.STRATEGY_MAP.keys())}")

        # 加载原始图片
        img = await QRCodeProcessor.load_image(image_source)

        # 预先生成灰度图（很多预处理都需要）
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        # 获取该策略对应的预处理步骤列表
        preprocessing_steps = QRCodeProcessor.STRATEGY_MAP[strategy]

        # 依次应用每种预处理并尝试解码
        for idx, step_key in enumerate(preprocessing_steps):
            strategy_name, preprocess_func = QRCodeProcessor.PREPROCESSING_STRATEGIES[step_key]

            try:
                # 按需处理图像
                processed_img = preprocess_func(img, gray)

                # 立即尝试解码
                result = QRCodeProcessor.decode(processed_img)

                if result:
                    # 解码成功，立即返回
                    return {
                        "success": True,
                        "content": result,
                        "strategy_used": f"opencv_{strategy_name}",
                        "preprocessing_index": idx,
                        "total_attempts": idx + 1
                    }

            except Exception as e:
                # 某个预处理步骤失败，继续尝试下一个
                continue

        # 所有预处理方法都失败
        return {
            "success": False,
            "content": None,
            "message": f"未检测到二维码或二维码损坏（已尝试 {len(preprocessing_steps)} 种预处理）",
            "total_attempts": len(preprocessing_steps)
        }

if __name__ == "__main__":
    import asyncio

    async def main():
        # 示例用法
        result = await QRCodeProcessor.parse("F:/Project/urbanLifeline/docs/qrcode.png", "enhanced")
        print(result)

    asyncio.run(main())
