import os
import json
import logging
from datetime import datetime
from fastapi import APIRouter, Query, Request, HTTPException
from fastapi.responses import FileResponse

from ...core.config import settings
from ...core.permission import get_current_user, is_admin_role
from ...services.template_scanner_service import TemplateScannerService, DocumentGeneratorV3
from ...utils.request_utils import safe_json

logger = logging.getLogger(__name__)

router = APIRouter()

scanner_service = TemplateScannerService()
generator_service = DocumentGeneratorV3()


def _get_template_base_dir() -> str:
    base = settings.TEMPLATE_BASE_DIR
    if os.path.isabs(base):
        return base
    # 尝试从项目根目录解析（优先，因为服务通常从项目根目录或 bff-api 子目录启动）
    project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../.."))
    candidate = os.path.abspath(os.path.join(project_root, base))
    if os.path.exists(candidate):
        return candidate
    # 尝试从当前工作目录解析（兼容开发环境直接运行）
    candidate = os.path.abspath(os.path.join(os.getcwd(), base))
    if os.path.exists(candidate):
        return candidate
    # EXE 模式：通过 paths.resolve_resource_path 解析（_internal/templates/）
    from ...core import paths
    candidate = paths.resolve_resource_path(base)
    if os.path.exists(candidate):
        return candidate
    # 兜底：返回候选路径，便于排查
    return candidate


# 模块加载时立即解析并缓存模板基础目录，避免运行时重复计算
_TEMPLATE_BASE_DIR = _get_template_base_dir()
logger.info(f"模板基础目录: {_TEMPLATE_BASE_DIR}")


def _load_index() -> list:
    base = _TEMPLATE_BASE_DIR
    index_path = os.path.join(base, "index.json")
    if not os.path.exists(index_path):
        logger.warning(f"模板索引文件不存在: {index_path}")
        return []
    try:
        with open(index_path, "r", encoding="utf-8") as f:
            data = json.load(f)
            logger.info(f"加载模板索引成功: {len(data)} 条记录")
            return data
    except Exception as e:
        logger.error(f"加载模板索引失败: {e}")
        return []


# ============================================================
# 固定路径路由（必须放在 /{template_id} 之前！）
# ============================================================

@router.get("")
async def list_document_templates(
    request: Request,
    page: int = Query(1, ge=1),
    pageSize: int = Query(20, ge=1, le=200),
    keyword: str = Query(""),
    category: str = Query(""),
    status: str = Query(""),
):
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        raise HTTPException(status_code=403, detail="无权限")

    try:
        templates = _load_index()

        if keyword:
            templates = [t for t in templates if keyword in t.get("name", "") or keyword in t.get("id", "")]
        if category:
            templates = [t for t in templates if t.get("category") == category]
        if status:
            templates = [t for t in templates if t.get("coverage_status", "not_scanned") == status]

        total = len(templates)
        offset = (page - 1) * pageSize
        page_items = templates[offset:offset + pageSize]

        result_list = []
        for item in page_items:
            result_list.append({
                "id": item.get("id"),
                "template_name": item.get("name"),
                "template_code": item.get("id"),
                "template_category": item.get("category"),
                "layout_type": item.get("layout"),
                "total_placeholders": item.get("total_placeholders", 0),
                "unique_placeholders": item.get("unique_placeholders", 0),
                "mapped_placeholders": item.get("mapped_placeholders", 0),
                "coverage_status": item.get("coverage_status", "not_scanned"),
                "last_scan_time": item.get("last_scan_time"),
            })

        return {"list": result_list, "total": total, "page": page, "pageSize": pageSize}
    except Exception as e:
        logger.error(f"获取模板列表失败: {e}")
        return {"list": [], "total": 0, "page": page, "pageSize": pageSize}


@router.get("/categories")
async def get_template_categories(request: Request):
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        raise HTTPException(status_code=403, detail="无权限")

    try:
        templates = _load_index()
        categories = sorted(set(t.get("category", "") for t in templates if t.get("category")))
        return {"categories": categories}
    except Exception:
        return {"categories": []}


@router.get("/stats")
async def get_templates_stats(request: Request):
    """获取全局统计信息（56个模板覆盖率总览）"""
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        raise HTTPException(status_code=403, detail="无权限")

    try:
        templates = _load_index()
        
        total = len(templates)
        by_status = {"ready": 0, "partial": 0, "empty": 0, "not_scanned": 0}
        by_category = {}
        coverage_values = []
        
        for t in templates:
            status = t.get("coverage_status", "not_scanned")
            by_status[status] = by_status.get(status, 0) + 1
            
            cat = t.get("category", "未分类")
            if cat not in by_category:
                by_category[cat] = {"total": 0, "ready": 0, "partial": 0, "empty": 0}
            by_category[cat]["total"] += 1
            
            if status in by_category[cat]:
                by_category[cat][status] += 1
            
            unique = t.get("unique_placeholders", 0)
            mapped = t.get("mapped_placeholders", 0)
            if unique > 0:
                coverage_values.append(round(mapped / unique * 100, 1))
        
        avg_coverage = round(sum(coverage_values) / len(coverage_values), 1) if coverage_values else 0
        
        return {
            "success": True,
            "data": {
                "total_templates": total,
                "by_status": by_status,
                "by_category": by_category,
                "average_coverage": avg_coverage,
                "standard_placeholder_count": 29,
            }
        }
    except Exception as e:
        logger.error(f"获取统计信息失败: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/scan-all")
async def scan_all_templates(request: Request):
    """批量扫描全部模板"""
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        raise HTTPException(status_code=403, detail="无权限")

    try:
        result = scanner_service.scan_all_templates()
        
        base = _get_template_base_dir()
        templates = _load_index()
        
        scan_map = {r['filename']: r for r in result['results']}
        
        for t in templates:
            docx_file = t.get("files", {}).get("docx", "")
            filename = os.path.basename(docx_file) if docx_file else ""
            
            if filename in scan_map:
                r = scan_map[filename]
                t["total_placeholders"] = r["total_placeholders"]
                t["unique_placeholders"] = r["unique_count"]
                t["mapped_placeholders"] = r["mapped_count"]
                t["coverage_status"] = r["coverage_status"]
                t["placeholder_mapping"] = r["mapping"]
                t["last_scan_time"] = r["scan_time"]
        
        index_path = os.path.join(base, "index.json")
        with open(index_path, "w", encoding="utf-8") as f:
            json.dump(templates, f, ensure_ascii=False, indent=2)
        
        return {
            "success": True,
            "data": result
        }
    except Exception as e:
        logger.error(f"批量扫描失败: {e}")
        raise HTTPException(status_code=500, detail=f"批量扫描失败: {str(e)}")


# ============================================================
# 动态路径路由（/{template_id}/...）
# ============================================================

@router.post("/{template_id}/scan")
async def scan_template(template_id: str, request: Request):
    """扫描单个模板的占位符"""
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        raise HTTPException(status_code=403, detail="无权限")

    try:
        base = _get_template_base_dir()
        templates = _load_index()
        template = next((t for t in templates if t.get("id") == template_id), None)
        
        if not template:
            raise HTTPException(status_code=404, detail="模板不存在")
        
        docx_path = os.path.join(base, template["files"]["docx"])
        if not os.path.exists(docx_path):
            raise HTTPException(status_code=404, detail="模板文件不存在")
        
        result = scanner_service.scan_template_file(docx_path)
        
        for t in templates:
            if t.get("id") == template_id:
                t["total_placeholders"] = result["total_placeholders"]
                t["unique_placeholders"] = result["unique_count"]
                t["mapped_placeholders"] = result["mapped_count"]
                t["coverage_status"] = result["coverage_status"]
                t["placeholder_mapping"] = result["mapping"]
                t["last_scan_time"] = result["scan_time"]
                break
        
        index_path = os.path.join(base, "index.json")
        with open(index_path, "w", encoding="utf-8") as f:
            json.dump(templates, f, ensure_ascii=False, indent=2)
        
        return {
            "success": True,
            "data": {
                **result,
                "template_id": template_id,
                "template_name": template.get("name")
            }
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"扫描模板失败 [{template_id}]: {e}")
        raise HTTPException(status_code=500, detail=f"扫描失败: {str(e)}")


@router.get("/{template_id}/placeholders")
async def get_template_placeholders(template_id: str, request: Request):
    """获取模板占位符详情"""
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        raise HTTPException(status_code=403, detail="无权限")

    try:
        base = _get_template_base_dir()
        templates = _load_index()
        template = next((t for t in templates if t.get("id") == template_id), None)
        
        if not template:
            raise HTTPException(status_code=404, detail="模板不存在")
        
        mapping = template.get("placeholder_mapping", {})
        
        placeholder_details = []
        for ph_name, ph_info in mapping.items():
            if isinstance(ph_info, dict):
                placeholder_details.append({
                    "name": ph_name,
                    "occurrences": ph_info.get("occurrences", 0),
                    "mapped": ph_info.get("mapped", False),
                    "source_table": ph_info.get("source"),
                    "source_field": ph_info.get("field"),
                    "fill_type": ph_info.get("type"),
                })
            else:
                placeholder_details.append({
                    "name": ph_name,
                    "occurrences": 1,
                    "mapped": False,
                    "source_table": None,
                    "source_field": None,
                    "fill_type": None,
                })
        
        return {
            "success": True,
            "data": {
                "template_id": template_id,
                "template_name": template.get("name"),
                "total_placeholders": template.get("total_placeholders", 0),
                "unique_placeholders": template.get("unique_placeholders", 0),
                "mapped_placeholders": template.get("mapped_placeholders", 0),
                "coverage_status": template.get("coverage_status", "not_scanned"),
                "placeholders": sorted(placeholder_details, key=lambda x: x.get('name', '')),
                "last_scan_time": template.get("last_scan_time"),
            }
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取占位符详情失败: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/{template_id}/coverage")
async def get_template_coverage(template_id: str, request: Request):
    """获取模板覆盖率信息"""
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        raise HTTPException(status_code=403, detail="无权限")

    try:
        base = _get_template_base_dir()
        templates = _load_index()
        template = next((t for t in templates if t.get("id") == template_id), None)
        
        if not template:
            raise HTTPException(status_code=404, detail="模板不存在")
        
        unique_count = template.get("unique_placeholders", 0)
        mapped_count = template.get("mapped_placeholders", 0)
        coverage_percent = round((mapped_count / unique_count * 100), 1) if unique_count > 0 else 0
        
        return {
            "success": True,
            "data": {
                "template_id": template_id,
                "template_name": template.get("name"),
                "total_placeholders": template.get("total_placeholders", 0),
                "unique_count": unique_count,
                "mapped_count": mapped_count,
                "unmapped_count": unique_count - mapped_count,
                "coverage_percent": coverage_percent,
                "status": template.get("coverage_status", "not_scanned"),
                "standard_total": 29,
                "last_scan_time": template.get("last_scan_time"),
            }
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取覆盖率失败: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/{template_id}/test-generate")
async def test_generate_document(template_id: str, request: Request):
    """测试生成：使用模拟数据填充模板"""
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        raise HTTPException(status_code=403, detail="无权限")

    try:
        base = _get_template_base_dir()
        templates = _load_index()
        template = next((t for t in templates if t.get("id") == template_id), None)
        
        if not template:
            raise HTTPException(status_code=404, detail="模板不存在")
        
        docx_path = os.path.join(base, template["files"]["docx"])
        if not os.path.exists(docx_path):
            raise HTTPException(status_code=404, detail="模板文件不存在")
        
        result = await generator_service.generate_test_document(template_id, docx_path)
        
        if result["success"]:
            return {"success": True, "data": result}
        else:
            raise HTTPException(status_code=500, detail=result["message"])
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"测试生成失败: {e}")
        raise HTTPException(status_code=500, detail=f"测试生成失败: {str(e)}")


@router.put("/{template_id}/mapping")
async def update_template_mapping(template_id: str, request: Request):
    """手动修正占位符映射关系"""
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        raise HTTPException(status_code=403, detail="无权限")

    try:
        body = await safe_json(request)
        new_mapping = body.get("mapping")
        
        if not new_mapping or not isinstance(new_mapping, dict):
            raise HTTPException(status_code=400, detail="mapping必须是JSON对象")
        
        base = _get_template_base_dir()
        templates = _load_index()
        template = next((t for t in templates if t.get("id") == template_id), None)
        
        if not template:
            raise HTTPException(status_code=404, detail="模板不存在")
        
        mapped_count = sum(1 for v in new_mapping.values() 
                          if isinstance(v, dict) and v.get("mapped"))
        unique_count = len(new_mapping)
        
        coverage_status = "ready"
        for v in new_mapping.values():
            if isinstance(v, dict) and not v.get("mapped"):
                coverage_status = "partial"
                break
        
        if unique_count == 0:
            coverage_status = "empty"
        
        for t in templates:
            if t.get("id") == template_id:
                t["placeholder_mapping"] = new_mapping
                t["mapped_placeholders"] = mapped_count
                t["unique_placeholders"] = unique_count
                t["coverage_status"] = coverage_status
                t["last_scan_time"] = datetime.now().isoformat()
                break
        
        index_path = os.path.join(base, "index.json")
        with open(index_path, "w", encoding="utf-8") as f:
            json.dump(templates, f, ensure_ascii=False, indent=2)
        
        return {
            "success": True,
            "message": "映射关系更新成功",
            "data": {
                "template_id": template_id,
                "mapped_count": mapped_count,
                "unique_count": unique_count,
                "coverage_status": coverage_status
            }
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"更新映射失败: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/public-template-file/{template_id}")
async def get_public_template_file(template_id: str):
    """公开的模板文件下载接口（供OnlyOffice服务器使用，无需认证）
    
    OnlyOffice 文档服务器通过 localhost:4010 直接访问此接口下载模板文件，
    避免了认证token传递的问题（errorCode -4 下载失败）。
    
    注意：此路由必须定义在 /{template_id}/... 动态路由之前！
    """
    try:
        base = _TEMPLATE_BASE_DIR
        templates = _load_index()
        template = next((t for t in templates if t.get("id") == template_id), None)
        
        if not template:
            raise HTTPException(status_code=404, detail="模板不存在")
        
        docx_path = os.path.join(base, template["files"]["docx"])
        if not os.path.exists(docx_path):
            raise HTTPException(status_code=404, detail="模板文件不存在")

        filename = os.path.basename(docx_path)
        logger.info(f"[公开接口] OnlyOffice 下载模板: {template_id} -> {filename}")
        return FileResponse(
            path=docx_path,
            filename=filename,
            media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"[公开接口] 获取模板文件失败: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/{template_id}/download")
async def download_template_docx(template_id: str, request: Request):
    """下载原始模板文件"""
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        raise HTTPException(status_code=403, detail="无权限")

    try:
        base = _get_template_base_dir()
        templates = _load_index()
        template = next((t for t in templates if t.get("id") == template_id), None)
        
        if not template:
            raise HTTPException(status_code=404, detail="模板不存在")
        
        docx_path = os.path.join(base, template["files"]["docx"])
        if not os.path.exists(docx_path):
            raise HTTPException(status_code=404, detail="Word模板文件不存在")

        filename = os.path.basename(docx_path)
        return FileResponse(
            path=docx_path,
            filename=filename,
            media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"下载模板失败: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/{template_id}/editor-config")
async def get_template_editor_config(template_id: str, request: Request):
    """获取模板的OnlyOffice编辑器配置（管理端编辑模板源文件）"""
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        raise HTTPException(status_code=403, detail="无权限")

    try:
        import time
        from ...core.config import settings as app_settings

        base = _get_template_base_dir()
        templates = _load_index()
        template = next((t for t in templates if t.get("id") == template_id), None)
        
        if not template:
            raise HTTPException(status_code=404, detail="模板不存在")
        
        docx_path = os.path.join(base, template["files"]["docx"])
        if not os.path.exists(docx_path):
            raise HTTPException(status_code=404, detail="模板文件不存在")

        document_key = f"tpl-{template_id}-{int(time.time() / 3600)}"
        
        # 统一的 server_url 获取逻辑
        server_url = getattr(app_settings, 'NEXT_PUBLIC_API_URL', None)
        if not server_url:
            forwarded_host = request.headers.get("X-Forwarded-Host") or request.headers.get("x-forwarded-host")
            forwarded_proto = request.headers.get("X-Forwarded-Proto") or request.headers.get("x-forwarded-proto", "http")
            if forwarded_host:
                server_url = f"{forwarded_proto}://{forwarded_host}"
            else:
                origin = request.headers.get('origin', '')
                server_url = origin if origin else f"http://127.0.0.1:{app_settings.BFF_API_PORT}"

        file_name = os.path.basename(docx_path)
        file_ext = "docx"

        callback_url = f"{server_url}/api/admin/document-templates/{template_id}/callback"
        # 使用 localhost 公开接口（无需认证），OnlyOffice 服务器可直接访问
        # 完整路径: /api/admin/document-templates/public-template-file/{template_id}
        file_url = f"http://localhost:{settings.BFF_API_PORT}/api/admin/document-templates/public-template-file/{template_id}"

        config = {
            "onlyofficeUrl": "/onlyoffice",
            "documentType": "word",
            "type": "desktop",
            "document": {
                "fileType": file_ext,
                "key": document_key,
                "url": file_url,
                "title": file_name,
                "permissions": {
                    "comment": True,
                    "download": True,
                    "edit": True,
                    "print": True,
                    "review": True
                },
            },
            "editorConfig": {
                "mode": "edit",
                "lang": "zh-CN",
                "callbackUrl": callback_url,
                "user": {
                    "id": str(current_user.get("userId", "admin")),
                    "name": current_user.get("username", "管理员"),
                },
            },
        }

        if app_settings.ONLYOFFICE_JWT_ENABLED and app_settings.ONLYOFFICE_JWT_SECRET:
            try:
                from ..user.documents import _generate_onlyoffice_jwt
                token = _generate_onlyoffice_jwt(config)
                config["token"] = token
            except Exception as e:
                logger.warning(f"JWT 编码错误: {e}")

        return config
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取编辑配置失败: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/{template_id}/file")
async def get_template_file(template_id: str, request: Request):
    """返回模板文件内容供OnlyOffice加载"""
    current_user = get_current_user(request)
    if not current_user or not is_admin_role(current_user["role"]):
        raise HTTPException(status_code=403, detail="无权限")

    try:
        base = _get_template_base_dir()
        templates = _load_index()
        template = next((t for t in templates if t.get("id") == template_id), None)
        
        if not template:
            raise HTTPException(status_code=404, detail="模板不存在")
        
        docx_path = os.path.join(base, template["files"]["docx"])
        if not os.path.exists(docx_path):
            raise HTTPException(status_code=404, detail="模板文件不存在")

        filename = os.path.basename(docx_path)
        return FileResponse(
            path=docx_path,
            filename=filename,
            media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        )
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取模板文件失败: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/{template_id}/callback")
async def template_edit_callback(template_id: str, request: Request):
    """OnlyOffice编辑回调：保存修改后的模板文件"""
    try:
        body = await safe_json(request)
        status = body.get("status")
        
        if status == 2:
            download_url = body.get("url")
            if download_url:
                import httpx
                async with httpx.AsyncClient(timeout=30) as client:
                    resp = await client.get(download_url)
                    if resp.status_code == 200:
                        base = _get_template_base_dir()
                        templates = _load_index()
                        template = next((t for t in templates if t.get("id") == template_id), None)
                        
                        if template:
                            docx_path = os.path.join(base, template["files"]["docx"])
                            with open(docx_path, "wb") as f:
                                f.write(resp.content)
                            logger.info(f"模板 [{template_id}] 已通过OnlyOffice保存更新")
        
        return {"error": 0}
    except Exception as e:
        logger.error(f"模板编辑回调处理失败: {e}")
        return {"error": 1, "message": str(e)}
