"""Super Admin — Tenant Management API
Routes:
  GET    /superadmin/tenants              list all tenants with stats
  POST   /superadmin/tenants              provision new tenant
  GET    /superadmin/tenants/{id}         tenant detail (with users)
  PUT    /superadmin/tenants/{id}         update tenant info
  POST   /superadmin/tenants/{id}/users   add user to tenant
  PUT    /superadmin/tenants/{id}/users/{uid}  update user
  DELETE /superadmin/tenants/{id}/users/{uid}  delete user
  GET    /superadmin/tenants/{id}/backup/stats  backup stats
  GET    /superadmin/tenants/{id}/backup         download backup
  POST   /superadmin/tenants/{id}/restore        restore from backup
"""
import json
import logging

import bcrypt
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, UploadFile, File, Query
from fastapi.responses import JSONResponse
from pydantic import BaseModel, EmailStr
from sqlalchemy import func, select, text
from sqlalchemy.ext.asyncio import AsyncSession

from app.api.deps import get_db, get_superadmin_user
from app.core.cache import cache_delete
from app.core.models.tenant import Tenant
from app.core.models.tenant_settings import TenantSettings
from app.core.models.user import User
from app.core.services.tenant_backup import export_tenant_data, restore_tenant_data, get_backup_stats
from app.core.services.tenant_seed import seed_tenant_defaults

logger = logging.getLogger("uvicorn.error")
router = APIRouter(prefix="/superadmin/tenants", tags=["超管-租户"])


# ── Schemas ───────────────────────────────────────────────────────────────────

class TenantCreate(BaseModel):
    name: str
    domain: str
    plan_type: str = "basic"
    admin_email: EmailStr
    admin_password: str
    admin_name: str = ""


class TenantUpdate(BaseModel):
    name: str | None = None
    domain: str | None = None
    status: str | None = None
    plan_type: str | None = None
    storage_profile_id: int | None = None
    storage_quota_mb: int | None = None
    ai_profile_id: int | None = None
    ai_allow_custom: bool | None = None
    ai_quota_monthly: int | None = None
    ai_quota_5h: int | None = None
    ai_quota_weekly: int | None = None


class UserCreate(BaseModel):
    email: EmailStr
    password: str
    name: str = ""
    role: str = "staff"


class UserUpdate(BaseModel):
    email: EmailStr | None = None
    name: str | None = None
    role: str | None = None
    is_active: bool | None = None
    password: str | None = None


# ── Helpers ───────────────────────────────────────────────────────────────────

VALID_ROLES = {"owner", "admin", "staff"}

def _hash_password(plain: str) -> str:
    return bcrypt.hashpw(plain.encode(), bcrypt.gensalt(rounds=12)).decode()


async def _get_tenant_or_404(db: AsyncSession, tenant_id: int) -> Tenant:
    result = await db.execute(select(Tenant).where(Tenant.id == tenant_id))
    tenant = result.scalar_one_or_none()
    if not tenant:
        raise HTTPException(status_code=404, detail="租户不存在")
    return tenant


async def _get_user_or_404(db: AsyncSession, tenant_id: int, user_id: int) -> User:
    result = await db.execute(
        select(User).where(User.id == user_id, User.tenant_id == tenant_id)
    )
    user = result.scalar_one_or_none()
    if not user:
        raise HTTPException(status_code=404, detail="用户不存在")
    return user


def _user_dict(u) -> dict:
    profile = {}
    if isinstance(u.profile, dict):
        profile = u.profile
    elif isinstance(u.profile, str):
        try:
            profile = json.loads(u.profile)
        except Exception:
            pass
    return {
        "id": u.id,
        "email": u.email,
        "role": u.role,
        "name": profile.get("name", ""),
        "is_active": bool(u.is_active),
        "created_at": u.created_at.isoformat() if u.created_at else None,
    }


# ── Tenant Routes ────────────────────────────────────────────────────────────

@router.get("/", summary="列出全部租户")
async def list_tenants(
    db: AsyncSession = Depends(get_db),
    _sa: User = Depends(get_superadmin_user),
):
    tenants = (await db.execute(select(Tenant).order_by(Tenant.id.asc()))).scalars().all()

    user_counts = dict(
        (await db.execute(
            select(User.tenant_id, func.count(User.id)).group_by(User.tenant_id)
        )).all()
    )

    order_rows = (await db.execute(
        text("SELECT tenant_id, COUNT(*) AS cnt FROM orders GROUP BY tenant_id")
    )).all()
    order_counts = {r[0]: r[1] for r in order_rows}

    return [
        {
            "id": t.id,
            "name": t.name,
            "domain": t.domain,
            "plan_type": t.plan_type,
            "status": t.status,
            "created_at": t.created_at.isoformat() if t.created_at else None,
            "user_count": user_counts.get(t.id, 0),
            "order_count": order_counts.get(t.id, 0),
            "storage_profile_id": t.storage_profile_id,
            "storage_quota_mb": t.storage_quota_mb,
            "storage_used_mb": t.storage_used_mb,
            "ai_profile_id": t.ai_profile_id,
            "ai_allow_custom": t.ai_allow_custom,
            "ai_quota_monthly": t.ai_quota_monthly,
            "ai_quota_5h": t.ai_quota_5h,
            "ai_quota_weekly": t.ai_quota_weekly,
            "ai_used_this_month": t.ai_used_this_month,
        }
        for t in tenants
    ]


@router.post("/", summary="开通新租户", status_code=201)
async def create_tenant(
    body: TenantCreate,
    db: AsyncSession = Depends(get_db),
    _sa: User = Depends(get_superadmin_user),
):
    existing = (await db.execute(
        select(Tenant).where(Tenant.domain == body.domain)
    )).scalar_one_or_none()
    if existing:
        raise HTTPException(status_code=409, detail=f"域名 {body.domain!r} 已被占用")

    tenant = Tenant(
        name=body.name,
        domain=body.domain,
        plan_type=body.plan_type,
        status="active",
    )
    db.add(tenant)
    await db.flush()

    settings_row = TenantSettings(
        tenant_id=tenant.id,
        store_name=body.name,
    )
    db.add(settings_row)

    profile = json.dumps({"name": body.admin_name or body.admin_email.split("@")[0]})
    admin_user = User(
        tenant_id=tenant.id,
        email=body.admin_email,
        password_hash=_hash_password(body.admin_password),
        role="owner",
        is_active=1,
        profile=profile,
    )
    db.add(admin_user)

    await db.flush()
    await seed_tenant_defaults(db, tenant.id)

    await db.commit()
    await db.refresh(tenant)

    logger.info("Provisioned new tenant id=%s name=%r domain=%r", tenant.id, tenant.name, tenant.domain)
    return {
        "id": tenant.id,
        "name": tenant.name,
        "domain": tenant.domain,
        "plan_type": tenant.plan_type,
        "status": tenant.status,
        "admin_email": body.admin_email,
    }


@router.get("/{tenant_id}", summary="租户详情")
async def get_tenant(
    tenant_id: int,
    db: AsyncSession = Depends(get_db),
    _sa: User = Depends(get_superadmin_user),
):
    tenant = await _get_tenant_or_404(db, tenant_id)
    users = (await db.execute(
        select(User).where(User.tenant_id == tenant_id).order_by(User.id.asc())
    )).scalars().all()

    return {
        "id": tenant.id,
        "name": tenant.name,
        "domain": tenant.domain,
        "plan_type": tenant.plan_type,
        "status": tenant.status,
        "created_at": tenant.created_at.isoformat() if tenant.created_at else None,
        "extra_config": tenant.extra_config,
        "users": [_user_dict(u) for u in users],
    }


@router.put("/{tenant_id}", summary="更新租户信息")
async def update_tenant(
    tenant_id: int,
    body: TenantUpdate,
    db: AsyncSession = Depends(get_db),
    _sa: User = Depends(get_superadmin_user),
):
    tenant = await _get_tenant_or_404(db, tenant_id)

    if body.name is not None:
        tenant.name = body.name
    if body.domain is not None:
        # Check domain uniqueness
        dup = (await db.execute(
            select(Tenant).where(Tenant.domain == body.domain, Tenant.id != tenant_id)
        )).scalar_one_or_none()
        if dup:
            raise HTTPException(status_code=409, detail=f"域名 {body.domain!r} 已被占用")
        await cache_delete(f"domain:{tenant.domain}")
        tenant.domain = body.domain
    if body.status is not None:
        if body.status not in ("active", "suspended"):
            raise HTTPException(status_code=422, detail="status 只能是 active 或 suspended")
        tenant.status = body.status
    if body.plan_type is not None:
        tenant.plan_type = body.plan_type
    if "storage_profile_id" in body.model_fields_set:
        tenant.storage_profile_id = body.storage_profile_id
    if body.storage_quota_mb is not None:
        tenant.storage_quota_mb = body.storage_quota_mb
    if "ai_profile_id" in body.model_fields_set:
        tenant.ai_profile_id = body.ai_profile_id
    if body.ai_allow_custom is not None:
        tenant.ai_allow_custom = body.ai_allow_custom
    if "ai_quota_monthly" in body.model_fields_set:
        tenant.ai_quota_monthly = body.ai_quota_monthly
    if "ai_quota_5h" in body.model_fields_set:
        tenant.ai_quota_5h = body.ai_quota_5h
    if "ai_quota_weekly" in body.model_fields_set:
        tenant.ai_quota_weekly = body.ai_quota_weekly
    # 清除域名缓存（状态/套餐变更时立即生效）
    await cache_delete(f"domain:{tenant.domain}")
    await db.commit()
    await db.refresh(tenant)
    return {
        "id": tenant.id,
        "name": tenant.name,
        "domain": tenant.domain,
        "plan_type": tenant.plan_type,
        "status": tenant.status,
    }


# ── User CRUD under Tenant ───────────────────────────────────────────────────

@router.post("/{tenant_id}/users", summary="为租户添加用户", status_code=201)
async def create_tenant_user(
    tenant_id: int,
    body: UserCreate,
    db: AsyncSession = Depends(get_db),
    _sa: User = Depends(get_superadmin_user),
):
    await _get_tenant_or_404(db, tenant_id)

    if body.role not in VALID_ROLES:
        raise HTTPException(422, f"role 必须是 {VALID_ROLES} 之一")
    if len(body.password) < 8:
        raise HTTPException(422, "密码至少8位")

    # Check email uniqueness within tenant
    dup = (await db.execute(
        select(User).where(User.tenant_id == tenant_id, User.email == body.email)
    )).scalar_one_or_none()
    if dup:
        raise HTTPException(409, f"邮箱 {body.email!r} 在该租户下已存在")

    profile = json.dumps({"name": body.name or body.email.split("@")[0]})
    user = User(
        tenant_id=tenant_id,
        email=body.email,
        password_hash=_hash_password(body.password),
        role=body.role,
        is_active=1,
        profile=profile,
    )
    db.add(user)
    await db.commit()
    await db.refresh(user)

    logger.info("Created user id=%s email=%r for tenant=%s", user.id, user.email, tenant_id)
    return _user_dict(user)


@router.put("/{tenant_id}/users/{user_id}", summary="更新租户用户")
async def update_tenant_user(
    tenant_id: int,
    user_id: int,
    body: UserUpdate,
    db: AsyncSession = Depends(get_db),
    _sa: User = Depends(get_superadmin_user),
):
    user = await _get_user_or_404(db, tenant_id, user_id)

    if body.email is not None and body.email != user.email:
        dup = (await db.execute(
            select(User).where(User.tenant_id == tenant_id, User.email == body.email, User.id != user_id)
        )).scalar_one_or_none()
        if dup:
            raise HTTPException(409, f"邮箱 {body.email!r} 在该租户下已存在")
        user.email = body.email

    if body.role is not None:
        if body.role not in VALID_ROLES:
            raise HTTPException(422, f"role 必须是 {VALID_ROLES} 之一")
        user.role = body.role

    if body.is_active is not None:
        user.is_active = 1 if body.is_active else 0

    if body.password is not None:
        if len(body.password) < 8:
            raise HTTPException(422, "密码至少8位")
        user.password_hash = _hash_password(body.password)

    if body.name is not None:
        profile = {}
        if isinstance(user.profile, dict):
            profile = user.profile
        elif isinstance(user.profile, str):
            try:
                profile = json.loads(user.profile)
            except Exception:
                pass
        profile["name"] = body.name
        user.profile = json.dumps(profile)

    await db.commit()
    await db.refresh(user)
    return _user_dict(user)


@router.delete("/{tenant_id}/users/{user_id}", summary="删除租户用户", status_code=204)
async def delete_tenant_user(
    tenant_id: int,
    user_id: int,
    db: AsyncSession = Depends(get_db),
    _sa: User = Depends(get_superadmin_user),
):
    user = await _get_user_or_404(db, tenant_id, user_id)

    # Prevent deleting the last owner
    if user.role == "owner":
        owner_count = (await db.execute(
            select(func.count(User.id)).where(
                User.tenant_id == tenant_id, User.role == "owner"
            )
        )).scalar()
        if owner_count <= 1:
            raise HTTPException(400, "不能删除租户唯一的 owner 账号")

    await db.delete(user)
    await db.commit()


# ── Backup / Restore ────────────────────────────────────────────────────────

@router.get("/{tenant_id}/backup/stats", summary="备份数据统计")
async def backup_stats(
    tenant_id: int,
    db: AsyncSession = Depends(get_db),
    _sa: User = Depends(get_superadmin_user),
):
    await _get_tenant_or_404(db, tenant_id)
    return await get_backup_stats(db, tenant_id)


@router.get("/{tenant_id}/backup", summary="下载租户备份")
async def download_backup(
    tenant_id: int,
    db: AsyncSession = Depends(get_db),
    _sa: User = Depends(get_superadmin_user),
):
    tenant = await _get_tenant_or_404(db, tenant_id)
    data = await export_tenant_data(db, tenant_id)
    data["tenant_name"] = tenant.name
    data["tenant_domain"] = tenant.domain

    from fastapi.responses import StreamingResponse
    import io

    content = json.dumps(data, ensure_ascii=False, indent=2, default=str)
    buf = io.BytesIO(content.encode("utf-8"))
    filename = f"tenant_{tenant_id}_{tenant.domain}_{data['exported_at'][:10]}.json"

    return StreamingResponse(
        buf,
        media_type="application/json",
        headers={"Content-Disposition": f'attachment; filename="{filename}"'},
    )


@router.get("/{tenant_id}/storage/orphans", summary="扫描指定租户的孤儿图片")
async def scan_tenant_orphans(
    tenant_id: int,
    db: AsyncSession = Depends(get_db),
    _sa: User = Depends(get_superadmin_user),
):
    from app.api.routers.admin.storage import (
        _get_tenant_storage, _collect_tenant_refs, _list_storage_files, _select_orphans,
    )
    await _get_tenant_or_404(db, tenant_id)
    storage, prefix = await _get_tenant_storage(db, tenant_id)
    ref_keys, ref_basenames = await _collect_tenant_refs(db, tenant_id)
    all_files = await _list_storage_files(storage, prefix=prefix)
    total_size = sum(f["size"] for f in all_files)
    orphans = _select_orphans(all_files, ref_keys, ref_basenames)
    orphan_size = sum(f["size"] for f in orphans)
    return {
        "total_files": len(all_files),
        "total_size": total_size,
        "used_files": len(all_files) - len(orphans),
        "orphan_files": len(orphans),
        "orphan_size": orphan_size,
        "orphans": sorted(orphans, key=lambda x: x["size"], reverse=True),
    }


@router.delete("/{tenant_id}/storage/orphans", summary="删除指定租户的孤儿图片")
async def delete_tenant_orphans(
    tenant_id: int,
    db: AsyncSession = Depends(get_db),
    _sa: User = Depends(get_superadmin_user),
):
    from app.api.routers.admin.storage import (
        _get_tenant_storage, _collect_tenant_refs, _list_storage_files, _select_orphans,
    )
    await _get_tenant_or_404(db, tenant_id)
    storage, prefix = await _get_tenant_storage(db, tenant_id)
    ref_keys, ref_basenames = await _collect_tenant_refs(db, tenant_id)
    all_files = await _list_storage_files(storage, prefix=prefix)
    orphans = _select_orphans(all_files, ref_keys, ref_basenames)
    deleted, failed = [], []
    for f in orphans:
        try:
            await storage.delete(f["name"])
            deleted.append(f)
        except Exception as e:
            failed.append({"name": f["name"], "error": str(e)})
    freed = sum(f["size"] for f in deleted)
    return {"tenant_id": tenant_id, "deleted": len(deleted), "freed_bytes": freed, "failed": failed}


@router.post("/{tenant_id}/storage/recalculate-usage", summary="Recalculate tenant storage usage")
async def recalculate_tenant_storage_usage(
    tenant_id: int,
    db: AsyncSession = Depends(get_db),
    _sa: User = Depends(get_superadmin_user),
):
    from app.api.routers.admin.storage import _recalculate_tenant_storage_usage
    tenant = await _get_tenant_or_404(db, tenant_id)
    used_mb = await _recalculate_tenant_storage_usage(db, tenant_id)
    return {
        "tenant_id": tenant_id,
        "storage_used_mb": used_mb,
        "storage_quota_mb": tenant.storage_quota_mb,
    }


@router.post("/{tenant_id}/storage/migrate", summary="Start tenant storage migration task")
async def migrate_tenant_storage(
    tenant_id: int,
    background_tasks: BackgroundTasks,
    db: AsyncSession = Depends(get_db),
    _sa: User = Depends(get_superadmin_user),
):
    from app.api.routers.admin.storage import _start_storage_migration_task
    await _get_tenant_or_404(db, tenant_id)
    return await _start_storage_migration_task(db, tenant_id, background_tasks)


@router.get("/{tenant_id}/storage/migrate/{task_id}", summary="Get tenant storage migration task")
async def get_tenant_storage_migration_task(
    tenant_id: int,
    task_id: int,
    db: AsyncSession = Depends(get_db),
    _sa: User = Depends(get_superadmin_user),
):
    from app.api.routers.admin.storage import _get_storage_migration_task
    await _get_tenant_or_404(db, tenant_id)
    return await _get_storage_migration_task(db, tenant_id, task_id)


@router.post("/{tenant_id}/restore", summary="从备份恢复租户数据")
async def restore_backup(
    tenant_id: int,
    file: UploadFile = File(...),
    clear_existing: bool = Query(True, description="恢复前清除现有数据"),
    db: AsyncSession = Depends(get_db),
    _sa: User = Depends(get_superadmin_user),
):
    await _get_tenant_or_404(db, tenant_id)

    if not file.filename or not file.filename.endswith(".json"):
        raise HTTPException(400, "请上传 .json 备份文件")

    try:
        raw = await file.read()
        backup_data = json.loads(raw.decode("utf-8"))
    except (json.JSONDecodeError, UnicodeDecodeError) as e:
        raise HTTPException(400, f"备份文件格式错误：{str(e)[:100]}")

    if "tables" not in backup_data:
        raise HTTPException(400, "无效的备份文件：缺少 tables 字段")

    result = await restore_tenant_data(db, tenant_id, backup_data, clear_existing=clear_existing)
    logger.info(
        "Restored tenant=%s from backup, total_rows=%d clear=%s",
        tenant_id, result["total_rows"], clear_existing,
    )
    return result
