# -*- coding: utf-8 -*-
"""
租户管理后台 — 店铺管理员 CRUD 路由。

仅 admin_type='tenant_admin' 的管理员可调用。
店铺管理员复用 auth.tenant_admins 表，admin_type='site_admin'。

端点清单：
- GET    /api/tenant/site-admins              — 店铺管理员列表
- POST   /api/tenant/site-admins              — 创建店铺管理员
- PATCH  /api/tenant/site-admins/{id}         — 编辑店铺管理员
- DELETE /api/tenant/site-admins/{id}         — 软删除店铺管理员
- POST   /api/tenant/site-admins/{id}/reset-password — 重置密码

AI_CHANGELOG
- 2026-03-23 21:00:00 | Prompt: P20260323-210000（根治 tenant_admin managed_site_ids 限制）| Direct cause：JWT managed_site_ids 静态签发，新建店铺后所有端点受限 | Summary：create_site_admin 和 edit_site_admin 的权限子集校验改用 get_effective_site_ids(admin)（覆盖新建店铺）| Verify：创建/编辑店铺管理员时可选新建店铺
"""

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 pydantic import Field

from app.auth.jwt import hash_password
from app.auth.tenant_admins import CurrentTenantAdmin, get_effective_site_ids, require_tenant_admin
from app.database import get_connection
from app.schemas.base import CamelModel

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/tenant", tags=["租户店铺管理员"])


# ── 权限守卫：仅 tenant_admin 可调用 ─────────────────────


def _require_tenant_admin_type(admin: CurrentTenantAdmin) -> CurrentTenantAdmin:
    """校验当前登录者为租户管理员（非店铺管理员）。"""
    if admin.admin_type != "tenant_admin":
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="仅租户管理员可执行此操作",
        )
    return admin


# ── Schema ────────────────────────────────────────────────


class SiteAdminCreateRequest(CamelModel):
    """创建店铺管理员请求。"""
    username: str = Field(..., max_length=56, description="用户名（site_code 前缀 + 最长 50 字符）")
    password: str = Field(..., min_length=6, description="初始密码")
    display_name: str | None = Field(None, max_length=100, description="显示名称")
    managed_site_ids: list[int] = Field(..., min_length=1, description="管辖门店 ID 列表")


class SiteAdminEditRequest(CamelModel):
    """编辑店铺管理员请求。"""
    display_name: str | None = Field(None, max_length=100)
    managed_site_ids: list[int] | None = Field(None, min_length=1)
    is_active: bool | None = None


class SiteAdminResetPasswordRequest(CamelModel):
    """重置密码请求。"""
    new_password: str = Field(..., min_length=6)


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


@router.get("/site-admins")
async def list_site_admins(
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    keyword: Optional[str] = Query(None, description="搜索用户名/显示名称"),
    admin: CurrentTenantAdmin = Depends(require_tenant_admin),
):
    """列出当前租户下的店铺管理员。"""
    _require_tenant_admin_type(admin)
    offset = (page - 1) * page_size

    conn = get_connection()
    try:
        with conn.cursor() as cur:
            where_parts = [
                "ta.tenant_id = %s",
                "ta.admin_type = 'site_admin'",
                "ta.deleted_at IS NULL",
            ]
            params: list = [admin.tenant_id]

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

            where_sql = " AND ".join(where_parts)

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

            cur.execute(
                f"""
                SELECT ta.id, ta.username, ta.display_name, ta.managed_site_ids,
                       ta.is_active, ta.created_at, ta.last_login_at
                  FROM auth.tenant_admins ta
                 WHERE {where_sql}
                 ORDER BY ta.created_at DESC
                 LIMIT %s OFFSET %s
                """,
                params + [page_size, offset],
            )
            rows = cur.fetchall()
    finally:
        conn.close()

    items = [
        {
            "id": r[0], "username": r[1], "displayName": r[2],
            "managedSiteIds": list(r[3]) if r[3] else [],
            "isActive": r[4],
            "createdAt": r[5].isoformat() if r[5] else None,
            "lastLoginAt": r[6].isoformat() if r[6] else None,
        }
        for r in rows
    ]
    return {"items": items, "total": total, "page": page, "pageSize": page_size}


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


@router.post("/site-admins", status_code=status.HTTP_201_CREATED)
async def create_site_admin(
    body: SiteAdminCreateRequest,
    admin: CurrentTenantAdmin = Depends(require_tenant_admin),
):
    """
    创建店铺管理员。

    用户名校验：必须以管辖店铺的 site_code 开头。
    managed_site_ids 必须是当前租户管理员管辖范围的子集。
    """
    _require_tenant_admin_type(admin)

    # 校验 managed_site_ids 是当前管理员有效管辖范围的子集
    # [CHANGE P20260323-210000] intent: 使用 get_effective_site_ids 替代 JWT managed_site_ids，
    # tenant_admin 按 tenant_id 查库获取有效范围（覆盖新建店铺）
    effective_ids = get_effective_site_ids(admin)
    if not set(body.managed_site_ids).issubset(set(effective_ids)):
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="所选门店超出您的管辖范围",
        )

    # 校验用户名以 site_code 开头
    conn = get_connection()
    try:
        with conn.cursor() as cur:
            # 查询第一个管辖店铺的 site_code
            cur.execute(
                "SELECT site_code FROM biz.sites WHERE site_id = %s AND is_active = true",
                (body.managed_site_ids[0],),
            )
            site_row = cur.fetchone()
            if site_row is None or site_row[0] is None:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail="第一个管辖店铺未设置简写ID",
                )
            expected_prefix = site_row[0].upper()
            if not body.username.upper().startswith(expected_prefix):
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail=f"用户名必须以简写ID '{expected_prefix}' 开头",
                )

            # 插入记录
            password_hash = hash_password(body.password)
            cur.execute(
                """
                INSERT INTO auth.tenant_admins
                    (username, password_hash, display_name, tenant_id,
                     managed_site_ids, admin_type, created_by)
                VALUES (LOWER(%s), %s, %s, %s, %s, 'site_admin', %s)
                RETURNING id, created_at
                """,
                (
                    body.username,
                    password_hash,
                    body.display_name,
                    admin.tenant_id,
                    body.managed_site_ids,
                    admin.admin_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()
        logger.error("创建店铺管理员失败", exc_info=True)
        raise HTTPException(status_code=500, detail="创建失败")
    finally:
        conn.close()

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


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


@router.patch("/site-admins/{admin_id}")
async def edit_site_admin(
    admin_id: int,
    body: SiteAdminEditRequest,
    admin: CurrentTenantAdmin = Depends(require_tenant_admin),
):
    """编辑店铺管理员（显示名称/管辖门店/启用状态）。"""
    _require_tenant_admin_type(admin)

    if body.managed_site_ids is not None:
        effective_ids = get_effective_site_ids(admin)
        if not set(body.managed_site_ids).issubset(set(effective_ids)):
            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="所选门店超出您的管辖范围")

    set_clauses: list[str] = []
    params: list = []
    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=422, detail="至少需要提供一个修改字段")

    params.extend([admin_id, admin.tenant_id])

    conn = get_connection()
    try:
        with conn.cursor() as cur:
            cur.execute(
                f"""
                UPDATE auth.tenant_admins
                   SET {', '.join(set_clauses)}
                 WHERE id = %s AND tenant_id = %s
                   AND admin_type = 'site_admin' AND deleted_at IS NULL
                RETURNING id
                """,
                params,
            )
            if cur.fetchone() is None:
                raise HTTPException(status_code=404, detail="店铺管理员不存在")
        conn.commit()
    except HTTPException:
        raise
    except Exception:
        conn.rollback()
        raise
    finally:
        conn.close()

    return {"id": admin_id}


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


@router.delete("/site-admins/{admin_id}")
async def delete_site_admin(
    admin_id: int,
    admin: CurrentTenantAdmin = Depends(require_tenant_admin),
):
    """软删除店铺管理员。"""
    _require_tenant_admin_type(admin)

    conn = get_connection()
    try:
        with conn.cursor() as cur:
            cur.execute(
                """
                UPDATE auth.tenant_admins SET deleted_at = NOW()
                 WHERE id = %s AND tenant_id = %s
                   AND admin_type = 'site_admin' AND deleted_at IS NULL
                RETURNING id
                """,
                (admin_id, admin.tenant_id),
            )
            if cur.fetchone() is None:
                raise HTTPException(status_code=404, detail="店铺管理员不存在")
        conn.commit()
    except HTTPException:
        raise
    except Exception:
        conn.rollback()
        raise
    finally:
        conn.close()

    return {"id": admin_id}


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


@router.post("/site-admins/{admin_id}/reset-password")
async def reset_site_admin_password(
    admin_id: int,
    body: SiteAdminResetPasswordRequest,
    admin: CurrentTenantAdmin = Depends(require_tenant_admin),
):
    """重置店铺管理员密码。"""
    _require_tenant_admin_type(admin)

    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 tenant_id = %s
                   AND admin_type = 'site_admin' AND deleted_at IS NULL
                RETURNING id
                """,
                (new_hash, admin_id, admin.tenant_id),
            )
            if cur.fetchone() is None:
                raise HTTPException(status_code=404, detail="店铺管理员不存在")
        conn.commit()
    except HTTPException:
        raise
    except Exception:
        conn.rollback()
        raise
    finally:
        conn.close()

    return {"id": admin_id}
