from __future__ import annotations

import mimetypes
from pathlib import Path
from typing import Any

from fastapi import UploadFile
from sqlalchemy import bindparam, text

from fastapi_common.fastapi_common_sqlalchemy.database import GetAsyncSession
from fastapi_common.fastapi_common_storage.oss_path_utils import OssPathUtils
from fastapi_common.fastapi_common_web.domain.responses import StatusCodeEnum
from fastapi_common.fastapi_common_web.exception.LeauditException import LeauditException
from fastapi_modules.fastapi_leaudit.domian.Dto.contractTemplateDto import (
    ContractTemplateCreateDTO,
    ContractTemplateListQueryDTO,
    ContractTemplateSearchQueryDTO,
)
from fastapi_modules.fastapi_leaudit.domian.vo.contractTemplateVo import (
    ContractTemplateCategoryVO,
    ContractTemplateCreateVO,
    ContractTemplateDetailVO,
    ContractTemplateListItemVO,
    ContractTemplatePageVO,
    ContractTemplateSearchCategoryVO,
    ContractTemplateSearchResultVO,
)
from fastapi_modules.fastapi_leaudit.services.contractTemplateService import IContractTemplateService
from fastapi_modules.fastapi_leaudit.services.impl.ssoUserCompat import SsoUserCompat
from fastapi_modules.fastapi_leaudit.services.impl.tenantResolver import TenantResolver
from fastapi_modules.fastapi_leaudit.services.ossService import IOssService
from fastapi_modules.fastapi_leaudit.services.impl.ossServiceImpl import OssServiceImpl

_ALLOWED_SORT_FIELDS = {
    "id": "t.id",
    "title": "t.title",
    "created_at": "t.created_at",
    "updated_at": "t.updated_at",
}


class ContractTemplateServiceImpl(IContractTemplateService):
    """合同模板服务实现。"""

    def __init__(self, OssService: IOssService | None = None, TenantResolverService: TenantResolver | None = None) -> None:
        self.OssService = OssService or OssServiceImpl()
        self.TenantResolver = TenantResolverService or TenantResolver()

    async def ListCategories(self, IncludeDisabled: bool, WithTemplateCount: bool) -> list[ContractTemplateCategoryVO]:
        count_select = "COUNT(t.id)::int AS template_count" if WithTemplateCount else "0::int AS template_count"
        filters = ["c.deleted_at IS NULL"]
        if not IncludeDisabled:
            filters.append("1=1")
        sql = text(
            f"""
            SELECT
                c.id,
                c.name,
                c.icon,
                c.description,
                COALESCE(c.sort_order, 0) AS sort_order,
                {count_select},
                TRUE AS is_enabled
            FROM contract_categories c
            LEFT JOIN contract_templates t
                ON t.category_id = c.id
               AND t.deleted_at IS NULL
            WHERE {' AND '.join(filters)}
            GROUP BY c.id, c.name, c.icon, c.description, c.sort_order
            ORDER BY COALESCE(c.sort_order, 0) ASC, c.name ASC
            """
        )

        async with GetAsyncSession() as session:
            await self._ensureContractTemplateSchema(session)
            rows = (await session.execute(sql)).mappings().all()

        return [self._to_category_vo(row) for row in rows]

    async def ListTemplates(self, Query: ContractTemplateListQueryDTO, CurrentUserId: int) -> ContractTemplatePageVO:
        async with GetAsyncSession() as session:
            await self._ensureContractTemplateSchema(session)
            currentUser = await self._getCurrentUserContext(CurrentUserId, session)
            where_clause, params, needs_category_name_filter = self._build_template_filters(
                keyword=Query.keyword,
                category_id=Query.category_id,
                category_name=Query.category_name,
                region=Query.region,
                tenant_code=Query.tenant_code,
                file_format=Query.file_format,
                is_featured=Query.is_featured,
                currentUser=currentUser,
            )
            order_sql = self._build_order_clause(Query.sort_by, Query.sort_order, default_field="updated_at", default_order="desc")
            offset = max(Query.page - 1, 0) * Query.page_size
            params.update({"limit": Query.page_size, "offset": offset})

            from_sql = self._build_template_from_sql(needs_category_name_filter)

            count_sql = text(
                f"""
                SELECT COUNT(*)
                {from_sql}
                WHERE {where_clause}
                """
            )
            list_sql = text(
                f"""
                SELECT
                    t.id,
                    t.template_code,
                    t.title,
                    t.category_id,
                    c.name AS category_name,
                    c.icon AS category_icon,
                    c.description AS category_description,
                    t.region,
                    COALESCE(NULLIF(BTRIM(t.tenant_code), ''), NULL) AS tenant_code,
                    CASE
                        WHEN t.tenant_name IS NOT NULL AND BTRIM(t.tenant_name) <> '' THEN t.tenant_name
                        WHEN t.region IS NOT NULL AND BTRIM(t.region) <> '' THEN t.region
                        WHEN NULLIF(BTRIM(t.tenant_code), '') = 'PUBLIC' THEN '公共'
                        WHEN NULLIF(BTRIM(t.tenant_code), '') = 'PROVINCIAL' THEN '省级'
                        ELSE NULL
                    END AS tenant_name,
                    t.description,
                    t.file_path,
                    t.pdf_file_path,
                    t.file_format,
                    t.original_file_name,
                    t.mime_type,
                    COALESCE(t.file_size, 0) AS file_size,
                    t.pdf_file_size,
                    COALESCE(t.is_featured, FALSE) AS is_featured,
                    t.created_by,
                    t.updated_by,
                    t.created_at,
                    t.updated_at
                {from_sql}
                WHERE {where_clause}
                ORDER BY {order_sql}
                LIMIT :limit OFFSET :offset
                """
            )
            count_sql, list_sql = self._bind_expanding(count_sql, list_sql, params)
            total = int((await session.execute(count_sql, params)).scalar_one())
            rows = (await session.execute(list_sql, params)).mappings().all()

        return ContractTemplatePageVO(
            total=total,
            page=Query.page,
            page_size=Query.page_size,
            total_pages=max((total + Query.page_size - 1) // Query.page_size, 1) if total else 0,
            templates=[self._to_list_item_vo(row) for row in rows],
        )

    async def SearchTemplates(self, Query: ContractTemplateSearchQueryDTO, CurrentUserId: int) -> ContractTemplateSearchResultVO:
        list_query = ContractTemplateListQueryDTO(
            keyword=Query.q,
            category_id=Query.category_id,
            category_name=Query.category_name,
            region=Query.region,
            tenant_code=Query.tenant_code,
            page=Query.page,
            page_size=Query.page_size,
            sort_by=Query.sort_by,
            sort_order=Query.sort_order,
        )
        page_result = await self.ListTemplates(list_query, CurrentUserId)
        category_stats = await self._load_search_category_stats(Query.q, Query.region, Query.tenant_code, CurrentUserId)

        return ContractTemplateSearchResultVO(
            total=page_result.total,
            page=page_result.page,
            page_size=page_result.page_size,
            total_pages=page_result.total_pages,
            templates=page_result.templates,
            category_stats=category_stats,
        )

    async def GetTemplateDetail(self, TemplateId: int, CurrentUserId: int) -> ContractTemplateDetailVO | None:
        async with GetAsyncSession() as session:
            await self._ensureContractTemplateSchema(session)
            currentUser = await self._getCurrentUserContext(CurrentUserId, session)
            params: dict[str, Any] = {"template_id": TemplateId}
            scope_filters = self._build_template_scope_filters(currentUser, params, requestedRegion=None)
            sql = text(
                f"""
                SELECT
                    t.id,
                    t.template_code,
                    t.title,
                    t.category_id,
                    c.name AS category_name,
                    c.icon AS category_icon,
                    c.description AS category_description,
                    t.region,
                    COALESCE(NULLIF(BTRIM(t.tenant_code), ''), NULL) AS tenant_code,
                    CASE
                        WHEN t.tenant_name IS NOT NULL AND BTRIM(t.tenant_name) <> '' THEN t.tenant_name
                        WHEN t.region IS NOT NULL AND BTRIM(t.region) <> '' THEN t.region
                        WHEN NULLIF(BTRIM(t.tenant_code), '') = 'PUBLIC' THEN '公共'
                        WHEN NULLIF(BTRIM(t.tenant_code), '') = 'PROVINCIAL' THEN '省级'
                        ELSE NULL
                    END AS tenant_name,
                    t.description,
                    t.file_path,
                    t.pdf_file_path,
                    t.file_format,
                    t.original_file_name,
                    t.mime_type,
                    COALESCE(t.file_size, 0) AS file_size,
                    t.pdf_file_size,
                    COALESCE(t.is_featured, FALSE) AS is_featured,
                    t.created_by,
                    t.updated_by,
                    t.created_at,
                    t.updated_at
                FROM contract_templates t
                LEFT JOIN contract_categories c ON c.id = t.category_id
                WHERE t.id = :template_id
                  AND t.deleted_at IS NULL
                  AND c.deleted_at IS NULL
                  AND {' AND '.join(scope_filters)}
                LIMIT 1
                """
            )
            row = (await session.execute(sql, params)).mappings().first()

        if not row:
            return None
        return self._to_detail_vo(row)

    async def CreateTemplate(
        self,
        Body: ContractTemplateCreateDTO,
        File: UploadFile,
        PdfFile: UploadFile | None,
        CurrentUserId: int,
    ) -> ContractTemplateCreateVO:
        if File is None or not File.filename:
            raise LeauditException(StatusCodeEnum.HTTP_400_BAD_REQUEST, "模板主文件不能为空")

        fileContent = await File.read()
        if not fileContent:
            raise LeauditException(StatusCodeEnum.HTTP_400_BAD_REQUEST, "模板主文件内容不能为空")

        normalizedCode = (Body.template_code or "").strip()
        normalizedTitle = (Body.title or "").strip()
        if not normalizedCode:
            raise LeauditException(StatusCodeEnum.HTTP_400_BAD_REQUEST, "模板编码不能为空")
        if not normalizedTitle:
            raise LeauditException(StatusCodeEnum.HTTP_400_BAD_REQUEST, "模板标题不能为空")

        fileExt = Path(File.filename).suffix.lstrip(".").lower()
        if fileExt not in {"doc", "docx", "pdf"}:
            raise LeauditException(StatusCodeEnum.HTTP_400_BAD_REQUEST, "当前仅支持上传 DOC、DOCX、PDF 模板")
        mimeType = File.content_type or mimetypes.guess_type(File.filename)[0] or "application/octet-stream"

        pdfContent: bytes | None = None
        pdfMimeType: str | None = None
        pdfFileName: str | None = None
        if PdfFile and PdfFile.filename:
            pdfContent = await PdfFile.read()
            if not pdfContent:
                raise LeauditException(StatusCodeEnum.HTTP_400_BAD_REQUEST, "预览 PDF 文件内容不能为空")
            pdfExt = Path(PdfFile.filename).suffix.lstrip(".").lower()
            if pdfExt != "pdf":
                raise LeauditException(StatusCodeEnum.HTTP_400_BAD_REQUEST, "预览文件仅支持 PDF")
            pdfMimeType = PdfFile.content_type or "application/pdf"
            pdfFileName = PdfFile.filename

        async with GetAsyncSession() as session:
            await self._ensureContractTemplateSchema(session)
            currentUser = await self._getCurrentUserContext(CurrentUserId, session)
            resolvedTenantCode, resolvedTenantName, resolvedRegion = self._resolve_upload_scope(currentUser, Body.region, Body.tenant_code)
            categoryRow = (
                await session.execute(
                    text(
                        """
                        SELECT id, name
                        FROM contract_categories
                        WHERE id = :category_id
                          AND deleted_at IS NULL
                        LIMIT 1
                        """
                    ),
                    {"category_id": Body.category_id},
                )
            ).mappings().first()
            if not categoryRow:
                raise LeauditException(StatusCodeEnum.HTTP_404_NOT_FOUND, "合同模板分类不存在")

            duplicateRow = (
                await session.execute(
                    text(
                        """
                        SELECT id
                        FROM contract_templates
                        WHERE (
                                tenant_code = :tenant_code
                                OR (
                                    (tenant_code IS NULL OR BTRIM(tenant_code) = '')
                                    AND region = :region
                                )
                              )
                          AND template_code = :template_code
                          AND deleted_at IS NULL
                        LIMIT 1
                        """
                    ),
                    {"tenant_code": resolvedTenantCode, "region": resolvedRegion, "template_code": normalizedCode},
                )
            ).mappings().first()
            if duplicateRow:
                raise LeauditException(StatusCodeEnum.HTTP_409_CONFLICT, f"当前租户已存在模板编码 {normalizedCode}")

            categoryName = str(categoryRow["name"] or "未分类")
            objectKey = OssPathUtils.BuildContractTemplateKey(
                Region=resolvedRegion,
                CategoryName=categoryName,
                TemplateCode=normalizedCode,
                FileRole="source",
                FileName=File.filename,
            )
            filePath = await self.OssService.UploadBytes(
                ObjectKey=objectKey,
                Content=fileContent,
                ContentType=mimeType,
            )

            pdfPath: str | None = None
            if pdfContent is not None and pdfFileName:
                pdfObjectKey = OssPathUtils.BuildContractTemplateKey(
                    Region=resolvedRegion,
                    CategoryName=categoryName,
                    TemplateCode=normalizedCode,
                    FileRole="preview",
                    FileName=pdfFileName,
                )
                pdfPath = await self.OssService.UploadBytes(
                    ObjectKey=pdfObjectKey,
                    Content=pdfContent,
                    ContentType=pdfMimeType or "application/pdf",
                )

            createdRow = (
                await session.execute(
                    text(
                        """
                        INSERT INTO contract_templates (
                            template_code,
                            title,
                            category_id,
                            tenant_code,
                            tenant_name,
                            region,
                            description,
                            file_path,
                            pdf_file_path,
                            file_format,
                            original_file_name,
                            mime_type,
                            file_size,
                            pdf_file_size,
                            is_featured,
                            created_by,
                            updated_by,
                            created_at,
                            updated_at
                        ) VALUES (
                            :template_code,
                            :title,
                            :category_id,
                            :tenant_code,
                            :tenant_name,
                            :region,
                            :description,
                            :file_path,
                            :pdf_file_path,
                            :file_format,
                            :original_file_name,
                            :mime_type,
                            :file_size,
                            :pdf_file_size,
                            :is_featured,
                            :created_by,
                            :updated_by,
                            NOW(),
                            NOW()
                        )
                        RETURNING id
                        """
                    ),
                    {
                        "template_code": normalizedCode,
                        "title": normalizedTitle,
                        "category_id": Body.category_id,
                        "tenant_code": resolvedTenantCode,
                        "tenant_name": resolvedTenantName,
                        "region": resolvedRegion,
                        "description": (Body.description or "").strip() or None,
                        "file_path": filePath,
                        "pdf_file_path": pdfPath,
                        "file_format": fileExt,
                        "original_file_name": File.filename,
                        "mime_type": mimeType,
                        "file_size": len(fileContent),
                        "pdf_file_size": len(pdfContent) if pdfContent is not None else None,
                        "is_featured": Body.is_featured,
                        "created_by": CurrentUserId,
                        "updated_by": CurrentUserId,
                    },
                )
            ).mappings().first()
            await session.commit()

        if not createdRow:
            raise LeauditException(StatusCodeEnum.HTTP_500_INTERNAL_SERVER_ERROR, "合同模板创建失败")

        detail = await self.GetTemplateDetail(int(createdRow["id"]), CurrentUserId)
        if not detail:
            raise LeauditException(StatusCodeEnum.HTTP_500_INTERNAL_SERVER_ERROR, "合同模板创建成功但详情读取失败")
        return ContractTemplateCreateVO(**detail.model_dump())

    async def DeleteTemplate(self, TemplateId: int, CurrentUserId: int) -> None:
        async with GetAsyncSession() as session:
            await self._ensureContractTemplateSchema(session)
            currentUser = await self._getCurrentUserContext(CurrentUserId, session)
            params: dict[str, Any] = {"template_id": TemplateId}
            scope_filters = self._build_template_scope_filters(currentUser, params, requestedRegion=None, writable=True)
            row = (
                await session.execute(
                    text(
                        f"""
                        SELECT id
                        FROM contract_templates t
                        WHERE t.id = :template_id
                          AND t.deleted_at IS NULL
                          AND {' AND '.join(scope_filters)}
                        LIMIT 1
                        """
                    ),
                    params,
                )
            ).mappings().first()
            if not row:
                raise LeauditException(StatusCodeEnum.HTTP_404_NOT_FOUND, "合同模板不存在或无权删除")

            await session.execute(
                text(
                    """
                    UPDATE contract_templates
                    SET deleted_at = NOW(),
                        updated_at = NOW(),
                        updated_by = :updated_by
                    WHERE id = :template_id
                      AND deleted_at IS NULL
                    """
                ),
                {"template_id": TemplateId, "updated_by": CurrentUserId},
            )
            await session.commit()

    async def _load_search_category_stats(
        self,
        keyword: str,
        requestedRegion: str | None,
        requestedTenantCode: str | None,
        CurrentUserId: int,
    ) -> list[ContractTemplateSearchCategoryVO]:
        clean_keyword = (keyword or "").strip()
        if not clean_keyword:
            return []

        async with GetAsyncSession() as session:
            await self._ensureContractTemplateSchema(session)
            currentUser = await self._getCurrentUserContext(CurrentUserId, session)
            params: dict[str, Any] = {"keyword": f"%{clean_keyword}%"}
            scope_filters = self._build_template_scope_filters(
                currentUser,
                params,
                requestedRegion=requestedRegion,
                requestedTenantCode=requestedTenantCode,
            )
            filters = [
                "c.deleted_at IS NULL",
                "t.deleted_at IS NULL",
                "("
                "t.title ILIKE :keyword "
                "OR COALESCE(t.description, '') ILIKE :keyword "
                "OR COALESCE(t.template_code, '') ILIKE :keyword "
                "OR COALESCE(c.name, '') ILIKE :keyword"
                ")",
                *scope_filters,
            ]

            sql = text(
                f"""
                SELECT
                    c.id,
                    c.name,
                    COUNT(t.id)::int AS search_count
                FROM contract_categories c
                LEFT JOIN contract_templates t ON t.category_id = c.id
                WHERE {' AND '.join(filters)}
                GROUP BY c.id, c.name
                ORDER BY c.name ASC
                """
            )
            (sql,) = self._bind_expanding(sql, params)
            rows = (await session.execute(sql, params)).mappings().all()

        return [
            ContractTemplateSearchCategoryVO(
                id=int(row["id"]),
                name=str(row["name"] or ""),
                search_count=int(row["search_count"] or 0),
            )
            for row in rows
            if row.get("id") is not None
        ]

    def _build_template_filters(
        self,
        keyword: str | None,
        category_id: int | None,
        category_name: str | None,
        region: str | None,
        tenant_code: str | None,
        file_format: str | None,
        is_featured: bool | None,
        currentUser: dict[str, Any],
    ) -> tuple[str, dict[str, Any], bool]:
        filters = ["t.deleted_at IS NULL", "c.deleted_at IS NULL"]
        params: dict[str, Any] = {}
        needs_category_name_filter = False

        filters.extend(self._build_template_scope_filters(currentUser, params, region, tenant_code))

        if category_id is not None:
            filters.append("t.category_id = :category_id")
            params["category_id"] = category_id
        elif category_name:
            filters.append("c.name = :category_name")
            params["category_name"] = category_name.strip()
            needs_category_name_filter = True

        if file_format:
            filters.append("LOWER(t.file_format) = :file_format")
            params["file_format"] = file_format.strip().lower()

        if is_featured is not None:
            filters.append("COALESCE(t.is_featured, FALSE) = :is_featured")
            params["is_featured"] = is_featured

        clean_keyword = (keyword or "").strip()
        if clean_keyword:
            filters.append(
                "("
                "t.title ILIKE :keyword "
                "OR COALESCE(t.description, '') ILIKE :keyword "
                "OR COALESCE(t.template_code, '') ILIKE :keyword "
                "OR COALESCE(c.name, '') ILIKE :keyword"
                ")"
            )
            params["keyword"] = f"%{clean_keyword}%"
            needs_category_name_filter = True

        return " AND ".join(filters), params, needs_category_name_filter

    def _build_template_from_sql(self, needs_category_name_filter: bool) -> str:
        _ = needs_category_name_filter
        return """
            FROM contract_templates t
            LEFT JOIN contract_categories c ON c.id = t.category_id
        """

    def _build_order_clause(self, sort_by: str | None, sort_order: str | None, default_field: str, default_order: str) -> str:
        field = _ALLOWED_SORT_FIELDS.get(str(sort_by or "").strip().lower(), _ALLOWED_SORT_FIELDS[default_field])
        direction = "DESC" if str(sort_order or default_order).strip().lower() == "desc" else "ASC"
        return f"{field} {direction}, t.id ASC"

    def _bind_expanding(self, *sql_objects_and_params: Any):
        sql_objects = list(sql_objects_and_params[:-1])
        params = sql_objects_and_params[-1]
        if "visible_tenant_codes" in params:
            sql_objects = [sql.bindparams(bindparam("visible_tenant_codes", expanding=True)) for sql in sql_objects]
        if "visible_regions" in params:
            sql_objects = [sql.bindparams(bindparam("visible_regions", expanding=True)) for sql in sql_objects]
        return tuple(sql_objects)

    def _to_category_vo(self, row: Any) -> ContractTemplateCategoryVO:
        return ContractTemplateCategoryVO(
            id=int(row["id"]),
            name=str(row["name"] or ""),
            icon=row.get("icon"),
            description=row.get("description"),
            sort_order=int(row.get("sort_order") or 0),
            template_count=int(row.get("template_count") or 0),
            is_enabled=bool(row.get("is_enabled", True)),
        )

    def _to_list_item_vo(self, row: Any) -> ContractTemplateListItemVO:
        tenant_name = row.get("tenant_name") or row.get("region") or "省级"
        return ContractTemplateListItemVO(
            id=int(row["id"]),
            template_code=str(row.get("template_code") or ""),
            title=str(row.get("title") or ""),
            category_id=int(row.get("category_id") or 0),
            category_name=row.get("category_name"),
            category_icon=row.get("category_icon"),
            description=row.get("description"),
            region=str(row.get("region") or tenant_name),
            tenant_code=row.get("tenant_code"),
            tenant_name=tenant_name,
            file_path=row.get("file_path"),
            pdf_file_path=row.get("pdf_file_path"),
            file_format=str(row.get("file_format") or ""),
            original_file_name=row.get("original_file_name"),
            mime_type=row.get("mime_type"),
            file_size=int(row.get("file_size") or 0),
            pdf_file_size=int(row["pdf_file_size"]) if row.get("pdf_file_size") is not None else None,
            is_featured=bool(row.get("is_featured", False)),
            created_by=int(row["created_by"]) if row.get("created_by") is not None else None,
            updated_by=int(row["updated_by"]) if row.get("updated_by") is not None else None,
            created_at=self._stringify_time(row.get("created_at")),
            updated_at=self._stringify_time(row.get("updated_at")),
        )

    def _to_detail_vo(self, row: Any) -> ContractTemplateDetailVO:
        base = self._to_list_item_vo(row)
        return ContractTemplateDetailVO(
            **base.model_dump(),
            category_description=row.get("category_description"),
            placeholder_schema=None,
        )

    def _stringify_time(self, value: Any) -> str | None:
        if value is None:
            return None
        return str(value)

    def _build_template_scope_filters(
        self,
        currentUser: dict[str, Any],
        params: dict[str, Any],
        requestedRegion: str | None,
        requestedTenantCode: str | None = None,
        writable: bool = False,
    ) -> list[str]:
        requested_tenant_code, requested_region = self._normalize_scope_value(requestedRegion, requestedTenantCode, currentUser)
        current_tenant_code = str(currentUser.get("tenant_code") or "").strip()
        current_region = str(currentUser["tenant_scope_value"] or currentUser["area"] or "").strip()

        if currentUser["is_global"]:
            if requested_tenant_code:
                return self._tenant_filter_sql(
                    params,
                    tenant_code=requested_tenant_code,
                    region=requested_region,
                    prefix="requested",
                )
            if requested_region:
                if requested_region in {"省级", "公共"}:
                    return self._tenant_filter_sql(
                        params,
                        tenant_code="PROVINCIAL" if requested_region == "省级" else "PUBLIC",
                        region=requested_region,
                        prefix="requested",
                    )
                params["requested_region"] = requested_region
                return ["t.region = :requested_region"]
            return ["1=1"]

        if writable:
            if not current_tenant_code and not current_region:
                return ["1=0"]
            if requested_tenant_code and current_tenant_code and requested_tenant_code != current_tenant_code:
                return ["1=0"]
            if requested_tenant_code and not current_tenant_code:
                return ["1=0"]
            if requested_region and requested_region != current_region:
                return ["1=0"]
            return self._tenant_filter_sql(
                params,
                tenant_code=current_tenant_code or requested_tenant_code,
                region=current_region or requested_region,
                prefix="scope",
            )

        if currentUser["can_manage"]:
            if not current_tenant_code and not current_region:
                return ["1=0"]
            if requested_tenant_code:
                if requested_tenant_code in {"PUBLIC", "PROVINCIAL"}:
                    return self._tenant_filter_sql(
                        params,
                        tenant_code=requested_tenant_code,
                        region=requested_region,
                        prefix="requested",
                    )
                if current_tenant_code and requested_tenant_code != current_tenant_code:
                    return ["1=0"]
                if not current_tenant_code and requested_region != current_region:
                    return ["1=0"]
                return self._tenant_filter_sql(
                    params,
                    tenant_code=requested_tenant_code,
                    region=requested_region,
                    prefix="requested",
                )
            if requested_region:
                if requested_region in {"省级", "公共"}:
                    return self._tenant_filter_sql(
                        params,
                        tenant_code="PROVINCIAL" if requested_region == "省级" else "PUBLIC",
                        region=requested_region,
                        prefix="requested",
                    )
                if requested_region != current_region:
                    return ["1=0"]
                return self._tenant_filter_sql(
                    params,
                    tenant_code=current_tenant_code or None,
                    region=current_region or requested_region,
                    prefix="requested",
                )
            return self._visible_tenant_filters(
                params,
                tenant_codes=["PROVINCIAL", "PUBLIC", current_tenant_code],
                legacy_regions=["省级", "公共", current_region],
            )

        if requested_tenant_code:
            if requested_tenant_code in {"PUBLIC", "PROVINCIAL"}:
                return self._tenant_filter_sql(
                    params,
                    tenant_code=requested_tenant_code,
                    region=requested_region,
                    prefix="requested",
                )
            if current_tenant_code and requested_tenant_code == current_tenant_code:
                return self._tenant_filter_sql(
                    params,
                    tenant_code=requested_tenant_code,
                    region=requested_region,
                    prefix="requested",
                )
            if not current_tenant_code and current_region and requested_region == current_region:
                return self._tenant_filter_sql(
                    params,
                    tenant_code=requested_tenant_code,
                    region=requested_region,
                    prefix="requested",
                )
            return ["1=0"]

        if requested_region:
            if requested_region in {"省级", "公共"}:
                return self._tenant_filter_sql(
                    params,
                    tenant_code="PROVINCIAL" if requested_region == "省级" else "PUBLIC",
                    region=requested_region,
                    prefix="requested",
                )
            if current_region and requested_region == current_region:
                return self._tenant_filter_sql(
                    params,
                    tenant_code=current_tenant_code or None,
                    region=current_region or requested_region,
                    prefix="requested",
                )
            return ["1=0"]

        if current_tenant_code or current_region:
            return self._visible_tenant_filters(
                params,
                tenant_codes=["PROVINCIAL", "PUBLIC", current_tenant_code],
                legacy_regions=["省级", "公共", current_region],
            )
        return self._tenant_filter_sql(
            params,
            tenant_code="PROVINCIAL",
            region="省级",
            prefix="requested",
        )

    async def _getCurrentUserContext(self, CurrentUserId: int, session=None) -> dict[str, Any]:
        own_session = False
        if session is None:
            own_session = True
            session_cm = GetAsyncSession()
            session = await session_cm.__aenter__()
        try:
            sso_user_columns = await SsoUserCompat.get_columns(session)
            tenant_code_select = SsoUserCompat.optional_coalesce_as(
                sso_user_columns,
                alias="u",
                column="tenant_code",
                fallback_sql="''",
            )
            tenant_name_select = SsoUserCompat.optional_coalesce_as(
                sso_user_columns,
                alias="u",
                column="tenant_name",
                fallback_sql="''",
            )
            row = (
                await session.execute(
                    text(
                        f"""
                        SELECT
                            u.id,
                            COALESCE(u.area, '') AS area,
                            {tenant_code_select},
                            {tenant_name_select},
                            COALESCE(bool_or(r.role_key IN ('super_admin', 'provincial_admin')), FALSE) AS is_global,
                            COALESCE(bool_or(r.role_key IN ('super_admin', 'provincial_admin', 'admin')), FALSE) AS can_manage
                        FROM sso_users u
                        LEFT JOIN user_role ur ON ur.user_id = u.id
                        LEFT JOIN roles r ON r.id = ur.role_id
                        WHERE u.id = :user_id
                        GROUP BY u.id, u.area
                        """
                    ),
                    {"user_id": CurrentUserId},
                )
            ).mappings().first()
            if not row:
                raise LeauditException(StatusCodeEnum.HTTP_404_NOT_FOUND, "当前用户不存在")
            tenant = await self.TenantResolver.ResolveUserContext(
                Area=str(row["area"] or ""),
                TenantCode=str(row.get("tenant_code") or "") or None,
                TenantName=str(row.get("tenant_name") or "") or None,
                Source="contract_template_user_context",
            )
            return {
                "id": int(row["id"]),
                "area": str(row["area"] or ""),
                "tenant_code": tenant.tenant_code or (str(row.get("tenant_code") or "") or None),
                "tenant_name": tenant.tenant_name or (str(row.get("tenant_name") or "") or None),
                "tenant_scope_value": tenant.tenant_name or tenant.normalized_value or str(row["area"] or ""),
                "is_global": bool(row["is_global"]),
                "can_manage": bool(row["can_manage"]),
                "is_area_admin": bool(row["can_manage"]) and not bool(row["is_global"]),
            }
        finally:
            if own_session:
                await session_cm.__aexit__(None, None, None)

    def _resolve_upload_scope(
        self,
        currentUser: dict[str, Any],
        requestedRegion: str | None,
        requestedTenantCode: str | None = None,
    ) -> tuple[str | None, str | None, str]:
        requested_tenant_code, requested_region = self._normalize_scope_value(requestedRegion, requestedTenantCode, currentUser)
        current_tenant_code = str(currentUser.get("tenant_code") or "").strip() or None
        current_tenant_name = str(currentUser.get("tenant_name") or "").strip() or None
        current_region = str(currentUser["tenant_scope_value"] or currentUser["area"] or "").strip()
        if not currentUser.get("is_area_admin"):
            raise LeauditException(StatusCodeEnum.HTTP_403_FORBIDDEN, "当前仅支持租户管理员上传合同模板")
        if not current_region:
            raise LeauditException(StatusCodeEnum.HTTP_403_FORBIDDEN, "当前租户管理员账号未配置所属租户，无法上传合同模板")
        if requested_tenant_code and not current_tenant_code:
            raise LeauditException(StatusCodeEnum.HTTP_403_FORBIDDEN, "当前账号未配置标准租户编码，不能显式指定模板租户")
        if requested_tenant_code and current_tenant_code and requested_tenant_code != current_tenant_code:
            raise LeauditException(StatusCodeEnum.HTTP_403_FORBIDDEN, "当前仅允许上传到本人所属租户")
        if requested_region and requested_region != current_region:
            raise LeauditException(StatusCodeEnum.HTTP_403_FORBIDDEN, "当前仅允许上传到本人所属租户")
        return current_tenant_code, current_tenant_name or current_region, current_region

    def _normalize_scope_value(
        self,
        requestedRegion: str | None,
        requestedTenantCode: str | None,
        currentUser: dict[str, Any],
    ) -> tuple[str | None, str]:
        tenant_code = str(requestedTenantCode or "").strip()
        if tenant_code:
            if tenant_code == str(currentUser.get("tenant_code") or "").strip():
                return (
                    str(currentUser.get("tenant_code") or "").strip() or None,
                    str(currentUser.get("tenant_scope_value") or currentUser.get("tenant_name") or currentUser.get("area") or "").strip(),
                )
            if tenant_code == "PUBLIC":
                return "PUBLIC", "公共"
            if tenant_code == "PROVINCIAL":
                return "PROVINCIAL", "省级"
            return tenant_code, str(requestedRegion or "").strip()
        return None, str(requestedRegion or "").strip()

    def _tenant_filter_sql(
        self,
        params: dict[str, Any],
        *,
        tenant_code: str | None,
        region: str | None,
        prefix: str,
    ) -> list[str]:
        normalized_region = str(region or "").strip()
        normalized_tenant_code = str(tenant_code or "").strip()
        if normalized_tenant_code:
            params[f"{prefix}_tenant_code"] = normalized_tenant_code
            if normalized_region:
                params[f"{prefix}_region"] = normalized_region
                return [
                    "("
                    f"t.tenant_code = :{prefix}_tenant_code "
                    "OR ("
                    "(t.tenant_code IS NULL OR BTRIM(t.tenant_code) = '') "
                    f"AND t.region = :{prefix}_region"
                    ")"
                    ")"
                ]
            return [f"t.tenant_code = :{prefix}_tenant_code"]
        if normalized_region:
            params[f"{prefix}_region"] = normalized_region
            return [f"t.region = :{prefix}_region"]
        return ["1=0"]

    def _visible_tenant_filters(
        self,
        params: dict[str, Any],
        *,
        tenant_codes: list[str | None],
        legacy_regions: list[str | None],
    ) -> list[str]:
        normalized_tenant_codes = [code.strip() for code in tenant_codes if code and str(code).strip()]
        normalized_regions = [region.strip() for region in legacy_regions if region and str(region).strip()]
        if normalized_tenant_codes:
            params["visible_tenant_codes"] = normalized_tenant_codes
            if normalized_regions:
                params["visible_regions"] = normalized_regions
                return [
                    "("
                    "t.tenant_code IN :visible_tenant_codes "
                    "OR ("
                    "(t.tenant_code IS NULL OR BTRIM(t.tenant_code) = '') "
                    "AND t.region IN :visible_regions"
                    ")"
                    ")"
                ]
            return ["t.tenant_code IN :visible_tenant_codes"]
        if normalized_regions:
            params["visible_regions"] = normalized_regions
            return ["t.region IN :visible_regions"]
        return ["1=0"]

    async def _ensureContractTemplateSchema(self, session) -> None:
        statements = [
            """
            ALTER TABLE contract_categories
            ADD COLUMN IF NOT EXISTS created_by BIGINT
            """,
            """
            ALTER TABLE contract_categories
            ADD COLUMN IF NOT EXISTS updated_by BIGINT
            """,
            """
            ALTER TABLE contract_categories
            ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ
            """,
            """
            ALTER TABLE contract_templates
            ADD COLUMN IF NOT EXISTS region VARCHAR(50) NOT NULL DEFAULT '省级'
            """,
            """
            ALTER TABLE contract_templates
            ADD COLUMN IF NOT EXISTS tenant_code VARCHAR(64)
            """,
            """
            ALTER TABLE contract_templates
            ADD COLUMN IF NOT EXISTS tenant_name VARCHAR(128)
            """,
            """
            ALTER TABLE contract_templates
            ADD COLUMN IF NOT EXISTS pdf_file_path VARCHAR(500)
            """,
            """
            ALTER TABLE contract_templates
            ADD COLUMN IF NOT EXISTS original_file_name VARCHAR(500) NOT NULL DEFAULT ''
            """,
            """
            ALTER TABLE contract_templates
            ADD COLUMN IF NOT EXISTS mime_type VARCHAR(200)
            """,
            """
            ALTER TABLE contract_templates
            ADD COLUMN IF NOT EXISTS file_size BIGINT NOT NULL DEFAULT 0
            """,
            """
            ALTER TABLE contract_templates
            ADD COLUMN IF NOT EXISTS pdf_file_size BIGINT
            """,
            """
            ALTER TABLE contract_templates
            ADD COLUMN IF NOT EXISTS created_by BIGINT
            """,
            """
            ALTER TABLE contract_templates
            ADD COLUMN IF NOT EXISTS updated_by BIGINT
            """,
            """
            ALTER TABLE contract_templates
            ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ
            """,
        ]
        for statement in statements:
            await session.execute(text(statement))
        await session.execute(
            text(
                """
                UPDATE contract_templates
                SET region = '省级'
                WHERE region IS NULL OR BTRIM(region) = ''
                """
            )
        )
        await session.execute(
            text(
                """
                UPDATE contract_templates
                SET tenant_name = region
                WHERE (tenant_name IS NULL OR BTRIM(tenant_name) = '')
                  AND region IS NOT NULL
                  AND BTRIM(region) <> ''
                """
            )
        )
        await session.execute(
            text(
                """
                UPDATE contract_templates
                SET original_file_name = COALESCE(NULLIF(original_file_name, ''), title || CASE
                    WHEN file_format IS NOT NULL AND BTRIM(file_format) <> '' THEN '.' || LOWER(file_format)
                    ELSE ''
                END)
                WHERE original_file_name IS NULL OR BTRIM(original_file_name) = ''
                """
            )
        )
        await session.commit()
