#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
页面元数据管理服务（异步版本）
"""
import logging
from typing import Any, Dict, List

from sqlalchemy import select, update, delete, func, and_
from sqlalchemy.ext.asyncio import AsyncSession

from online_dev.page_manager.model import PageMeta
from app.data_scope_utils import get_data_scope_filter, apply_data_scope_to_conditions

logger = logging.getLogger(__name__)

# 资源类型（用于数据权限配置）
RESOURCE_TYPE = "page"
RESOURCE_DISPLAY_NAME = "页面管理"


class PageServiceException(Exception):
    """页面服务异常"""
    pass


class PageService:
    """
    页面元数据管理服务

    数据权限：
    - 使用 list_with_data_scope() 自动应用数据权限
    - 支持本人、本部门、本部门及下级、全部等数据范围
    """

    # ============ 查询 ============

    @staticmethod
    async def list(
            db: AsyncSession,
            page: int = 1,
            page_size: int = 20,
            application_id: str = None,
            name: str = None,
            code: str = None,
            category: str = None,
            status: str = None
    ) -> Dict[str, Any]:
        """分页查询页面列表"""
        conditions = [PageMeta.is_deleted == False]

        # 应用过滤
        if application_id:
            conditions.append(PageMeta.application_id == application_id)
        else:
            # 如果没有指定 application_id，只返回主应用的页面（application_id 为 NULL）
            conditions.append(PageMeta.application_id.is_(None))

        if name:
            conditions.append(PageMeta.name.ilike(f"%{name}%"))
        if code:
            conditions.append(PageMeta.code.ilike(f"%{code}%"))
        if category:
            conditions.append(PageMeta.category == category)
        if status:
            conditions.append(PageMeta.status == status)

        # 获取总数
        count_stmt = select(func.count(PageMeta.id)).where(and_(*conditions))
        total_result = await db.execute(count_stmt)
        total = total_result.scalar() or 0

        # 获取列表
        offset = (page - 1) * page_size
        stmt = select(PageMeta).where(and_(*conditions)).order_by(
            PageMeta.sort, PageMeta.sys_create_datetime.desc()
        ).offset(offset).limit(page_size)

        result = await db.execute(stmt)
        items = list(result.scalars().all())

        return {
            "items": items,
            "total": total,
            "page": page,
            "page_size": page_size
        }

    @staticmethod
    async def list_with_data_scope(
            db: AsyncSession,
            page: int = 1,
            page_size: int = 20,
            application_id: str = None,
            name: str = None,
            code: str = None,
            category: str = None,
            status: str = None
    ) -> Dict[str, Any]:
        """
        分页查询页面列表（带数据权限过滤）

        自动从上下文获取当前用户信息，应用数据权限过滤
        """
        conditions = [PageMeta.is_deleted == False]

        # 应用过滤
        if application_id:
            conditions.append(PageMeta.application_id == application_id)
        else:
            conditions.append(PageMeta.application_id.is_(None))

        if name:
            conditions.append(PageMeta.name.ilike(f"%{name}%"))
        if code:
            conditions.append(PageMeta.code.ilike(f"%{code}%"))
        if category:
            conditions.append(PageMeta.category == category)
        if status:
            conditions.append(PageMeta.status == status)

        # 获取数据权限过滤条件并应用
        data_scope_filter = await get_data_scope_filter(db, RESOURCE_TYPE)
        scope_conditions = apply_data_scope_to_conditions(PageMeta, data_scope_filter)
        conditions.extend(scope_conditions)

        # 获取总数
        count_stmt = select(func.count(PageMeta.id)).where(and_(*conditions))
        total_result = await db.execute(count_stmt)
        total = total_result.scalar() or 0

        # 获取列表
        offset = (page - 1) * page_size
        stmt = select(PageMeta).where(and_(*conditions)).order_by(
            PageMeta.sort, PageMeta.sys_create_datetime.desc()
        ).offset(offset).limit(page_size)

        result = await db.execute(stmt)
        items = list(result.scalars().all())

        return {
            "items": items,
            "total": total,
            "page": page,
            "page_size": page_size
        }

    @staticmethod
    async def get(db: AsyncSession, page_id: str) -> PageMeta:
        """获取页面详情"""
        stmt = select(PageMeta).where(
            PageMeta.id == page_id,
            PageMeta.is_deleted == False
        )
        result = await db.execute(stmt)
        page = result.scalar_one_or_none()

        if not page:
            raise PageServiceException(f"页面不存在: {page_id}")

        return page

    @staticmethod
    async def get_by_code(db: AsyncSession, code: str) -> PageMeta:
        """根据编码获取页面"""
        stmt = select(PageMeta).where(
            PageMeta.code == code,
            PageMeta.is_deleted == False
        )
        result = await db.execute(stmt)
        page = result.scalar_one_or_none()

        if not page:
            raise PageServiceException(f"页面不存在: {code}")

        return page

    # ============ 创建 ============

    @staticmethod
    async def create(
            db: AsyncSession,
            data: Dict[str, Any],
            user_id: str = None
    ) -> PageMeta:
        """创建页面"""
        code = data.get("code")

        # 检查编码唯一性
        stmt = select(PageMeta).where(
            PageMeta.code == code,
            PageMeta.is_deleted == False
        )
        result = await db.execute(stmt)
        if result.scalar_one_or_none():
            raise PageServiceException(f"页面编码已存在: {code}")

        # 从上下文获取用户信息
        from utils.context import get_current_user_info_from_context
        user_info = get_current_user_info_from_context()

        page = PageMeta(
            application_id=data.get("application_id"),
            name=data.get("name"),
            code=code,
            category=data.get("category", ""),
            description=data.get("description", ""),
            page_config=data.get("page_config", {}),
            sort=data.get("sort", 0),
            sys_creator_id=user_id or (user_info.get('user_id') if user_info else None),
            sys_modifier_id=user_id or (user_info.get('user_id') if user_info else None),
        )

        # 自动填充部门ID
        if user_info and user_info.get('dept_id'):
            page.sys_dept_id = user_info.get('dept_id')

        db.add(page)
        await db.commit()
        await db.refresh(page)

        logger.info(f"页面创建成功: {page.code}")
        return page

    # ============ 更新 ============

    @staticmethod
    async def update(
            db: AsyncSession,
            page_id: str,
            data: Dict[str, Any],
            user_id: str = None
    ) -> PageMeta:
        """更新页面"""
        page = await PageService.get(db, page_id)

        # 更新基本字段
        if "name" in data and data["name"] is not None:
            page.name = data["name"]
        if "category" in data and data["category"] is not None:
            page.category = data["category"]
        if "description" in data and data["description"] is not None:
            page.description = data["description"]
        if "sort" in data and data["sort"] is not None:
            page.sort = data["sort"]
        if "page_config" in data and data["page_config"] is not None:
            page.page_config = data["page_config"]

        page.sys_modifier_id = user_id

        await db.commit()
        await db.refresh(page)

        logger.info(f"页面更新成功: {page.code}")
        return page

    # ============ 删除 ============

    @staticmethod
    async def _cleanup_page_publish_resources(db: AsyncSession, page: PageMeta) -> None:
        """清理页面发布产生的菜单（取消发布、删除时共用）"""
        from core.menu.model import Menu

        delete_menu_stmt = delete(Menu).where(
            Menu.path == f"/page-render/{page.code}"
        )
        result = await db.execute(delete_menu_stmt)
        if result.rowcount > 0:
            logger.info(
                "物理删除页面菜单: %s, 删除数量: %s",
                page.code,
                result.rowcount,
            )

    @staticmethod
    async def delete(db: AsyncSession, page_id: str) -> bool:
        """删除页面（软删除）"""
        from core.menu.service import MenuService

        page = await PageService.get(db, page_id)
        await PageService._cleanup_page_publish_resources(db, page)
        page.is_deleted = True
        page.status = "draft"
        await db.commit()
        await MenuService.invalidate_cache()
        logger.info("页面删除成功: %s", page.code)
        return True

    @staticmethod
    async def batch_delete(db: AsyncSession, page_ids: List[str]) -> int:
        """批量删除页面"""
        from core.menu.service import MenuService

        stmt = select(PageMeta).where(
            PageMeta.id.in_(page_ids),
            PageMeta.is_deleted == False,
        )
        pages = list((await db.execute(stmt)).scalars().all())
        for page in pages:
            await PageService._cleanup_page_publish_resources(db, page)

        update_stmt = update(PageMeta).where(
            PageMeta.id.in_(page_ids),
            PageMeta.is_deleted == False,
        ).values(is_deleted=True, status="draft")
        result = await db.execute(update_stmt)
        await db.commit()

        count = result.rowcount
        if count > 0:
            await MenuService.invalidate_cache()
        logger.info("批量删除页面成功: %s 个", count)
        return count

    # ============ 发布/取消发布 ============

    @staticmethod
    async def publish(
            db: AsyncSession,
            page_id: str,
            publish_config: Dict[str, Any] = None
    ) -> PageMeta:
        """发布页面并创建菜单"""
        from core.menu.model import Menu
        from core.menu.service import MenuService

        page = await PageService.get(db, page_id)

        if page.status == "published":
            raise PageServiceException("页面已发布")

        # 更新页面状态
        page.status = "published"
        page.version += 1

        # 创建或更新菜单
        if publish_config:
            menu_parent_id = publish_config.get("menu_parent_id")
            menu_path = f"/page-render/{page.code}"

            # 检查是否已存在该页面的菜单
            menu_stmt = select(Menu).where(
                Menu.path == menu_path
            )
            menu_result = await db.execute(menu_stmt)
            existing_menu = menu_result.scalar_one_or_none()

            if existing_menu:
                # 更新现有菜单
                existing_menu.name = publish_config.get("menu_name", page.name)
                existing_menu.title = publish_config.get("menu_name", page.name)
                existing_menu.parent_id = menu_parent_id
                existing_menu.icon = publish_config.get("menu_icon", "lucide:layout-dashboard")
                existing_menu.order = publish_config.get("menu_order", 0)
                existing_menu.type = "online_page"
                existing_menu.application_id = page.application_id
                logger.info(f"更新页面菜单: {page.code}")
            else:
                # 创建新菜单
                new_menu = Menu(
                    application_id=page.application_id,
                    name=publish_config.get("menu_name", page.name),
                    title=publish_config.get("menu_name", page.name),
                    path=menu_path,
                    component="online-dev/page-render/index",
                    type="online_page",
                    parent_id=menu_parent_id,
                    icon=publish_config.get("menu_icon", "lucide:layout-dashboard"),
                    order=publish_config.get("menu_order", 0),
                )
                db.add(new_menu)
                logger.info(f"创建页面菜单: {page.code}")

        await db.commit()
        await db.refresh(page)

        # 清空菜单缓存
        await MenuService.invalidate_cache()
        logger.info("已清空菜单缓存")

        logger.info(f"页面发布成功: {page.code}, version={page.version}")
        return page

    @staticmethod
    async def unpublish(db: AsyncSession, page_id: str) -> PageMeta:
        """取消发布页面并物理删除菜单"""
        from core.menu.service import MenuService

        page = await PageService.get(db, page_id)

        if page.status == "draft":
            raise PageServiceException("页面未发布")

        page.status = "draft"

        await PageService._cleanup_page_publish_resources(db, page)

        await db.commit()
        await db.refresh(page)

        # 清空菜单缓存
        await MenuService.invalidate_cache()
        logger.info("已清空菜单缓存")

        logger.info("页面取消发布: %s", page.code)
        return page

    # ============ 复制 ============

    @staticmethod
    async def copy(
            db: AsyncSession,
            page_id: str,
            new_code: str,
            new_name: str = None,
            user_id: str = None
    ) -> PageMeta:
        """复制页面"""
        source = await PageService.get(db, page_id)

        # 检查新编码唯一性
        stmt = select(PageMeta).where(
            PageMeta.code == new_code,
            PageMeta.is_deleted == False
        )
        result = await db.execute(stmt)
        if result.scalar_one_or_none():
            raise PageServiceException(f"页面编码已存在: {new_code}")

        new_page = PageMeta(
            application_id=source.application_id,
            name=new_name or f"{source.name}_副本",
            code=new_code,
            category=source.category,
            description=source.description,
            status="draft",
            version=1,
            page_config=source.page_config,
            sort=source.sort,
            sys_creator_id=user_id,
            sys_modifier_id=user_id,
        )
        db.add(new_page)
        await db.commit()
        await db.refresh(new_page)

        logger.info(f"页面复制成功: {source.code} -> {new_code}")
        return new_page

    # ============ 导入/导出 ============

    @staticmethod
    async def export_config(db: AsyncSession, page_id: str) -> Dict[str, Any]:
        """导出页面配置"""
        page = await PageService.get(db, page_id)

        return {
            "name": page.name,
            "code": page.code,
            "category": page.category,
            "description": page.description,
            "page_config": page.page_config,
        }

    @staticmethod
    async def check_import(db: AsyncSession, code: str) -> Dict[str, Any]:
        """导入预检查：编码是否冲突"""
        code_exists = False
        if code:
            stmt = select(PageMeta).where(
                PageMeta.code == code,
                PageMeta.is_deleted == False,
            )
            result = await db.execute(stmt)
            code_exists = result.scalar_one_or_none() is not None

        return {
            "code_exists": code_exists,
            "can_import": not code_exists,
        }

    @staticmethod
    async def import_config(
            db: AsyncSession,
            data: Dict[str, Any],
            user_id: str = None
    ) -> PageMeta:
        """导入页面配置"""
        required_fields = ["name", "code"]
        for field in required_fields:
            if not data.get(field):
                raise PageServiceException(f"缺少必要字段: {field}")

        return await PageService.create(db, data, user_id)

    # ============ 获取分类列表 ============

    @staticmethod
    async def get_categories(db: AsyncSession) -> List[str]:
        """获取所有分类"""
        stmt = select(PageMeta.category).where(
            PageMeta.is_deleted == False,
            PageMeta.category != ""
        ).distinct()

        result = await db.execute(stmt)
        return [row[0] for row in result.fetchall()]
