import base64
import json
import os
import re
import shutil
import tempfile
import time
import zipfile
from contextlib import contextmanager

import httpx
import orjson
from botocore.exceptions import ClientError
from bs4 import BeautifulSoup
from cryptography.fernet import Fernet
from fastapi import status
from loguru import logger
from vis3.internal.common.exceptions import AppEx, ErrorCode
from vis3.internal.config import settings
from vis3.internal.utils.path import split_s3_path

fernet = Fernet(settings.ENCRYPT_KEY)


@contextmanager
def timer(operation: str):
    start = time.time()
    yield
    duration = time.time() - start
    # 毫秒
    logger.debug(f"{operation} took {duration * 1000:.2f} ms")


def is_valid_ip(ip: str) -> bool:
    pattern = re.compile(
        r"""
        ^
        (?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}
        (?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])
        $
    """,
        re.VERBOSE,
    )
    return pattern.match(ip) is not None


# 是否需要直接读取文件内容，图片、视频、音频、pdf、epub类型直接调用预览接口
def should_not_read_as_raw(mimetype: str):
    result = (
        mimetype.startswith("image/")
        or mimetype.startswith("video/")
        or mimetype.startswith("audio/")
        or mimetype.startswith("application/pdf")
        or mimetype.startswith("application/epub")
    )

    return result


def ping_host(url: str) -> bool:
    if not url.startswith("http"):
        url = "http://" + url

    try:
        response = httpx.get(url, timeout=5)
        # 检查状态码是否在有效范围内
        return response.status_code < 500
    except httpx.RequestError:
        return False
    except Exception:
        raise AppEx(
            code=ErrorCode.PING_50000_ERROR,
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
        )
    
async def validate_path_accessibility(path: str, endpoint: str, ak: str, sk: str):
    from vis3.internal.client.s3_reader import S3Reader

    is_endpoint_valid = ping_host(endpoint)

    if not is_endpoint_valid:
        return False

    client = S3Reader.get_client(
        ak=ak,
        sk=sk,
        endpoint=endpoint,
        region_name=None,
    )
    bucket_name, prefix = split_s3_path(path)

    def _list_objects():
        return client.list_objects(Bucket=bucket_name, Prefix=prefix, MaxKeys=1)

    try:
        res = await S3Reader._run_in_executor(_list_objects)
        contents = res.get("Contents", [])
        logger.info(contents)
    except ClientError:
        return False

    return True


def decrypt_secret_key(encrypted_secret_key: str) -> str:
    encrypted_bytes = base64.urlsafe_b64decode(encrypted_secret_key.encode())
    return fernet.decrypt(encrypted_bytes).decode()


def convert_mobi_stream_to_html(mobi_content: bytes) -> str:
    import mobi

    """
	将mobi文件内容流转换为包含内嵌资源的HTML
	"""
    # 使用BytesIO创建一个内存中的文件对象
    with tempfile.NamedTemporaryFile(delete=False, suffix=".mobi") as temp_file:
        temp_file.write(mobi_content)
        temp_file.flush()
        temp_file_path = temp_file.name

    # 使用 mobi 库将 MOBI 文件解包到临时目录
    mobi_temp_dir, _ = mobi.extract(temp_file_path)

    # 查找解包后的 EPUB 或 HTML 文件
    epub_file = None
    html_files = []
    image_files = {}

    # 收集所有图片文件
    for root, _, files in os.walk(mobi_temp_dir):
        for file in files:
            if file.endswith(".epub"):
                epub_file = os.path.join(root, file)
            elif file.endswith(".html"):
                html_files.append(os.path.join(root, file))
            elif file.lower().endswith((".jpg", ".jpeg", ".png", ".gif")):
                # 存储图片路径，以文件名为键
                image_files[file] = os.path.join(root, file)

    # 如果存在 EPUB 文件，则进一步解压 EPUB 文件
    if epub_file:
        with zipfile.ZipFile(epub_file, "r") as zip_ref:
            zip_ref.extractall(mobi_temp_dir)

        # 再次查找解压后的所有文件
        for root, _, files in os.walk(mobi_temp_dir):
            for file in files:
                if file.endswith(".html"):
                    html_files.append(os.path.join(root, file))
                elif file.lower().endswith((".jpg", ".jpeg", ".png", ".gif")):
                    # 存储图片路径，以文件名为键
                    image_files[file] = os.path.join(root, file)

    # 合并所有 HTML 文件内容
    html_content = ""
    for html_file in html_files:
        with open(html_file, "r", encoding="utf-8", errors="ignore") as f:
            content = f.read()
            soup = BeautifulSoup(content, "html.parser")

            # 处理图片标签，将图片转为base64嵌入
            for img_tag in soup.find_all("img"):
                src = img_tag.get("src")
                if src:
                    # 提取文件名
                    img_filename = os.path.basename(src)
                    if img_filename in image_files:
                        # 读取图片并转换为base64
                        try:
                            with open(image_files[img_filename], "rb") as img_file:
                                img_data = img_file.read()
                                img_type = os.path.splitext(img_filename)[1][1:].lower()
                                if img_type == "jpg":
                                    img_type = "jpeg"
                                base64_data = base64.b64encode(img_data).decode("utf-8")
                                # 更新图片源为base64
                                img_tag["src"] = (
                                    f"data:image/{img_type};base64,{base64_data}"
                                )
                        except Exception as e:
                            logger.error(f"处理图片时出错: {e}")
                            img_tag.replace_with(
                                soup.new_tag(
                                    "div",
                                    attrs={"class": "image-placeholder"},
                                    string="图片加载失败",
                                )
                            )
                    else:
                        # 如果找不到图片文件，显示占位符
                        img_tag.replace_with(
                            soup.new_tag(
                                "div",
                                attrs={"class": "image-placeholder"},
                                string="图片未找到",
                            )
                        )

            html_content += str(soup)

    # 删除临时文件
    shutil.rmtree(mobi_temp_dir)
    os.remove(temp_file_path)

    return html_content


def convert_epub_stream_to_html(epub_content: bytes) -> str:
    import ebooklib
    from ebooklib import epub

    book = None

    with tempfile.NamedTemporaryFile(delete=False, suffix=".epub") as temp_file:
        temp_file.write(epub_content)
        temp_file.flush()

        book = epub.read_epub(temp_file.name)

    html_content = ""
    # 收集所有图片资源
    image_resources = {}

    # 提取所有图片资源
    for item in book.get_items_of_type(ebooklib.ITEM_IMAGE):
        # 获取图片ID和内容
        image_id = item.get_id()
        image_content = item.get_content()
        image_type = item.media_type.split("/")[1]
        if image_type == "jpg":
            image_type = "jpeg"
        # 将图片内容转为base64
        base64_data = base64.b64encode(image_content).decode("utf-8")
        image_resources[image_id] = f"data:image/{image_type};base64,{base64_data}"

    for item in book.get_items_of_type(ebooklib.ITEM_DOCUMENT):
        content = item.get_content().decode("utf-8", errors="ignore")
        soup = BeautifulSoup(content, "html.parser")

        # 处理图片标签
        for img_tag in soup.find_all("img"):
            src = img_tag.get("src")
            if src:
                # EPUB中图片src通常使用相对路径或ID引用
                img_id = src.split("/")[-1]
                # 尝试查找匹配的图片资源
                for resource_id, resource_data in image_resources.items():
                    if img_id in resource_id or resource_id in img_id:
                        img_tag["src"] = resource_data
                        break
                else:
                    # 如果找不到图片资源
                    img_tag.replace_with(
                        soup.new_tag(
                            "div",
                            attrs={"class": "image-placeholder"},
                            string="图片未找到",
                        )
                    )

        # 删除a标签里的href
        for a_tag in soup.find_all("a"):
            a_tag.name = "span"
            if "href" in a_tag.attrs:
                del a_tag.attrs["href"]

        html_content += f"<section>{str(soup.body) if soup.body else ''}</section>"

    full_html = f"""
	<!DOCTYPE html>
	<html lang="en">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>EPUB Preview</title>
		<style>
			body {{ font-family: Arial, sans-serif; line-height: 1.6; padding: 1rem 5rem; background-color: #f2f2f2; }}
			img {{ max-width: 100%; height: auto; }}
			section {{ background-color: #fff; padding: 2rem 4rem; margin-top: 2rem; }}
			section:first-child {{ margin-top: 0; }}
			.image-placeholder {{ padding: 1rem; background-color: #eee; text-align: center; color: #666; }}
		</style>
	</head>
	<body>
		{html_content}
	</body>
	</html>
	"""

    # 删除临时文件
    os.remove(temp_file.name)

    return full_html


def json_dumps(d: dict, **kwargs) -> str:
    if not kwargs and orjson:
        try:
            return orjson.dumps(d).decode("utf-8")
        except Exception:
            pass
    return json.dumps(d, ensure_ascii=False, **kwargs)
