import json
from typing import Optional
from .cache import get_redis
from .config import settings
from .database import get_pool, get_system_pool, _has_split_databases
import aiomysql

REGION_PREFIX = "bff:region:"
REGION_TREE_CACHE_KEY = "bff:region:tree"
REGION_TREE_CACHE_TTL = 3600

DEFAULT_REGION_TREE = {
    "id": "root",
    "name": "中国",
    "path": "/中国",
    "level": "country",
    "children": [
        {
            "id": "fujian",
            "name": "福建省",
            "path": "/中国/福建省",
            "level": "province",
            "children": [
                {"id": "fuzhou", "name": "福州市", "path": "/中国/福建省/福州市", "level": "city", "children": []},
                {"id": "xiamen", "name": "厦门市", "path": "/中国/福建省/厦门市", "level": "city", "children": []},
            ]
        },
        {
            "id": "xinjiang",
            "name": "新疆维吾尔自治区",
            "path": "/中国/新疆维吾尔自治区",
            "level": "province",
            "children": [
                {"id": "urumqi", "name": "乌鲁木齐市", "path": "/中国/新疆维吾尔自治区/乌鲁木齐市", "level": "city", "children": []},
            ]
        }
    ]
}


async def get_region_tree() -> dict:
    r = await get_redis()
    cached = await r.get(REGION_TREE_CACHE_KEY)
    if cached:
        return json.loads(cached)

    try:
        pool = await get_system_pool() if _has_split_databases() else await get_pool()
        async with pool.acquire() as conn:
            async with conn.cursor(aiomysql.DictCursor) as cur:
                await cur.execute(
                    "SELECT config_value FROM system_configs WHERE config_key = 'region.tree'"
                )
                row = await cur.fetchone()
                if row:
                    tree = json.loads(row["config_value"])
                    try:
                        await r.setex(REGION_TREE_CACHE_KEY, REGION_TREE_CACHE_TTL, json.dumps(tree, ensure_ascii=False))
                    except Exception:
                        pass
                    return tree
    except Exception:
        pass

    return DEFAULT_REGION_TREE


async def resolve_visible_regions(user_region_path: Optional[str]) -> list:
    if not user_region_path:
        return ["/中国"]

    tree = await get_region_tree()
    visible = ["/中国"]

    if user_region_path != "/中国":
        visible.append(user_region_path)
        path_parts = user_region_path.strip("/").split("/")
        current_path = ""
        for part in path_parts[:-1]:
            current_path += f"/{part}"
            if current_path not in visible:
                visible.append(current_path)

    return visible


async def get_user_region(user_id: str) -> Optional[dict]:
    r = await get_redis()
    cache_key = f"{REGION_PREFIX}user:{user_id}"
    cached = await r.get(cache_key)
    if cached:
        return json.loads(cached)

    row = None
    try:
        pool = await get_pool()
        async with pool.acquire() as conn:
            async with conn.cursor(aiomysql.DictCursor) as cur:
                await cur.execute(
                    "SELECT enforcement_region, enforcement_region_path FROM users WHERE id = %s",
                    (int(user_id),)
                )
                row = await cur.fetchone()
    except Exception:
        pass

    if not row or not dict(row).get("enforcement_region_path"):
        result = {"region": None, "region_path": None, "level": "country"}
    else:
        path = row["enforcement_region_path"]
        level = "country"
        if path:
            depth = len(path.strip("/").split("/"))
            if depth == 1:
                level = "country"
            elif depth == 2:
                level = "province"
            elif depth == 3:
                level = "city"
            elif depth >= 4:
                level = "district"
        result = {
            "region": row["enforcement_region"],
            "region_path": path,
            "level": level,
        }

    try:
        await r.setex(cache_key, 600, json.dumps(result, ensure_ascii=False))
    except Exception:
        pass

    return result


async def set_user_region(user_id: str, region: str, region_path: str) -> bool:
    try:
        pool = await get_pool()
        async with pool.acquire() as conn:
            async with conn.cursor() as cur:
                await cur.execute(
                    "UPDATE users SET enforcement_region = %s, enforcement_region_path = %s WHERE id = %s",
                    (region, region_path, int(user_id))
                )
                await conn.commit()
    except Exception:
        return False

    r = await get_redis()
    cache_key = f"{REGION_PREFIX}user:{user_id}"
    try:
        await r.delete(cache_key)
    except Exception:
        pass

    return True


async def inject_region_to_ai_params(user_id: str, params: dict) -> dict:
    user_region = await get_user_region(user_id)
    if user_region and user_region.get("region_path"):
        visible_regions = await resolve_visible_regions(user_region["region_path"])
        params["region_path"] = user_region["region_path"]
        params["region_level"] = user_region.get("level", "country")
        params["visible_regions"] = visible_regions
    else:
        params["region_level"] = "country"
        params["visible_regions"] = ["/中国"]

    return params


async def update_region_tree(tree: dict) -> bool:
    try:
        pool = await get_system_pool() if _has_split_databases() else await get_pool()
        async with pool.acquire() as conn:
            async with conn.cursor() as cur:
                await cur.execute(
                    "INSERT INTO system_configs (config_key, config_value, config_type, category, description) VALUES ('region.tree', %s, 'json', 'region', '区域树形结构') ON DUPLICATE KEY UPDATE config_value = %s",
                    (json.dumps(tree, ensure_ascii=False), json.dumps(tree, ensure_ascii=False))
                )
                await conn.commit()
    except Exception:
        return False

    r = await get_redis()
    try:
        await r.setex(REGION_TREE_CACHE_KEY, REGION_TREE_CACHE_TTL, json.dumps(tree, ensure_ascii=False))
    except Exception:
        pass

    return True


def find_node_in_tree(tree: dict, path: str) -> Optional[dict]:
    if tree.get("path") == path:
        return tree
    for child in tree.get("children", []):
        result = find_node_in_tree(child, path)
        if result:
            return result
    return None
