#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import annotations

import json
import os
from typing import Optional

from flask import Blueprint, current_app, redirect, render_template, session, url_for
from flask_login import current_user, login_required

from routes.decorators import admin_required
from services.runtime import get_logger

pages_bp = Blueprint("pages", __name__)


def _collect_entry_css_files(manifest: dict, entry_name: str) -> list[str]:
    css_files: list[str] = []
    seen_css: set[str] = set()
    visited: set[str] = set()

    def _append_css(entry_obj: dict) -> None:
        for css_file in entry_obj.get("css") or []:
            css_path = str(css_file or "").strip()
            if not css_path or css_path in seen_css:
                continue
            seen_css.add(css_path)
            css_files.append(css_path)

    def _walk_manifest_key(manifest_key: str) -> None:
        key = str(manifest_key or "").strip()
        if not key or key in visited:
            return
        visited.add(key)
        entry_obj = manifest.get(key)
        if not isinstance(entry_obj, dict):
            return
        _append_css(entry_obj)
        for imported_key in entry_obj.get("imports") or []:
            _walk_manifest_key(imported_key)

    entry = manifest.get(entry_name) or {}
    if isinstance(entry, dict):
        _append_css(entry)
        for imported_key in entry.get("imports") or []:
            _walk_manifest_key(imported_key)

    return css_files


def render_app_spa_or_legacy(
    legacy_template_name: str,
    legacy_context: Optional[dict] = None,
    spa_initial_state: Optional[dict] = None,
    spa_entry_name: str = "index.html",
):
    """渲染前台 Vue SPA（构建产物位于 static/app），失败则回退旧模板。"""
    logger = get_logger()
    legacy_context = legacy_context or {}
    manifest_path = os.path.join(current_app.root_path, "static", "app", ".vite", "manifest.json")
    try:
        with open(manifest_path, "r", encoding="utf-8") as f:
            manifest = json.load(f)

        entry = manifest.get(spa_entry_name) or {}
        js_file = entry.get("file")
        css_files = _collect_entry_css_files(manifest, spa_entry_name)

        if not js_file:
            logger.warning(f"[app_spa] manifest缺少入口文件: {manifest_path}")
            return render_template(legacy_template_name, **legacy_context)

        app_spa_js_file = f"app/{js_file}"
        app_spa_css_files = [f"app/{p}" for p in css_files]
        app_spa_build_id = _get_asset_build_id(
            os.path.join(current_app.root_path, "static"),
            [app_spa_js_file, *app_spa_css_files],
        )

        return render_template(
            "app.html",
            app_spa_js_file=app_spa_js_file,
            app_spa_css_files=app_spa_css_files,
            app_spa_build_id=app_spa_build_id,
            app_spa_initial_state=spa_initial_state,
        )
    except FileNotFoundError:
        logger.info(f"[app_spa] 未找到manifest: {manifest_path}，回退旧模板: {legacy_template_name}")
        return render_template(legacy_template_name, **legacy_context)
    except Exception as e:
        logger.error(f"[app_spa] 加载manifest失败: {e}")
        return render_template(legacy_template_name, **legacy_context)


def _get_asset_build_id(static_root: str, rel_paths: list[str]) -> Optional[str]:
    mtimes = []
    for rel_path in rel_paths:
        if not rel_path:
            continue
        try:
            mtimes.append(os.path.getmtime(os.path.join(static_root, rel_path)))
        except OSError:
            continue
    if not mtimes:
        return None
    return str(int(max(mtimes)))


@pages_bp.route("/")
def index():
    """主页 - 重定向到登录或应用"""
    if current_user.is_authenticated:
        return redirect(url_for("pages.app_page"))
    return redirect(url_for("pages.login_page"))


@pages_bp.route("/login")
def login_page():
    """登录页面"""
    return render_app_spa_or_legacy("login.html", spa_entry_name="login.html")


@pages_bp.route("/register")
def register_page():
    """注册页面"""
    return render_app_spa_or_legacy("register.html")


@pages_bp.route("/social-bind-callback")
def social_bind_callback_page():
    """快捷登录绑定回调页面（由前端路由继续处理授权参数）。"""
    return render_app_spa_or_legacy("index.html")


@pages_bp.route("/app")
@login_required
def app_page():
    """主应用页面"""
    return render_app_spa_or_legacy("index.html")


@pages_bp.route("/app/<path:subpath>")
@login_required
def app_page_subpath(subpath):
    """SPA 子路由刷新支持（History 模式）"""
    return render_app_spa_or_legacy("index.html")


@pages_bp.route("/yuyx")
def admin_login_page():
    """后台登录页面"""
    if "admin_id" in session:
        return redirect(url_for("pages.admin_page"))
    return render_template("admin_login.html")


@pages_bp.route("/yuyx/admin")
@admin_required
def admin_page():
    """后台管理页面"""
    logger = get_logger()
    manifest_path = os.path.join(current_app.root_path, "static", "admin", ".vite", "manifest.json")
    try:
        with open(manifest_path, "r", encoding="utf-8") as f:
            manifest = json.load(f)

        entry = manifest.get("index.html") or {}
        js_file = entry.get("file")
        css_files = entry.get("css") or []

        if not js_file:
            logger.error(f"[admin_spa] manifest缺少入口文件: {manifest_path}")
            return "后台前端资源缺失，请重新构建管理端", 503

        admin_spa_js_file = f"admin/{js_file}"
        admin_spa_css_files = [f"admin/{p}" for p in css_files]
        admin_spa_build_id = _get_asset_build_id(
            os.path.join(current_app.root_path, "static"),
            [admin_spa_js_file, *admin_spa_css_files],
        )

        return render_template(
            "admin.html",
            admin_spa_js_file=admin_spa_js_file,
            admin_spa_css_files=admin_spa_css_files,
            admin_spa_build_id=admin_spa_build_id,
        )
    except FileNotFoundError:
        logger.error(f"[admin_spa] 未找到manifest: {manifest_path}")
        return "后台前端资源未构建，请联系管理员", 503
    except Exception as e:
        logger.error(f"[admin_spa] 加载manifest失败: {e}")
        return "后台页面加载失败，请稍后重试", 500
