# -*- coding: utf-8 -*-
"""
管理端路由 — 租户管理员 CRUD。

端点清单：
- GET    /api/admin/tenant-admins              — 管理员列表（分页 + 关键词搜索）
- POST   /api/admin/tenant-admins              — 创建管理员
- PATCH  /api/admin/tenant-admins/{id}         — 编辑管理员
- DELETE /api/admin/tenant-admins/{id}         — 软删除管理员
- POST   /api/admin/tenant-admins/{id}/reset-password — 重置密码

所有端点要求 JWT + site_admin 或 tenant_admin 角色。

需求: 14.1-14.7, A2.3, A2.6, A2.7, A2.8, A4.1
"""

from __future__ import annotations

import logging
from typing import Optional

from fastapi import APIRouter, Depends, HTTPException, Query, status
from psycopg2 import errors as pg_errors

from app.auth.dependencies import CurrentUser, get_current_user
from app.auth.jwt import hash_password
from app.database import get_connection
from app.schemas.admin_tenant_admins import (
    ResetPasswordRequest,
    TenantAdminCreateRequest,
    TenantAdminEditRequest,
    TenantAdminListItem,
)

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/admin", tags=["管理端租户管理员"])


# ── 管理端权限依赖：要求 site_admin 或 tenant_admin 角色 ──


def _require_admin():
    """
    管理端依赖：要求 JWT 中角色包含 site_admin 或 tenant_admin。

    直接从 JWT 校验角色，不查 auth.users 表（管理员在 admin_users 表，
    不在 auth.users 中，require_permission 会报"用户不存在"）。
    """

    async def _dependency(
        user: CurrentUser = Depends(get_current_user),
    ) -> CurrentUser:
        admin_roles = {"site_admin", "tenant_admin"}
        if not admin_roles.intersection(user.roles):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="需要管理员权限（site_admin 或 tenant_admin）",
            )
        return user

    return _dependency


# ── GET /api/admin/tenant-admins ──────────────────────────


@router.get("/tenant-admins")
async def list_tenant_admins(
    page: int = Query(1, ge=1, description="页码"),
    page_size: int = Query(20, ge=1, le=100, description="每页条数"),
    keyword: Optional[str] = Query(None, description="关键词搜索（用户名/显示名称）"),
    include_inactive: bool = Query(False, description="是否包含已禁用的管理员"),
    user: CurrentUser = Depends(_require_admin()),
):
    """
    查询租户管理员列表，支持分页和关键词搜索。

    默认只返回 is_active=true 的记录；include_inactive=true 时返回所有记录。
    JOIN biz.tenants 获取 tenant_name。

    需求 14.1, A2.7, A2.6
    """
    offset = (page - 1) * page_size

    conn = get_connection()
    try:
        with conn.cursor() as cur:
            # 构建查询
            where_clauses: list[str] = []
            params: list = []

            # CHANGE 2026-03-22 | Prompt: 删除与禁用分离 | 始终过滤已删除记录
            where_clauses.append("ta.deleted_at IS NULL")

            # CHANGE 2026-03-23 | Prompt: 任务5.1 A2.7 | 默认过滤 is_active
            if not include_inactive:
                where_clauses.append("ta.is_active = true")

            if keyword:
                where_clauses.append(
                    "(ta.username ILIKE %s OR ta.display_name ILIKE %s)"
                )
                like_val = f"%{keyword}%"
                params.extend([like_val, like_val])

            where_sql = ("WHERE " + " AND ".join(where_clauses)) if where_clauses else ""

            # 查询总数
            cur.execute(
                f"""
                SELECT COUNT(*)
                FROM auth.tenant_admins ta
                {where_sql}
                """,
                params,
            )
            total = cur.fetchone()[0]

            # CHANGE 2026-03-23 | Prompt: 任务5.1 A2.6 | JOIN biz.tenants 获取 tenant_name
            # CHANGE 2026-03-23 | Prompt: 角色体系隔离 | 加入 admin_type 列
            cur.execute(
                f"""
                SELECT ta.id, ta.username, ta.display_name, ta.tenant_id,
                       ta.managed_site_ids,
                       ta.is_active, ta.created_at, ta.last_login_at,
                       bt.tenant_name, ta.admin_type
                FROM auth.tenant_admins ta
                LEFT JOIN biz.tenants bt ON bt.tenant_id = ta.tenant_id
                {where_sql}
                ORDER BY ta.created_at DESC
                LIMIT %s OFFSET %s
                """,
                params + [page_size, offset],
            )
            rows = cur.fetchall()
    finally:
        conn.close()

    items = [
        TenantAdminListItem(
            id=r[0],
            username=r[1],
            display_name=r[2],
            tenant_id=r[3],
            managed_site_ids=list(r[4]) if r[4] else [],
            is_active=r[5],
            created_at=r[6].isoformat() if r[6] else None,
            last_login_at=r[7].isoformat() if r[7] else None,
            tenant_name=r[8],
            admin_type=r[9],
        )
        for r in rows
    ]

    # 返回分页格式（由 ResponseWrapperMiddleware 包装为 {code:0, data:...}）
    return {"items": items, "total": total, "page": page, "page_size": page_size}


# ── POST /api/admin/tenant-admins ─────────────────────────


@router.post("/tenant-admins", status_code=status.HTTP_201_CREATED)
async def create_tenant_admin(
    body: TenantAdminCreateRequest,
    user: CurrentUser = Depends(_require_admin()),
):
    """
    创建租户管理员。

    密码 bcrypt 哈希，username UNIQUE 冲突返回 409，记录 created_by。
    创建时校验 tenant_id 在 biz.tenants 中存在且 is_active=true。

    需求 14.2, 14.3, A2.6
    """
    password_hash = hash_password(body.password)

    conn = get_connection()
    try:
        with conn.cursor() as cur:
            # CHANGE 2026-03-23 | Prompt: 任务5.1 A2.6 | 校验 tenant_id 存在性
            cur.execute(
                "SELECT id FROM biz.tenants WHERE tenant_id = %s AND is_active = true",
                (body.tenant_id,),
            )
            if cur.fetchone() is None:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail="租户不存在",
                )

            # CHANGE 2026-03-23 | Prompt: 登录用户名大小写不敏感 | 存储时统一小写
            cur.execute(
                """
                INSERT INTO auth.tenant_admins
                    (username, password_hash, display_name, tenant_id, managed_site_ids, created_by)
                VALUES (LOWER(%s), %s, %s, %s, %s, %s)
                RETURNING id, created_at
                """,
                (
                    body.username,
                    password_hash,
                    body.display_name,
                    body.tenant_id,
                    body.managed_site_ids,
                    user.user_id,
                ),
            )
            row = cur.fetchone()
        conn.commit()
    except HTTPException:
        raise
    except pg_errors.UniqueViolation:
        conn.rollback()
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="用户名已存在",
        )
    except Exception:
        conn.rollback()
        raise
    finally:
        conn.close()

    return {"id": row[0], "created_at": row[1].isoformat() if row[1] else None}


# ── PATCH /api/admin/tenant-admins/{id} ───────────────────


@router.patch("/tenant-admins/{admin_id}")
async def edit_tenant_admin(
    admin_id: int,
    body: TenantAdminEditRequest,
    user: CurrentUser = Depends(_require_admin()),
):
    """
    编辑租户管理员信息（username / display_name / managed_site_ids / is_active）。

    管理员 ID 不存在返回 404。
    修改 username 时校验全局唯一性（排除自身），冲突返回 409。

    需求 14.4, 14.6, A2.8
    """
    # 构建动态 SET 子句
    set_clauses: list[str] = []
    params: list = []

    # CHANGE 2026-03-23 | Prompt: 任务5.1 A2.8 | 支持修改 username
    # CHANGE 2026-03-23 | Prompt: 登录用户名大小写不敏感 | 存储时统一小写
    if body.username is not None:
        set_clauses.append("username = LOWER(%s)")
        params.append(body.username)
    if body.display_name is not None:
        set_clauses.append("display_name = %s")
        params.append(body.display_name)
    if body.managed_site_ids is not None:
        set_clauses.append("managed_site_ids = %s")
        params.append(body.managed_site_ids)
    if body.is_active is not None:
        set_clauses.append("is_active = %s")
        params.append(body.is_active)

    if not set_clauses:
        raise HTTPException(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            detail="至少需要提供一个修改字段",
        )

    params.append(admin_id)

    conn = get_connection()
    try:
        with conn.cursor() as cur:
            # CHANGE 2026-03-23 | Prompt: 任务5.1 A2.8 | username 唯一性校验（排除自身）
            # CHANGE 2026-03-22 | Prompt: 删除与禁用分离 | 只在未删除记录中校验唯一性
            # CHANGE 2026-03-23 | Prompt: 登录用户名大小写不敏感 | LOWER() 比较 + 存储小写
            if body.username is not None:
                cur.execute(
                    "SELECT id FROM auth.tenant_admins WHERE LOWER(username) = LOWER(%s) AND id != %s AND deleted_at IS NULL",
                    (body.username, admin_id),
                )
                if cur.fetchone() is not None:
                    raise HTTPException(
                        status_code=status.HTTP_409_CONFLICT,
                        detail="用户名已存在",
                    )

            cur.execute(
                f"""
                UPDATE auth.tenant_admins
                SET {', '.join(set_clauses)}
                WHERE id = %s AND deleted_at IS NULL
                RETURNING id
                """,
                params,
            )
            row = cur.fetchone()
        if row is None:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="租户管理员不存在",
            )
        conn.commit()
    except HTTPException:
        raise
    except Exception:
        conn.rollback()
        raise
    finally:
        conn.close()

    return {"id": admin_id}


# ── DELETE /api/admin/tenant-admins/{id} ──────────────────


@router.delete("/tenant-admins/{admin_id}")
async def delete_tenant_admin(
    admin_id: int,
    user: CurrentUser = Depends(_require_admin()),
):
    """
    软删除租户管理员（设置 deleted_at=NOW()）。

    无论 is_active 状态如何，均可删除。
    管理员不存在或已删除返回 404；重复删除幂等返回 404。

    需求 A2.3
    """
    # CHANGE 2026-03-22 | Prompt: 删除与禁用分离 | deleted_at 软删除，不再检查 is_active
    conn = get_connection()
    try:
        with conn.cursor() as cur:
            cur.execute(
                "UPDATE auth.tenant_admins SET deleted_at = NOW() "
                "WHERE id = %s AND deleted_at IS NULL "
                "RETURNING id",
                (admin_id,),
            )
            row = cur.fetchone()

            if row is None:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail="管理员不存在",
                )
        conn.commit()
    except HTTPException:
        raise
    except Exception:
        conn.rollback()
        raise
    finally:
        conn.close()

    return {"id": admin_id}


# ── POST /api/admin/tenant-admins/{id}/reset-password ─────


@router.post("/tenant-admins/{admin_id}/reset-password")
async def reset_password(
    admin_id: int,
    body: ResetPasswordRequest,
    user: CurrentUser = Depends(_require_admin()),
):
    """
    重置租户管理员密码。

    新密码 bcrypt 哈希后更新 password_hash。管理员 ID 不存在返回 404。
    需求 14.5
    """
    new_hash = hash_password(body.new_password)

    conn = get_connection()
    try:
        with conn.cursor() as cur:
            cur.execute(
                """
                UPDATE auth.tenant_admins
                SET password_hash = %s
                WHERE id = %s AND deleted_at IS NULL
                RETURNING id
                """,
                (new_hash, admin_id),
            )
            row = cur.fetchone()
        if row is None:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="租户管理员不存在",
            )
        conn.commit()
    except HTTPException:
        raise
    except Exception:
        conn.rollback()
        raise
    finally:
        conn.close()

    return {"id": admin_id}
