"""Super Admin — Storage Profile Management
Routes:
  GET    /superadmin/storage-profiles            list all profiles
  POST   /superadmin/storage-profiles            create profile
  PUT    /superadmin/storage-profiles/{id}        update profile
  DELETE /superadmin/storage-profiles/{id}        delete profile (blocked if tenants reference it)
"""
import json
import uuid

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, field_validator
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession

from app.api.deps import get_db, get_superadmin_user
from app.core.models.storage_profile import StorageProfile
from app.core.models.tenant import Tenant
from app.core.storage import get_storage, check_storage_deps

router = APIRouter(prefix="/superadmin/storage-profiles", tags=["超管-存储配置"])

_STRIP_FIELDS = (
    "name", "purpose", "provider", "bucket", "region",
    "endpoint_url", "access_key", "secret_key",
)


def _normalize_public_url(v):
    """去空格；非空且缺协议前缀时自动补 https://（公开访问域名必须是完整 URL，否则前端会把它当相对路径解析）"""
    if not isinstance(v, str):
        return v
    v = v.strip()
    if v and not v.startswith("http://") and not v.startswith("https://"):
        v = f"https://{v}"
    return v


class StorageProfileCreate(BaseModel):
    name: str
    purpose: str = "tenant_data"
    provider: str
    bucket: str | None = None
    region: str | None = None
    endpoint_url: str | None = None
    access_key: str | None = None
    secret_key: str | None = None
    public_url_base: str | None = None
    use_acl: bool = False
    is_active: bool = True
    migration_skip_hosts: list[str] = []

    @field_validator(*_STRIP_FIELDS, mode="before")
    @classmethod
    def _strip(cls, v):
        return v.strip() if isinstance(v, str) else v

    @field_validator("public_url_base", mode="before")
    @classmethod
    def _normalize_public_url_base(cls, v):
        return _normalize_public_url(v)


class StorageProfileTest(BaseModel):
    """测试连接 — 字段与 Create 一致，额外支持 profile_id 用于编辑场景下密钥留空时回退到已保存的值"""
    profile_id: int | None = None
    provider: str
    bucket: str | None = None
    region: str | None = None
    endpoint_url: str | None = None
    access_key: str | None = None
    secret_key: str | None = None
    use_acl: bool = False

    @field_validator("provider", "bucket", "region", "endpoint_url", "access_key", "secret_key", mode="before")
    @classmethod
    def _strip(cls, v):
        return v.strip() if isinstance(v, str) else v


class StorageProfileUpdate(BaseModel):
    name: str | None = None
    purpose: str | None = None
    provider: str | None = None
    bucket: str | None = None
    region: str | None = None
    endpoint_url: str | None = None
    access_key: str | None = None
    secret_key: str | None = None
    public_url_base: str | None = None
    use_acl: bool | None = None
    is_active: bool | None = None
    migration_skip_hosts: list[str] | None = None

    @field_validator(*_STRIP_FIELDS, mode="before")
    @classmethod
    def _strip(cls, v):
        return v.strip() if isinstance(v, str) else v

    @field_validator("public_url_base", mode="before")
    @classmethod
    def _normalize_public_url_base(cls, v):
        return _normalize_public_url(v)


@router.get("/", summary="列出所有存储配置")
async def list_profiles(db: AsyncSession = Depends(get_db), _=Depends(get_superadmin_user)):
    r = await db.execute(select(StorageProfile).order_by(StorageProfile.id))
    profiles = r.scalars().all()
    return [
        {
            "id": p.id, "name": p.name, "purpose": p.purpose, "provider": p.provider,
            "bucket": p.bucket, "region": p.region, "endpoint_url": p.endpoint_url,
            "public_url_base": p.public_url_base, "use_acl": p.use_acl, "is_active": p.is_active,
            "migration_skip_hosts": json.loads(p.migration_skip_hosts) if p.migration_skip_hosts else [],
        }
        for p in profiles
    ]


@router.post("/", summary="创建存储配置", status_code=201)
async def create_profile(
    body: StorageProfileCreate, db: AsyncSession = Depends(get_db), _=Depends(get_superadmin_user)
):
    data = body.model_dump()
    data["migration_skip_hosts"] = json.dumps(data["migration_skip_hosts"]) if data.get("migration_skip_hosts") else None
    profile = StorageProfile(**data)
    db.add(profile)
    await db.commit()
    await db.refresh(profile)
    return {"id": profile.id}


@router.post("/test", summary="测试存储配置连接")
async def test_profile(
    body: StorageProfileTest, db: AsyncSession = Depends(get_db), _=Depends(get_superadmin_user)
):
    if body.provider == "local":
        return {"ok": True, "message": "本地存储无需测试连接"}

    access_key, secret_key = body.access_key, body.secret_key
    if body.profile_id and (not access_key or not secret_key):
        r = await db.execute(select(StorageProfile).where(StorageProfile.id == body.profile_id))
        existing = r.scalar_one_or_none()
        if existing:
            access_key = access_key or existing.access_key
            secret_key = secret_key or existing.secret_key

    dep = check_storage_deps(body.provider)
    if not dep["ok"]:
        raise HTTPException(
            status_code=501,
            detail=f"存储依赖未安装：{dep['missing']}。请在服务器执行：{dep['install_cmd']}，然后重启后端。",
        )

    cfg = {
        "cdn_provider": body.provider,
        "cdn_s3_bucket": body.bucket,
        "cdn_s3_region": body.region or "us-east-1",
        "cdn_s3_access_key": access_key,
        "cdn_s3_secret_key": secret_key,
        "cdn_s3_endpoint_url": body.endpoint_url,
        "cdn_s3_use_acl": body.use_acl,
        "cdn_r2_endpoint": body.endpoint_url,
        "cdn_oss_bucket": body.bucket,
        "cdn_oss_endpoint": body.endpoint_url,
        "cdn_oss_access_key": access_key,
        "cdn_oss_secret_key": secret_key,
    }
    storage = get_storage(cfg)
    test_key = f"__connection_test__/{uuid.uuid4().hex}.txt"
    try:
        await storage.save(test_key, b"ls connection test", content_type="text/plain")
    except Exception as e:
        raise HTTPException(status_code=400, detail=f"连接测试失败：{e}")
    try:
        await storage.delete(test_key)
    except Exception:
        pass  # 清理失败不影响测试结果，主要验证写权限是否正常
    return {"ok": True, "message": "连接成功，已写入并清理测试文件"}


@router.put("/{profile_id}", summary="更新存储配置")
async def update_profile(
    profile_id: int, body: StorageProfileUpdate,
    db: AsyncSession = Depends(get_db), _=Depends(get_superadmin_user),
):
    r = await db.execute(select(StorageProfile).where(StorageProfile.id == profile_id))
    profile = r.scalar_one_or_none()
    if not profile:
        raise HTTPException(status_code=404, detail="存储配置不存在")
    for k, v in body.model_dump(exclude_unset=True).items():
        if k in ("secret_key", "access_key") and not v:
            continue
        if k == "migration_skip_hosts":
            setattr(profile, k, json.dumps(v) if v is not None else None)
            continue
        setattr(profile, k, v)
    await db.commit()
    return {"id": profile.id, "ok": True}


@router.delete("/{profile_id}", summary="删除存储配置")
async def delete_profile(
    profile_id: int, db: AsyncSession = Depends(get_db), _=Depends(get_superadmin_user)
):
    count_r = await db.execute(
        select(func.count()).select_from(Tenant).where(Tenant.storage_profile_id == profile_id)
    )
    if count_r.scalar_one() > 0:
        raise HTTPException(status_code=400, detail="仍有租户在使用该存储配置，请先迁移租户")
    r = await db.execute(select(StorageProfile).where(StorageProfile.id == profile_id))
    profile = r.scalar_one_or_none()
    if not profile:
        raise HTTPException(status_code=404, detail="存储配置不存在")
    await db.delete(profile)
    await db.commit()
    return {"ok": True}
